Skip to content

Commit

Permalink
vi.blockTimes is now a [5]time.Duration instead of [5]uint64
Browse files Browse the repository at this point in the history
  • Loading branch information
altergui committed Dec 12, 2023
1 parent 64ee8d6 commit 9d2b332
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 74 deletions.
7 changes: 6 additions & 1 deletion api/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,14 @@ func (a *API) chainInfoHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext)
return err
}

var blockTimesInMs [5]uint64
for i, v := range a.vocinfo.BlockTimes() {
blockTimesInMs[i] = uint64(v.Milliseconds())
}

data, err := json.Marshal(&ChainInfo{
ID: a.vocapp.ChainID(),
BlockTime: a.vocinfo.BlockTimes(),
BlockTime: blockTimesInMs,
ElectionCount: a.indexer.CountTotalProcesses(),
OrganizationCount: a.indexer.CountTotalEntities(),
Height: a.vocapp.Height(),
Expand Down
119 changes: 46 additions & 73 deletions vochain/vochaininfo/vochaininfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package vochaininfo
import (
"context"
"fmt"
"math"
"sync"
"time"

Expand All @@ -18,7 +17,7 @@ import (
// Avg1/10/60/360 are the block time average for 1 minute, 10 minutes, 1 hour and 6 hours
type VochainInfo struct {
votesPerMinute uint64
blockTimes [5]uint64
blockTimes [5]time.Duration
blocksMinute float64
vnode *vochain.BaseApplication
close chan bool
Expand Down Expand Up @@ -62,7 +61,7 @@ func (vi *VochainInfo) updateCounters() {

voteCacheSize.Set(uint64(vi.vnode.State.CacheSize()))
mempoolSize.Set(uint64(vi.vnode.MempoolSize()))
blockPeriodMinute.Set(vi.BlockTimes()[0])
blockPeriodMinute.Set(uint64(vi.BlockTimes()[0].Milliseconds()))
blocksSyncLastMinute.Set(uint64(vi.BlocksLastMinute()))
}

Expand All @@ -73,7 +72,7 @@ func (vi *VochainInfo) Height() uint64 {

// averageBlockTime calculates the average block time for the last intervalBlocks blocks.
// The timestamp information is taken from the block headers.
func (vi *VochainInfo) averageBlockTime(intervalBlocks int64) float64 {
func (vi *VochainInfo) averageBlockTime(intervalBlocks int64) time.Duration {
if intervalBlocks == 0 {
return 0
}
Expand All @@ -96,91 +95,74 @@ func (vi *VochainInfo) averageBlockTime(intervalBlocks int64) float64 {
}

// Calculate the time frame in seconds
timeFrameSeconds := currentTime.Sub(*startTime).Seconds()
timeFrame := currentTime.Sub(*startTime)

// Adjust the average block time based on the actual time frame
return timeFrameSeconds / float64(intervalBlocks)
return timeFrame / time.Duration(intervalBlocks)
}

func (vi *VochainInfo) updateBlockTimes() {
vi.blockTimes = [5]uint64{
uint64(1000 * vi.averageBlockTime(60/int64(vi.vnode.BlockTimeTarget().Seconds())*1)),
uint64(1000 * vi.averageBlockTime(60/int64(vi.vnode.BlockTimeTarget().Seconds())*10)),
uint64(1000 * vi.averageBlockTime(60/int64(vi.vnode.BlockTimeTarget().Seconds())*60)),
uint64(1000 * vi.averageBlockTime(60/int64(vi.vnode.BlockTimeTarget().Seconds())*360)),
uint64(1000 * vi.averageBlockTime(60/int64(vi.vnode.BlockTimeTarget().Seconds())*1440)),
vi.blockTimes = [5]time.Duration{
vi.averageBlockTime(int64(1 * time.Minute / vi.vnode.BlockTimeTarget())),
vi.averageBlockTime(int64(10 * time.Minute / vi.vnode.BlockTimeTarget())),
vi.averageBlockTime(int64(1 * time.Hour / vi.vnode.BlockTimeTarget())),
vi.averageBlockTime(int64(6 * time.Hour / vi.vnode.BlockTimeTarget())),
vi.averageBlockTime(int64(24 * time.Hour / vi.vnode.BlockTimeTarget())),
}
if vi.blockTimes[0] == 0 {
vi.blockTimes[0] = uint64(vi.vnode.BlockTimeTarget().Milliseconds())
vi.blockTimes[0] = vi.vnode.BlockTimeTarget()
}
}

// BlockTimes returns the average block time in milliseconds for 1, 10, 60, 360 and 1440 minutes.
// BlockTimes returns the average block time for 1, 10, 60, 360 and 1440 minutes.
// Value 0 means there is not yet an average.
func (vi *VochainInfo) BlockTimes() [5]uint64 {
func (vi *VochainInfo) BlockTimes() [5]time.Duration {
vi.lock.RLock()
defer vi.lock.RUnlock()
return vi.blockTimes
}

// EstimateBlockHeight provides an estimation time for a future blockchain height number.
func (vi *VochainInfo) EstimateBlockHeight(target time.Time) (uint64, error) {
currentTime := time.Now()
// diff time in seconds
diffTime := target.Unix() - currentTime.Unix()

// block time in ms
func (vi *VochainInfo) getBlockTimeBestEstimate(i int) time.Duration {
times := vi.BlockTimes()
getMaxTimeFrom := func(i int) uint64 {
for ; i >= 0; i-- {
if times[i] != 0 {
return uint64(times[i])
}
for ; i >= 0; i-- {
if times[i] != 0 {
return times[i]
}
return 10000 // fallback
}
inPast := diffTime < 0
absDiff := diffTime
// check diff is not too big
if absDiff > math.MaxUint64/1000 {
return 0, fmt.Errorf("target time %v is too far in the future", target)
}
if inPast {
absDiff = -absDiff
}
t := uint64(0)
return vi.vnode.BlockTimeTarget() // fallback
}

// EstimateBlockHeight provides an estimated blockchain height for a future or past date.
func (vi *VochainInfo) EstimateBlockHeight(target time.Time) (uint64, error) {
diffTime := target.Sub(time.Now())

Check failure on line 137 in vochain/vochaininfo/vochaininfo.go

View workflow job for this annotation

GitHub Actions / job_go_checks

should use time.Until instead of t.Sub(time.Now()) (S1024)

t := time.Duration(0)
switch {
// if less than around 15 minutes missing
case absDiff < 900:
t = getMaxTimeFrom(1)
case diffTime.Abs().Minutes() < 15:
t = vi.getBlockTimeBestEstimate(1)
// if less than around 6 hours missing
case absDiff < 21600:
t = getMaxTimeFrom(3)
case diffTime.Abs().Hours() < 6:
t = vi.getBlockTimeBestEstimate(3)
// if more than around 6 hours missing
default:
t = getMaxTimeFrom(4)
t = vi.getBlockTimeBestEstimate(4)
}
// Multiply by 1000 because t is represented in seconds, not ms.
// Dividing t first can floor the integer, leading to divide-by-zero
currentHeight := uint64(vi.Height())
blockDiff := uint64(absDiff*1000) / t
if inPast {
if blockDiff > currentHeight {
return 0, fmt.Errorf("target time %v is before origin", target)
}
return currentHeight - blockDiff, nil
blockDiff := uint64(diffTime / t)
// if target is in the past, diffTime is negative
if diffTime < 0 && currentHeight+blockDiff <= 0 {
return 0, fmt.Errorf("target time %v is before genesis", target)
}
return currentHeight + blockDiff, nil
}

// HeightTime estimates the UTC time for a future height or returns the
// block timestamp if height is in the past.
func (vi *VochainInfo) HeightTime(height uint64) time.Time {
times := vi.BlockTimes()
currentHeight := vi.Height()
diffHeight := int64(height - currentHeight)
diffHeight := int64(height - vi.Height())

if diffHeight < 0 {
if diffHeight < 0 { // height is in the past
blk := vi.vnode.GetBlockByHeight(int64(height))
if blk == nil {
log.Errorf("cannot get block height %d", height)
Expand All @@ -189,28 +171,19 @@ func (vi *VochainInfo) HeightTime(height uint64) time.Time {
return blk.Header.Time
}

getMaxTimeFrom := func(i int) uint64 {
for ; i >= 0; i-- {
if times[i] != 0 {
return times[i]
}
}
return 10000 // fallback
}

t := uint64(0)
t := time.Duration(0)
switch {
// if less than around 15 minutes missing
case diffHeight < 100:
t = getMaxTimeFrom(1)
case diffHeight < int64(15*time.Minute/vi.vnode.BlockTimeTarget()):
t = vi.getBlockTimeBestEstimate(1)
// if less than around 6 hours missing
case diffHeight < 1000:
t = getMaxTimeFrom(3)
// if less than around 6 hours missing
case diffHeight >= 1000:
t = getMaxTimeFrom(4)
case diffHeight < int64(6*time.Hour/vi.vnode.BlockTimeTarget()):
t = vi.getBlockTimeBestEstimate(3)
// if more than around 6 hours missing
case diffHeight >= int64(6*time.Hour/vi.vnode.BlockTimeTarget()):
t = vi.getBlockTimeBestEstimate(4)
}
return time.Now().Add(time.Duration(diffHeight*int64(t)) * time.Millisecond)
return time.Now().Add(time.Duration(diffHeight) * t)
}

// TreeSizes returns the current size of the ProcessTree, VoteTree and the votes per minute
Expand Down

0 comments on commit 9d2b332

Please sign in to comment.