Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cristaloleg committed Nov 4, 2024
1 parent 9b8a974 commit 6ff7cb7
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 2 deletions.
6 changes: 6 additions & 0 deletions headertest/dummy_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type DummyHeader struct {
// SoftFailure allows for testing scenarios where a header would fail
// verification with SoftFailure set to true
SoftFailure bool

// VerifyFn can be used to change header.Verify behaviour per header.
VerifyFn func(hdr *DummyHeader) error `json:"-"`
}

func RandDummyHeader(t *testing.T) *DummyHeader {
Expand Down Expand Up @@ -100,6 +103,9 @@ func (d *DummyHeader) IsExpired(period time.Duration) bool {
}

func (d *DummyHeader) Verify(hdr *DummyHeader) error {
if d.VerifyFn != nil {
return d.VerifyFn(hdr)
}
if hdr.VerifyFailure {
return &header.VerifyError{Reason: ErrDummyVerify, SoftFailure: hdr.SoftFailure}
}
Expand Down
2 changes: 1 addition & 1 deletion sync/sync_head.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ type NewValidatorSetCantBeTrustedError struct {
}

func (e *NewValidatorSetCantBeTrustedError) Error() string {
return fmt.Sprintf("sync: new validator set cant be trusted: head %d, attempted %s", e.NetHeadHeight, e.NetHeadHash)
return fmt.Sprintf("sync: new validator set cant be trusted: head %d, attempted %x", e.NetHeadHeight, e.NetHeadHash)
}

// isExpired checks if header is expired against trusting period.
Expand Down
142 changes: 141 additions & 1 deletion sync/sync_head_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,146 @@ func TestSyncer_HeadWithNotEnoughValidators(t *testing.T) {
require.True(t, wrappedGetter.withTrustedHead)
}

func TestSyncer_verifySkipping(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
t.Cleanup(cancel)

const total = 1000
const badHeaderHeight = total + 1

suite := headertest.NewTestSuite(t)
head := suite.Head()

localStore := newTestStore(t, ctx, head)
remoteStore := newTestStore(t, ctx, head)

// create a wrappedGetter to track exchange interactions
wrappedGetter := newWrappedGetter(local.NewExchange(remoteStore))

syncer, err := NewSyncer(
wrappedGetter,
localStore,
headertest.NewDummySubscriber(),
WithBlockTime(time.Nanosecond),
WithRecencyThreshold(time.Nanosecond), // forces a request for a new sync target
// ensures that syncer's store contains a subjective head that is within
// the unbonding period so that the syncer can use a header from the network
// as a sync target
WithTrustingPeriod(time.Hour),
)
require.NoError(t, err)

// start the syncer which triggers a Head request that will
// load the syncer's subjective head from the store, and request
// a new sync target from the network rather than from trusted peers
err = syncer.Start(ctx)
require.NoError(t, err)
t.Cleanup(func() {
err = syncer.Stop(ctx)
require.NoError(t, err)
})

t.Run("success (with bad candidates)", func(t *testing.T) {
const iters = 4

headers := suite.GenDummyHeaders(total)
err = remoteStore.Append(ctx, headers...)
require.NoError(t, err)

var verifyCounter atomic.Int32
for i := range total {
headers[i].VerifyFn = func(hdr *headertest.DummyHeader) error {
if i >= 501 {
return nil
}

verifyCounter.Add(1)
if verifyCounter.Load() <= iters {
return &header.VerifyError{
Reason: headertest.ErrDummyVerify,
SoftFailure: hdr.SoftFailure,
}
}
return nil
}
}

headers[total-1].VerifyFailure = true
headers[total-1].SoftFailure = true

subjHead, err := syncer.subjectiveHead(ctx)
require.NoError(t, err)

err = syncer.verifySkipping(ctx, subjHead, headers[total-1])
require.NoError(t, err)
})

t.Run("success", func(t *testing.T) {
const iters = 4

headers := suite.GenDummyHeaders(total)
err = remoteStore.Append(ctx, headers...)
require.NoError(t, err)

var verifyCounter atomic.Int32
for i := range total {
headers[i].VerifyFn = func(hdr *headertest.DummyHeader) error {
if hdr.Height() != badHeaderHeight {
return nil
}

verifyCounter.Add(1)
if verifyCounter.Load() >= iters {
return nil
}

return &header.VerifyError{
Reason: headertest.ErrDummyVerify,
SoftFailure: hdr.SoftFailure,
}
}
}

headers[total-1].VerifyFailure = true
headers[total-1].SoftFailure = true

subjHead, err := syncer.subjectiveHead(ctx)
require.NoError(t, err)

err = syncer.verifySkipping(ctx, subjHead, headers[total-1])
require.NoError(t, err)
})

t.Run("cannot verify", func(t *testing.T) {
headers := suite.GenDummyHeaders(total)
err = remoteStore.Append(ctx, headers...)
require.NoError(t, err)

for i := range total {
headers[i].VerifyFn = func(hdr *headertest.DummyHeader) error {
if hdr.Height() != badHeaderHeight {
return nil
}

return &header.VerifyError{
Reason: headertest.ErrDummyVerify,
SoftFailure: hdr.SoftFailure,
}
}
}

headers[total-1].VerifyFailure = true
headers[total-1].SoftFailure = true

subjHead, err := syncer.subjectiveHead(ctx)
require.NoError(t, err)

err = syncer.verifySkipping(ctx, subjHead, headers[total-1])
var verErr *NewValidatorSetCantBeTrustedError
assert.ErrorAs(t, err, &verErr)
})
}

type wrappedGetter struct {
ex header.Exchange[*headertest.DummyHeader]

Expand Down Expand Up @@ -170,7 +310,7 @@ func (t *wrappedGetter) Get(ctx context.Context, hash header.Hash) (*headertest.
}

func (t *wrappedGetter) GetByHeight(ctx context.Context, u uint64) (*headertest.DummyHeader, error) {
return nil, errors.New("implement me")
return t.ex.GetByHeight(ctx, u)
}

func (t *wrappedGetter) GetRangeByHeight(
Expand Down

0 comments on commit 6ff7cb7

Please sign in to comment.