Skip to content

Commit

Permalink
Don't use GetTipset(cert.Head()) to compute startup delay
Browse files Browse the repository at this point in the history
Resolves #720

Signed-off-by: Jakub Sztandera <oss@kubuxu.com>
  • Loading branch information
Kubuxu committed Oct 28, 2024
1 parent 322dc35 commit a01c322
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 8 deletions.
20 changes: 12 additions & 8 deletions host.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,18 @@ func (h *gpbftRunner) startInstanceAt(instance uint64, at time.Time) error {
func (h *gpbftRunner) computeNextInstanceStart(cert *certs.FinalityCertificate) (_nextStart time.Time) {
ecDelay := time.Duration(h.manifest.EC.DelayMultiplier * float64(h.manifest.EC.Period))

ts, err := h.ec.GetTipset(h.runningCtx, cert.ECChain.Head().Key)
head, err := h.ec.GetHead(h.runningCtx)
if err != nil {
// this should not happen
log.Errorf("could not get timestamp of just finalized tipset: %+v", err)
log.Errorf("ec.GetHead returned error: %+v", err)
return h.clock.Now().Add(ecDelay)
}

base := ts.Timestamp()
// the head of the cert becomes the new base
baseTipSet := cert.ECChain.Head()
// we are not trying to fetch the new base tipset from EC as it might not be available
// instead we compute the relative time from the EC.Head
baseTimestamp := computeTipsetTimestampAtEpoch(head, baseTipSet.Epoch, h.manifest.EC.Period)

// Try to align instances while catching up, if configured.
if h.manifest.CatchUpAlignment > 0 {
Expand All @@ -364,11 +368,11 @@ func (h *gpbftRunner) computeNextInstanceStart(cert *certs.FinalityCertificate)
// we're behind and try to align our start times. This helps keep nodes
// in-sync when bootstrapping and catching up.
if _nextStart.Before(now.Add(-h.manifest.CatchUpAlignment)) {
delay := now.Sub(base)
delay := now.Sub(baseTimestamp)
if offset := delay % h.manifest.CatchUpAlignment; offset > 0 {
delay += h.manifest.CatchUpAlignment - offset
}
_nextStart = base.Add(delay)
_nextStart = baseTimestamp.Add(delay)
}
}()
}
Expand All @@ -377,11 +381,11 @@ func (h *gpbftRunner) computeNextInstanceStart(cert *certs.FinalityCertificate)

if cert.ECChain.HasSuffix() {
// we decided on something new, the tipset that got finalized can at minimum be 30-60s old.
return base.Add(ecDelay).Add(lookbackDelay)
return baseTimestamp.Add(ecDelay).Add(lookbackDelay)
}
if cert.GPBFTInstance == h.manifest.InitialInstance {
// if we are at initial instance, there is no history to look at
return base.Add(ecDelay).Add(lookbackDelay)
return baseTimestamp.Add(ecDelay).Add(lookbackDelay)
}
backoffTable := h.manifest.EC.BaseDecisionBackoffTable

Expand Down Expand Up @@ -409,7 +413,7 @@ func (h *gpbftRunner) computeNextInstanceStart(cert *certs.FinalityCertificate)
backoff := time.Duration(float64(ecDelay) * backoffMultipler)
log.Infof("backing off for: %v", backoff)

return base.Add(backoff).Add(lookbackDelay)
return baseTimestamp.Add(backoff).Add(lookbackDelay)
}

// Sends a message to all other participants.
Expand Down
1 change: 1 addition & 0 deletions host_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package f3
16 changes: 16 additions & 0 deletions tipsettimestamp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package f3

import (
"time"

"github.com/filecoin-project/go-f3/ec"
)

func computeTipsetTimestampAtEpoch(validTipset ec.TipSet, epoch int64, ECPeriod time.Duration) time.Time {
// validTipset is base and epoch is the head
// timestamp(head) = genesis + epoch(head) * period
// timestamp(base) = genesis + epoch(base) * period + timestamp(head) - timestamp(head)
// timestamp(base) = timestamp(head) + genesis + epoch(base) * period - genesis - epoch(head) * period
// timestamp(base) = timestamp(head) + (epoch(base) - epoch(head)) * period
return validTipset.Timestamp().Add(time.Duration(epoch-validTipset.Epoch()) * ECPeriod)
}
90 changes: 90 additions & 0 deletions tipsettimestamp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package f3

import (
"testing"
"time"

"github.com/filecoin-project/go-f3/gpbft"
"github.com/stretchr/testify/assert"
)

type tipset struct {
genesis time.Time
period time.Duration
epoch int64
}

func (ts *tipset) String() string {
panic("not implemented")
}

func (ts *tipset) Key() gpbft.TipSetKey {
panic("not implemented")
}

func (ts *tipset) Beacon() []byte {
panic("not implemented")
}

func (ts *tipset) Epoch() int64 {
return ts.epoch
}

func (ts *tipset) Timestamp() time.Time {
return ts.genesis.Add(time.Duration(ts.epoch) * ts.period)
}

func tipsetGenerator(genesis time.Time, period time.Duration) func(epoch int64) *tipset {
return func(epoch int64) *tipset {
return &tipset{
genesis: genesis,
period: period,
epoch: epoch,
}
}
}

func TestComputeTipsetTimestampAtEpoch(t *testing.T) {
genesis := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
period := 30 * time.Second

generateTipset := tipsetGenerator(genesis, period)
tipset := generateTipset(10)

t.Run("Basic Functionality", func(t *testing.T) {
targetEpoch := int64(15)
expected := generateTipset(targetEpoch).Timestamp()
actual := computeTipsetTimestampAtEpoch(tipset, targetEpoch, period)
assert.Equal(t, expected, actual)
})

t.Run("Zero Epoch", func(t *testing.T) {
targetEpoch := int64(0)
expected := generateTipset(targetEpoch).Timestamp()
actual := computeTipsetTimestampAtEpoch(tipset, targetEpoch, period)
assert.Equal(t, expected, actual)
})

t.Run("Large Epoch", func(t *testing.T) {
largeEpoch := int64(1e6)
expected := generateTipset(largeEpoch).Timestamp()
actual := computeTipsetTimestampAtEpoch(tipset, largeEpoch, period)
assert.Equal(t, expected, actual)
})

t.Run("Boundary Condition", func(t *testing.T) {
boundaryEpoch := int64(1e3)
expected := generateTipset(boundaryEpoch).Timestamp()
actual := computeTipsetTimestampAtEpoch(tipset, boundaryEpoch, period)
assert.Equal(t, expected, actual)
})

t.Run("Consistency", func(t *testing.T) {
targetEpoch := int64(20)
expected := generateTipset(targetEpoch).Timestamp()
actual1 := computeTipsetTimestampAtEpoch(tipset, targetEpoch, period)
actual2 := computeTipsetTimestampAtEpoch(tipset, targetEpoch, period)
assert.Equal(t, expected, actual1)
assert.Equal(t, expected, actual2)
})
}

0 comments on commit a01c322

Please sign in to comment.