Skip to content

Commit 61a66a5

Browse files
Wondertanrenaynay
andauthored
refactor(libhead): unify VerifyAdjacent and VerifyNonAdjacent into Verify (#1777)
Co-authored-by: rene <[email protected]>
1 parent 6d874ba commit 61a66a5

20 files changed

+179
-158
lines changed

Diff for: core/exchange.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func (ce *Exchange) GetVerifiedRange(
6464
}
6565

6666
for _, h := range headers {
67-
err := from.VerifyAdjacent(h)
67+
err := from.Verify(h)
6868
if err != nil {
6969
return nil, err
7070
}

Diff for: core/header_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import (
88
"github.com/stretchr/testify/require"
99
"github.com/tendermint/tendermint/libs/rand"
1010

11+
"github.com/celestiaorg/celestia-node/header/headertest"
12+
1113
"github.com/celestiaorg/celestia-node/header"
12-
"github.com/celestiaorg/celestia-node/headertest"
1314
)
1415

1516
func TestMakeExtendedHeaderForEmptyBlock(t *testing.T) {

Diff for: das/daser_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import (
1616
"github.com/stretchr/testify/require"
1717
"github.com/tendermint/tendermint/types"
1818

19+
"github.com/celestiaorg/celestia-node/header/headertest"
20+
1921
"github.com/celestiaorg/celestia-node/fraud"
2022
"github.com/celestiaorg/celestia-node/header"
21-
"github.com/celestiaorg/celestia-node/headertest"
2223
libhead "github.com/celestiaorg/celestia-node/libs/header"
2324
"github.com/celestiaorg/celestia-node/share"
2425
"github.com/celestiaorg/celestia-node/share/availability/full"

Diff for: fraud/testing.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import (
1414
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
1515
"github.com/stretchr/testify/require"
1616

17+
"github.com/celestiaorg/celestia-node/header/headertest"
18+
1719
"github.com/celestiaorg/celestia-node/header"
18-
"github.com/celestiaorg/celestia-node/headertest"
1920
)
2021

2122
type DummyService struct {
File renamed without changes.
File renamed without changes.

Diff for: header/headertest/verify_test.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package headertest
2+
3+
import (
4+
"strconv"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
10+
libhead "github.com/celestiaorg/celestia-node/libs/header"
11+
12+
tmrand "github.com/tendermint/tendermint/libs/rand"
13+
)
14+
15+
func TestVerify(t *testing.T) {
16+
h := NewTestSuite(t, 2).GenExtendedHeaders(3)
17+
trusted, untrustedAdj, untrustedNonAdj := h[0], h[1], h[2]
18+
tests := []struct {
19+
prepare func() libhead.Header
20+
err bool
21+
}{
22+
{
23+
prepare: func() libhead.Header { return untrustedAdj },
24+
err: false,
25+
},
26+
{
27+
prepare: func() libhead.Header {
28+
return untrustedNonAdj
29+
},
30+
err: false,
31+
},
32+
{
33+
prepare: func() libhead.Header {
34+
untrusted := *untrustedAdj
35+
untrusted.ValidatorsHash = tmrand.Bytes(32)
36+
return &untrusted
37+
},
38+
err: true,
39+
},
40+
{
41+
prepare: func() libhead.Header {
42+
untrusted := *untrustedNonAdj
43+
untrusted.Commit = NewTestSuite(t, 2).Commit(RandRawHeader(t))
44+
return &untrusted
45+
},
46+
err: true,
47+
},
48+
{
49+
prepare: func() libhead.Header {
50+
untrusted := *untrustedAdj
51+
untrusted.RawHeader.LastBlockID.Hash = tmrand.Bytes(32)
52+
return &untrusted
53+
},
54+
err: true,
55+
},
56+
{
57+
prepare: func() libhead.Header {
58+
untrustedAdj.RawHeader.Time = untrustedAdj.RawHeader.Time.Add(time.Minute)
59+
return untrustedAdj
60+
},
61+
err: true,
62+
},
63+
{
64+
prepare: func() libhead.Header {
65+
untrustedAdj.RawHeader.Time = untrustedAdj.RawHeader.Time.Truncate(time.Hour)
66+
return untrustedAdj
67+
},
68+
err: true,
69+
},
70+
{
71+
prepare: func() libhead.Header {
72+
untrustedAdj.RawHeader.ChainID = "toaster"
73+
return untrustedAdj
74+
},
75+
err: true,
76+
},
77+
{
78+
prepare: func() libhead.Header {
79+
untrustedAdj.RawHeader.Height++
80+
return untrustedAdj
81+
},
82+
err: true,
83+
},
84+
}
85+
86+
for i, test := range tests {
87+
t.Run(strconv.Itoa(i), func(t *testing.T) {
88+
err := trusted.Verify(test.prepare())
89+
if test.err {
90+
assert.Error(t, err)
91+
} else {
92+
assert.NoError(t, err)
93+
}
94+
})
95+
}
96+
}

Diff for: header/verify.go

+36-38
Original file line numberDiff line numberDiff line change
@@ -10,53 +10,47 @@ import (
1010
libhead "github.com/celestiaorg/celestia-node/libs/header"
1111
)
1212

13-
// TODO(@Wondertan): We should request TrustingPeriod from the network's state params or
14-
// listen for network params changes to always have a topical value.
15-
16-
// VerifyNonAdjacent validates non-adjacent untrusted header against trusted 'eh'.
17-
func (eh *ExtendedHeader) VerifyNonAdjacent(untrusted libhead.Header) error {
13+
// Verify validates given untrusted Header against trusted ExtendedHeader.
14+
func (eh *ExtendedHeader) Verify(untrusted libhead.Header) error {
1815
untrst, ok := untrusted.(*ExtendedHeader)
1916
if !ok {
20-
return &libhead.VerifyError{Reason: fmt.Errorf("invalid header type: expected %T, got %T", eh, untrusted)}
21-
}
22-
if err := eh.verify(untrst); err != nil {
23-
return &libhead.VerifyError{Reason: err}
17+
// if the header of the type was given, something very wrong happens
18+
panic(fmt.Sprintf("invalid header type: expected %T, got %T", eh, untrusted))
2419
}
2520

26-
// Ensure that untrusted commit has enough of trusted commit's power.
27-
err := eh.ValidatorSet.VerifyCommitLightTrusting(eh.ChainID(), untrst.Commit, light.DefaultTrustLevel)
28-
if err != nil {
21+
if err := eh.verify(untrst); err != nil {
2922
return &libhead.VerifyError{Reason: err}
3023
}
3124

32-
return nil
33-
}
25+
isAdjacent := eh.Height()+1 == untrst.Height()
26+
if isAdjacent {
27+
// Optimized verification for adjacent headers
28+
// Check the validator hashes are the same
29+
if !bytes.Equal(untrst.ValidatorsHash, eh.NextValidatorsHash) {
30+
return &libhead.VerifyError{
31+
Reason: fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)",
32+
eh.NextValidatorsHash,
33+
untrst.ValidatorsHash,
34+
),
35+
}
36+
}
3437

35-
// VerifyAdjacent validates adjacent untrusted header against trusted 'eh'.
36-
func (eh *ExtendedHeader) VerifyAdjacent(untrusted libhead.Header) error {
37-
untrst, ok := untrusted.(*ExtendedHeader)
38-
if !ok {
39-
return &libhead.VerifyError{Reason: fmt.Errorf("invalid header type: expected %T, got %T", eh, untrusted)}
40-
}
41-
if untrst.Height() != eh.Height()+1 {
42-
return &libhead.ErrNonAdjacent{
43-
Head: eh.Height(),
44-
Attempted: untrst.Height(),
38+
if !bytes.Equal(untrst.LastHeader(), eh.Hash()) {
39+
return &libhead.VerifyError{
40+
Reason: fmt.Errorf("expected new header to point to last header hash (%X), but got %X)",
41+
eh.Hash(),
42+
untrst.LastHeader(),
43+
),
44+
}
4545
}
46-
}
4746

48-
if err := eh.verify(untrst); err != nil {
49-
return &libhead.VerifyError{Reason: err}
47+
return nil
5048
}
5149

52-
// Check the validator hashes are the same
53-
if !bytes.Equal(untrst.ValidatorsHash, eh.NextValidatorsHash) {
54-
return &libhead.VerifyError{
55-
Reason: fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)",
56-
eh.NextValidatorsHash,
57-
untrst.ValidatorsHash,
58-
),
59-
}
50+
// Ensure that untrusted commit has enough of trusted commit's power.
51+
err := eh.ValidatorSet.VerifyCommitLightTrusting(eh.ChainID(), untrst.Commit, light.DefaultTrustLevel)
52+
if err != nil {
53+
return &libhead.VerifyError{Reason: err}
6054
}
6155

6256
return nil
@@ -67,13 +61,17 @@ func (eh *ExtendedHeader) VerifyAdjacent(untrusted libhead.Header) error {
6761
var clockDrift = 10 * time.Second
6862

6963
// verify performs basic verification of untrusted header.
70-
func (eh *ExtendedHeader) verify(untrst *ExtendedHeader) error {
64+
func (eh *ExtendedHeader) verify(untrst libhead.Header) error {
65+
if untrst.Height() <= eh.Height() {
66+
return fmt.Errorf("untrusted header height(%d) <= current trusted header(%d)", untrst.Height(), eh.Height())
67+
}
68+
7169
if untrst.ChainID() != eh.ChainID() {
72-
return fmt.Errorf("new untrusted header has different chain %s, not %s", untrst.ChainID(), eh.ChainID())
70+
return fmt.Errorf("untrusted header has different chain %s, not %s", untrst.ChainID(), eh.ChainID())
7371
}
7472

7573
if !untrst.Time().After(eh.Time()) {
76-
return fmt.Errorf("expected new untrusted header time %v to be after old header time %v", untrst.Time(), eh.Time())
74+
return fmt.Errorf("untrusted header time(%v) must be after current trusted header(%v)", untrst.Time(), eh.Time())
7775
}
7876

7977
now := time.Now()

Diff for: headertest/verify_test.go

-67
This file was deleted.

Diff for: libs/header/errors.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package header
22

33
import "fmt"
44

5-
// VerifyError is thrown on during VerifyAdjacent and VerifyNonAdjacent if verification fails.
5+
// VerifyError is thrown on during Verify if it fails.
66
type VerifyError struct {
77
Reason error
88
}

Diff for: libs/header/header.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ type Header interface {
2121
LastHeader() Hash
2222
// Time returns time when header was created.
2323
Time() time.Time
24-
// VerifyAdjacent validates adjacent untrusted header against trusted header.
25-
VerifyAdjacent(Header) error
26-
// VerifyNonAdjacent validates non-adjacent untrusted header against trusted header.
27-
VerifyNonAdjacent(Header) error
24+
// Verify validates given untrusted Header against trusted Header.
25+
Verify(Header) error
2826
// Validate performs basic validation to check for missed/incorrect fields.
2927
Validate() error
3028

Diff for: libs/header/p2p/exchange_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func TestExchange_RequestVerifiedHeadersFails(t *testing.T) {
7979
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
8080
t.Cleanup(cancel)
8181
_, err := exchg.GetVerifiedRange(ctx, h, 3)
82-
require.Error(t, err)
82+
assert.Error(t, err)
8383

8484
// ensure that peer was added to the blacklist
8585
peers := exchg.peerTracker.connGater.ListBlockedPeers()

Diff for: libs/header/p2p/session.go

+10-13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package p2p
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"sort"
78
"time"
89

@@ -160,6 +161,7 @@ func (s *session[H]) doRequest(
160161
case <-s.ctx.Done():
161162
case s.reqCh <- req:
162163
}
164+
log.Errorw("processing response", "err", err)
163165
return
164166
}
165167
log.Debugw("request headers from peer succeeded", "peer", stat.peerID, "amount", req.Amount)
@@ -201,23 +203,18 @@ func (s *session[H]) validate(headers []H) error {
201203
return nil
202204
}
203205

204-
// verify that the first header in range is valid against the trusted header.
205-
err := s.from.VerifyNonAdjacent(headers[0])
206-
if err != nil {
207-
return nil
208-
}
209-
210-
if len(headers) == 1 {
211-
return nil
212-
}
213-
214-
trusted := headers[0]
206+
trusted := s.from
215207
// verify that the whole range is valid.
216-
for i := 1; i < len(headers); i++ {
217-
err = trusted.VerifyAdjacent(headers[i])
208+
for i := 0; i < len(headers); i++ {
209+
err := trusted.Verify(headers[i])
218210
if err != nil {
219211
return err
220212
}
213+
if trusted.Height()+1 != headers[i].Height() {
214+
// Exchange requires requested ranges to always consist of adjacent headers
215+
return fmt.Errorf("peer sent valid but non-adjacent header")
216+
}
217+
221218
trusted = headers[i]
222219
}
223220
return nil

0 commit comments

Comments
 (0)