@@ -42,26 +42,41 @@ func buildSyncTestSigner(tb testing.TB) (addr []byte, pub crypto.PubKey, signer
4242 return a , p , n
4343}
4444
45- func makeSignedHeader (t * testing.T , chainID string , height uint64 , proposer []byte , pub crypto.PubKey , signer signerpkg.Signer , appHash []byte ) * types.SignedHeader {
45+ // makeSignedHeaderBytes builds a valid SignedHeader and returns its binary encoding and the object
46+ func makeSignedHeaderBytes (tb testing.TB , chainID string , height uint64 , proposer []byte , pub crypto.PubKey , signer signerpkg.Signer , appHash []byte , data * types.Data ) ([]byte , * types.SignedHeader ) {
47+ time := uint64 (time .Now ().UnixNano ())
48+ dataHash := common .DataHashForEmptyTxs
49+ if data != nil {
50+ time = uint64 (data .Time ().UnixNano ())
51+ dataHash = data .DACommitment ()
52+ }
53+
4654 hdr := & types.SignedHeader {
4755 Header : types.Header {
48- BaseHeader : types.BaseHeader {ChainID : chainID , Height : height , Time : uint64 ( time . Now (). Add ( time . Duration ( height ) * time . Second ). UnixNano ()) },
56+ BaseHeader : types.BaseHeader {ChainID : chainID , Height : height , Time : time },
4957 AppHash : appHash ,
58+ DataHash : dataHash ,
5059 ProposerAddress : proposer ,
5160 },
5261 Signer : types.Signer {PubKey : pub , Address : proposer },
5362 }
54- // sign using aggregator provider (sync node will re-verify using sync provider, which defaults to same header bytes)
5563 bz , err := types .DefaultAggregatorNodeSignatureBytesProvider (& hdr .Header )
56- require .NoError (t , err )
64+ require .NoError (tb , err )
5765 sig , err := signer .Sign (bz )
58- require .NoError (t , err )
66+ require .NoError (tb , err )
5967 hdr .Signature = sig
60- return hdr
68+ bin , err := hdr .MarshalBinary ()
69+ require .NoError (tb , err )
70+ return bin , hdr
6171}
6272
6373func makeData (chainID string , height uint64 , txs int ) * types.Data {
64- d := & types.Data {Metadata : & types.Metadata {ChainID : chainID , Height : height , Time : uint64 (time .Now ().UnixNano ())}}
74+ d := & types.Data {
75+ Metadata : & types.Metadata {
76+ ChainID : chainID ,
77+ Height : height ,
78+ Time : uint64 (time .Now ().Add (time .Duration (height ) * time .Second ).UnixNano ())},
79+ }
6580 if txs > 0 {
6681 d .Txs = make (types.Txs , txs )
6782 for i := 0 ; i < txs ; i ++ {
@@ -71,6 +86,54 @@ func makeData(chainID string, height uint64, txs int) *types.Data {
7186 return d
7287}
7388
89+ func TestSyncer_validateBlock_DataHashMismatch (t * testing.T ) {
90+ ds := dssync .MutexWrap (datastore .NewMapDatastore ())
91+ st := store .New (ds )
92+ cm , err := cache .NewManager (config .DefaultConfig (), st , zerolog .Nop ())
93+ require .NoError (t , err )
94+
95+ addr , pub , signer := buildSyncTestSigner (t )
96+
97+ cfg := config .DefaultConfig ()
98+ gen := genesis.Genesis {ChainID : "tchain" , InitialHeight : 1 , StartTime : time .Now ().Add (- time .Second ), ProposerAddress : addr }
99+
100+ mockExec := testmocks .NewMockExecutor (t )
101+
102+ s := NewSyncer (
103+ st ,
104+ mockExec ,
105+ nil ,
106+ cm ,
107+ common .NopMetrics (),
108+ cfg ,
109+ gen ,
110+ nil ,
111+ nil ,
112+ zerolog .Nop (),
113+ common .DefaultBlockOptions (),
114+ make (chan error , 1 ),
115+ )
116+
117+ // Create header and data with correct hash
118+ data := makeData (gen .ChainID , 1 , 2 ) // non-empty
119+ _ , header := makeSignedHeaderBytes (t , gen .ChainID , 1 , addr , pub , signer , nil , data )
120+
121+ err = s .validateBlock (header , data )
122+ require .NoError (t , err )
123+
124+ // Create header and data with mismatched hash
125+ data = makeData (gen .ChainID , 1 , 2 ) // non-empty
126+ _ , header = makeSignedHeaderBytes (t , gen .ChainID , 1 , addr , pub , signer , nil , nil )
127+ err = s .validateBlock (header , data )
128+ require .Error (t , err )
129+
130+ // Create header and empty data
131+ data = makeData (gen .ChainID , 1 , 0 ) // empty
132+ _ , header = makeSignedHeaderBytes (t , gen .ChainID , 2 , addr , pub , signer , nil , nil )
133+ err = s .validateBlock (header , data )
134+ require .Error (t , err )
135+ }
136+
74137func TestProcessHeightEvent_SyncsAndUpdatesState (t * testing.T ) {
75138 ds := dssync .MutexWrap (datastore .NewMapDatastore ())
76139 st := store .New (ds )
@@ -104,9 +167,8 @@ func TestProcessHeightEvent_SyncsAndUpdatesState(t *testing.T) {
104167 s .ctx = context .Background ()
105168 // Create signed header & data for height 1
106169 lastState := s .GetLastState ()
107- hdr := makeSignedHeader (t , gen .ChainID , 1 , addr , pub , signer , lastState .AppHash )
108170 data := makeData (gen .ChainID , 1 , 0 )
109- // For empty data, header.DataHash should be set by producer; here we don't rely on it for syncing
171+ _ , hdr := makeSignedHeaderBytes ( t , gen . ChainID , 1 , addr , pub , signer , lastState . AppHash , data )
110172
111173 // Expect ExecuteTxs call for height 1
112174 mockExec .EXPECT ().ExecuteTxs (mock .Anything , mock .Anything , uint64 (1 ), mock .Anything , lastState .AppHash ).
@@ -154,17 +216,17 @@ func TestSequentialBlockSync(t *testing.T) {
154216
155217 // Sync two consecutive blocks via processHeightEvent so ExecuteTxs is called and state stored
156218 st0 := s .GetLastState ()
157- hdr1 := makeSignedHeader (t , gen .ChainID , 1 , addr , pub , signer , st0 .AppHash )
158219 data1 := makeData (gen .ChainID , 1 , 1 ) // non-empty
220+ _ , hdr1 := makeSignedHeaderBytes (t , gen .ChainID , 1 , addr , pub , signer , st0 .AppHash , data1 )
159221 // Expect ExecuteTxs call for height 1
160222 mockExec .EXPECT ().ExecuteTxs (mock .Anything , mock .Anything , uint64 (1 ), mock .Anything , st0 .AppHash ).
161223 Return ([]byte ("app1" ), uint64 (1024 ), nil ).Once ()
162224 evt1 := common.DAHeightEvent {Header : hdr1 , Data : data1 , DaHeight : 10 }
163225 s .processHeightEvent (& evt1 )
164226
165227 st1 , _ := st .GetState (context .Background ())
166- hdr2 := makeSignedHeader (t , gen .ChainID , 2 , addr , pub , signer , st1 .AppHash )
167228 data2 := makeData (gen .ChainID , 2 , 0 ) // empty data
229+ _ , hdr2 := makeSignedHeaderBytes (t , gen .ChainID , 2 , addr , pub , signer , st1 .AppHash , data2 )
168230 // Expect ExecuteTxs call for height 2
169231 mockExec .EXPECT ().ExecuteTxs (mock .Anything , mock .Anything , uint64 (2 ), mock .Anything , st1 .AppHash ).
170232 Return ([]byte ("app2" ), uint64 (1024 ), nil ).Once ()
@@ -287,17 +349,17 @@ func TestSyncLoopPersistState(t *testing.T) {
287349 // with n da blobs fetched
288350 for i := range myFutureDAHeight - myDAHeightOffset {
289351 chainHeight , daHeight := i , i + myDAHeightOffset
290- _ , sigHeader := makeSignedHeaderBytes (t , gen .ChainID , chainHeight , addr , pub , signer , nil , nil )
291- emptyData := types.Data {
352+ emptyData := & types.Data {
292353 Metadata : & types.Metadata {
293- ChainID : sigHeader .ChainID () ,
294- Height : sigHeader . Height () ,
295- Time : sigHeader . BaseHeader . Time ,
354+ ChainID : gen .ChainID ,
355+ Height : chainHeight ,
356+ Time : uint64 ( time . Now (). Add ( time . Duration ( chainHeight ) * time . Second ). UnixNano ()) ,
296357 },
297358 }
359+ _ , sigHeader := makeSignedHeaderBytes (t , gen .ChainID , chainHeight , addr , pub , signer , nil , emptyData )
298360 evts := []common.DAHeightEvent {{
299361 Header : sigHeader ,
300- Data : & emptyData ,
362+ Data : emptyData ,
301363 DaHeight : daHeight ,
302364 }}
303365 daRtrMock .On ("RetrieveFromDA" , mock .Anything , daHeight ).Return (evts , nil )
0 commit comments