@@ -30,11 +30,11 @@ import (
30
30
"github.com/ava-labs/libevm/rlp"
31
31
)
32
32
33
- func TestBodyRLPBackwardsCompatibility (t * testing.T ) {
34
- newTx := func (nonce uint64 ) * Transaction { return NewTx (& LegacyTx {Nonce : nonce }) }
35
- newHdr := func (hashLow byte ) * Header { return & Header {ParentHash : common.Hash {hashLow }} }
36
- newWithdraw := func (idx uint64 ) * Withdrawal { return & Withdrawal {Index : idx } }
33
+ func newTx (nonce uint64 ) * Transaction { return NewTx (& LegacyTx {Nonce : nonce }) }
34
+ func newHdr (parentHashHigh byte ) * Header { return & Header {ParentHash : common.Hash {parentHashHigh }} }
35
+ func newWithdraw (idx uint64 ) * Withdrawal { return & Withdrawal {Index : idx } }
37
36
37
+ func blockBodyRLPTestInputs () []* Body {
38
38
// We build up test-case [Body] instances from the Cartesian product of each
39
39
// of these components.
40
40
txMatrix := [][]* Transaction {
@@ -61,8 +61,11 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
61
61
}
62
62
}
63
63
}
64
+ return bodies
65
+ }
64
66
65
- for _ , body := range bodies {
67
+ func TestBodyRLPBackwardsCompatibility (t * testing.T ) {
68
+ for _ , body := range blockBodyRLPTestInputs () {
66
69
t .Run ("" , func (t * testing.T ) {
67
70
t .Cleanup (func () {
68
71
if t .Failed () {
@@ -86,8 +89,10 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
86
89
t .Run ("Decode" , func (t * testing.T ) {
87
90
got := new (Body )
88
91
err := rlp .DecodeBytes (wantRLP , got )
89
- require .NoErrorf (t , err , "rlp.DecodeBytes(rlp.EncodeToBytes(%T), %T) resulted in %s" ,
90
- (* withoutMethods )(body ), got , pretty .Sprint (got ))
92
+ require .NoErrorf (
93
+ t , err , "rlp.DecodeBytes(rlp.EncodeToBytes(%T), %T) resulted in %s" ,
94
+ (* withoutMethods )(body ), got , pretty .Sprint (got ),
95
+ )
91
96
92
97
want := body
93
98
// Regular RLP decoding will never leave these non-optional
@@ -112,17 +117,94 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
112
117
}
113
118
}
114
119
120
+ func TestBlockRLPBackwardsCompatibility (t * testing.T ) {
121
+ TestOnlyClearRegisteredExtras ()
122
+ t .Cleanup (TestOnlyClearRegisteredExtras )
123
+
124
+ RegisterExtras [
125
+ NOOPHeaderHooks , * NOOPHeaderHooks ,
126
+ NOOPBlockBodyHooks , * NOOPBlockBodyHooks , // types under test
127
+ struct {},
128
+ ]()
129
+
130
+ // Note that there are also a number of tests in `block_test.go` that ensure
131
+ // backwards compatibility as [NOOPBlockBodyHooks] are used by default when
132
+ // nothing is registered (the above registration is only for completeness).
133
+
134
+ for _ , body := range blockBodyRLPTestInputs () {
135
+ t .Run ("" , func (t * testing.T ) {
136
+ // [Block] doesn't export most of its fields so uses [extblock] as a
137
+ // proxy for RLP encoding, which is what we therefore use as the
138
+ // backwards-compatible gold standard.
139
+ hdr := newHdr (99 )
140
+ block := extblock {
141
+ Header : hdr ,
142
+ Txs : body .Transactions ,
143
+ Uncles : body .Uncles ,
144
+ Withdrawals : body .Withdrawals ,
145
+ }
146
+
147
+ // We've added [extblock.EncodeRLP] and [extblock.DecodeRLP] for our
148
+ // hooks.
149
+ type withoutMethods extblock
150
+
151
+ wantRLP , err := rlp .EncodeToBytes (withoutMethods (block ))
152
+ require .NoErrorf (t , err , "rlp.EncodeToBytes([%T with methods stripped])" , block )
153
+
154
+ // Our input to RLP might not be the canonical RLP output.
155
+ var wantBlock extblock
156
+ err = rlp .DecodeBytes (wantRLP , (* withoutMethods )(& wantBlock ))
157
+ require .NoErrorf (t , err , "rlp.DecodeBytes(..., [%T with methods stripped])" , & wantBlock )
158
+
159
+ t .Run ("Encode" , func (t * testing.T ) {
160
+ b := NewBlockWithHeader (hdr ).WithBody (* body ).WithWithdrawals (body .Withdrawals )
161
+ got , err := rlp .EncodeToBytes (b )
162
+ require .NoErrorf (t , err , "rlp.EncodeToBytes(%T)" , b )
163
+
164
+ assert .Equalf (t , wantRLP , got , "expect %T RLP identical to that from %T struct stripped of methods" , got , extblock {})
165
+ })
166
+
167
+ t .Run ("Decode" , func (t * testing.T ) {
168
+ var gotBlock Block
169
+ err := rlp .DecodeBytes (wantRLP , & gotBlock )
170
+ require .NoErrorf (t , err , "rlp.DecodeBytes(..., %T)" , & gotBlock )
171
+
172
+ got := extblock {
173
+ gotBlock .Header (),
174
+ gotBlock .Transactions (),
175
+ gotBlock .Uncles (),
176
+ gotBlock .Withdrawals (),
177
+ nil , // unexported libevm hooks
178
+ }
179
+
180
+ opts := cmp.Options {
181
+ cmp .Comparer ((* Header ).equalHash ),
182
+ cmp .Comparer ((* Transaction ).equalHash ),
183
+ cmpopts .IgnoreUnexported (extblock {}),
184
+ }
185
+ if diff := cmp .Diff (wantBlock , got , opts ); diff != "" {
186
+ t .Errorf ("rlp.DecodeBytes([RLP from %T stripped of methods], ...) diff (-want +got):\n %s" , extblock {}, diff )
187
+ }
188
+ })
189
+ })
190
+ }
191
+ }
192
+
115
193
// cChainBodyExtras carries the same additional fields as the Avalanche C-Chain
116
- // (ava-labs/coreth) [Body] and implements [BodyHooks] to achieve equivalent RLP
117
- // {en,de}coding.
194
+ // (ava-labs/coreth) [Body] and implements [BlockBodyHooks] to achieve
195
+ // equivalent RLP {en,de}coding.
196
+ //
197
+ // It is not intended as a full test of ava-labs/coreth existing functionality,
198
+ // which should be implemented when that module consumes libevm, but as proof of
199
+ // equivalence of the [rlp.Fields] approach.
118
200
type cChainBodyExtras struct {
119
201
Version uint32
120
202
ExtData * []byte
121
203
}
122
204
123
- var _ BodyHooks = (* cChainBodyExtras )(nil )
205
+ var _ BlockBodyHooks = (* cChainBodyExtras )(nil )
124
206
125
- func (e * cChainBodyExtras ) RLPFieldsForEncoding (b * Body ) * rlp.Fields {
207
+ func (e * cChainBodyExtras ) BodyRLPFieldsForEncoding (b * Body ) * rlp.Fields {
126
208
// The Avalanche C-Chain uses all of the geth required fields (but none of
127
209
// the optional ones) so there's no need to explicitly list them. This
128
210
// pattern might not be ideal for readability but is used here for
@@ -132,13 +214,13 @@ func (e *cChainBodyExtras) RLPFieldsForEncoding(b *Body) *rlp.Fields {
132
214
// compatibility so this is safe to do, but only for the required fields.
133
215
return & rlp.Fields {
134
216
Required : append (
135
- NOOPBodyHooks {}.RLPFieldsForEncoding (b ).Required ,
217
+ NOOPBlockBodyHooks {}.BodyRLPFieldsForEncoding (b ).Required ,
136
218
e .Version , e .ExtData ,
137
219
),
138
220
}
139
221
}
140
222
141
- func (e * cChainBodyExtras ) RLPFieldPointersForDecoding (b * Body ) * rlp.Fields {
223
+ func (e * cChainBodyExtras ) BodyRLPFieldPointersForDecoding (b * Body ) * rlp.Fields {
142
224
// An alternative to the pattern used above is to explicitly list all
143
225
// fields for better introspection.
144
226
return & rlp.Fields {
@@ -151,6 +233,20 @@ func (e *cChainBodyExtras) RLPFieldPointersForDecoding(b *Body) *rlp.Fields {
151
233
}
152
234
}
153
235
236
+ // See [cChainBodyExtras] intent.
237
+
238
+ func (e * cChainBodyExtras ) Copy () * cChainBodyExtras {
239
+ panic ("unimplemented" )
240
+ }
241
+
242
+ func (e * cChainBodyExtras ) BlockRLPFieldsForEncoding (b * BlockRLPProxy ) * rlp.Fields {
243
+ panic ("unimplemented" )
244
+ }
245
+
246
+ func (e * cChainBodyExtras ) BlockRLPFieldPointersForDecoding (b * BlockRLPProxy ) * rlp.Fields {
247
+ panic ("unimplemented" )
248
+ }
249
+
154
250
func TestBodyRLPCChainCompat (t * testing.T ) {
155
251
// The inputs to this test were used to generate the expected RLP with
156
252
// ava-labs/coreth. This serves as both an example of how to use [BodyHooks]
0 commit comments