@@ -170,7 +170,23 @@ type Body struct {
170
170
Withdrawals []* Withdrawal `rlp:"optional"`
171
171
}
172
172
173
- // Block represents an entire block in the Ethereum blockchain.
173
+ // Block represents an Ethereum block.
174
+ //
175
+ // Note the Block type tries to be 'immutable', and contains certain caches that rely
176
+ // on that. The rules around block immutability are as follows:
177
+ //
178
+ // - We copy all data when the block is constructed. This makes references held inside
179
+ // the block independent of whatever value was passed in.
180
+ //
181
+ // - We copy all header data on access. This is because any change to the header would mess
182
+ // up the cached hash and size values in the block. Calling code is expected to take
183
+ // advantage of this to avoid over-allocating!
184
+ //
185
+ // - When new body data is attached to the block, a shallow copy of the block is returned.
186
+ // This ensures block modifications are race-free.
187
+ //
188
+ // - We do not copy body data on access because it does not affect the caches, and also
189
+ // because it would be too expensive.
174
190
type Block struct {
175
191
header * Header
176
192
uncles []* Header
@@ -195,9 +211,8 @@ type extblock struct {
195
211
Withdrawals []* Withdrawal `rlp:"optional"`
196
212
}
197
213
198
- // NewBlock creates a new block. The input data is copied,
199
- // changes to header and to the field values will not affect the
200
- // block.
214
+ // NewBlock creates a new block. The input data is copied, changes to header and to the
215
+ // field values will not affect the block.
201
216
//
202
217
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
203
218
// are ignored and set to values derived from the given txs, uncles
@@ -234,13 +249,11 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
234
249
return b
235
250
}
236
251
237
- // NewBlockWithWithdrawals creates a new block with withdrawals. The input data
238
- // is copied, changes to header and to the field values will not
239
- // affect the block.
252
+ // NewBlockWithWithdrawals creates a new block with withdrawals. The input data is copied,
253
+ // changes to header and to the field values will not affect the block.
240
254
//
241
- // The values of TxHash, UncleHash, ReceiptHash and Bloom in header
242
- // are ignored and set to values derived from the given txs, uncles
243
- // and receipts.
255
+ // The values of TxHash, UncleHash, ReceiptHash and Bloom in header are ignored and set to
256
+ // values derived from the given txs, uncles and receipts.
244
257
func NewBlockWithWithdrawals (header * Header , txs []* Transaction , uncles []* Header , receipts []* Receipt , withdrawals []* Withdrawal , hasher TrieHasher ) * Block {
245
258
b := NewBlock (header , txs , uncles , receipts , hasher )
246
259
@@ -256,15 +269,7 @@ func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Heade
256
269
return b .WithWithdrawals (withdrawals )
257
270
}
258
271
259
- // NewBlockWithHeader creates a block with the given header data. The
260
- // header data is copied, changes to header and to the field values
261
- // will not affect the block.
262
- func NewBlockWithHeader (header * Header ) * Block {
263
- return & Block {header : CopyHeader (header )}
264
- }
265
-
266
- // CopyHeader creates a deep copy of a block header to prevent side effects from
267
- // modifying a header variable.
272
+ // CopyHeader creates a deep copy of a block header.
268
273
func CopyHeader (h * Header ) * Header {
269
274
cpy := * h
270
275
if cpy .Difficulty = new (big.Int ); h .Difficulty != nil {
@@ -295,7 +300,7 @@ func CopyHeader(h *Header) *Header {
295
300
return & cpy
296
301
}
297
302
298
- // DecodeRLP decodes the Ethereum
303
+ // DecodeRLP decodes a block from RLP.
299
304
func (b * Block ) DecodeRLP (s * rlp.Stream ) error {
300
305
var eb extblock
301
306
_ , size , _ := s .Kind ()
@@ -307,20 +312,28 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
307
312
return nil
308
313
}
309
314
310
- // EncodeRLP serializes b into the Ethereum RLP block format .
315
+ // EncodeRLP serializes a block as RLP.
311
316
func (b * Block ) EncodeRLP (w io.Writer ) error {
312
- return rlp .Encode (w , extblock {
317
+ return rlp .Encode (w , & extblock {
313
318
Header : b .header ,
314
319
Txs : b .transactions ,
315
320
Uncles : b .uncles ,
316
321
Withdrawals : b .withdrawals ,
317
322
})
318
323
}
319
324
320
- // TODO: copies
325
+ // Body returns the non-header content of the block.
326
+ // Note the returned data is not an independent copy.
327
+ func (b * Block ) Body () * Body {
328
+ return & Body {b .transactions , b .uncles , b .withdrawals }
329
+ }
330
+
331
+ // Accessors for body data. These do not return a copy because the content
332
+ // of the body slices does not affect the cached hash/size in block.
321
333
322
334
func (b * Block ) Uncles () []* Header { return b .uncles }
323
335
func (b * Block ) Transactions () Transactions { return b .transactions }
336
+ func (b * Block ) Withdrawals () Withdrawals { return b .withdrawals }
324
337
325
338
func (b * Block ) Transaction (hash common.Hash ) * Transaction {
326
339
for _ , transaction := range b .transactions {
@@ -331,6 +344,13 @@ func (b *Block) Transaction(hash common.Hash) *Transaction {
331
344
return nil
332
345
}
333
346
347
+ // Header returns the block header (as a copy).
348
+ func (b * Block ) Header () * Header {
349
+ return CopyHeader (b .header )
350
+ }
351
+
352
+ // Header value accessors. These do copy!
353
+
334
354
func (b * Block ) Number () * big.Int { return new (big.Int ).Set (b .header .Number ) }
335
355
func (b * Block ) GasLimit () uint64 { return b .header .GasLimit }
336
356
func (b * Block ) GasUsed () uint64 { return b .header .GasUsed }
@@ -356,10 +376,6 @@ func (b *Block) BaseFee() *big.Int {
356
376
return new (big.Int ).Set (b .header .BaseFee )
357
377
}
358
378
359
- func (b * Block ) Withdrawals () Withdrawals {
360
- return b .withdrawals
361
- }
362
-
363
379
func (b * Block ) ExcessBlobGas () * uint64 {
364
380
var excessBlobGas * uint64
365
381
if b .header .ExcessBlobGas != nil {
@@ -378,11 +394,6 @@ func (b *Block) BlobGasUsed() *uint64 {
378
394
return blobGasUsed
379
395
}
380
396
381
- func (b * Block ) Header () * Header { return CopyHeader (b .header ) }
382
-
383
- // Body returns the non-header content of the block.
384
- func (b * Block ) Body () * Body { return & Body {b .transactions , b .uncles , b .withdrawals } }
385
-
386
397
// Size returns the true RLP encoded storage size of the block, either by encoding
387
398
// and returning it, or returning a previously cached value.
388
399
func (b * Block ) Size () uint64 {
@@ -415,25 +426,31 @@ func CalcUncleHash(uncles []*Header) common.Hash {
415
426
return rlpHash (uncles )
416
427
}
417
428
429
+ // NewBlockWithHeader creates a block with the given header data. The
430
+ // header data is copied, changes to header and to the field values
431
+ // will not affect the block.
432
+ func NewBlockWithHeader (header * Header ) * Block {
433
+ return & Block {header : CopyHeader (header )}
434
+ }
435
+
418
436
// WithSeal returns a new block with the data from b but the header replaced with
419
437
// the sealed one.
420
438
func (b * Block ) WithSeal (header * Header ) * Block {
421
- cpy := * header
422
-
423
439
return & Block {
424
- header : & cpy ,
440
+ header : CopyHeader ( header ) ,
425
441
transactions : b .transactions ,
426
442
uncles : b .uncles ,
427
443
withdrawals : b .withdrawals ,
428
444
}
429
445
}
430
446
431
- // WithBody returns a new block with the given transaction and uncle contents.
447
+ // WithBody returns a copy of the block with the given transaction and uncle contents.
432
448
func (b * Block ) WithBody (transactions []* Transaction , uncles []* Header ) * Block {
433
449
block := & Block {
434
- header : CopyHeader ( b .header ) ,
450
+ header : b .header ,
435
451
transactions : make ([]* Transaction , len (transactions )),
436
452
uncles : make ([]* Header , len (uncles )),
453
+ withdrawals : b .withdrawals ,
437
454
}
438
455
copy (block .transactions , transactions )
439
456
for i := range uncles {
@@ -442,13 +459,18 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block {
442
459
return block
443
460
}
444
461
445
- // WithWithdrawals sets the withdrawal contents of a block, does not return a new block .
462
+ // WithWithdrawals returns a copy of the block containing the given withdrawals .
446
463
func (b * Block ) WithWithdrawals (withdrawals []* Withdrawal ) * Block {
464
+ block := & Block {
465
+ header : b .header ,
466
+ transactions : b .transactions ,
467
+ uncles : b .uncles ,
468
+ }
447
469
if withdrawals != nil {
448
- b .withdrawals = make ([]* Withdrawal , len (withdrawals ))
449
- copy (b .withdrawals , withdrawals )
470
+ block .withdrawals = make ([]* Withdrawal , len (withdrawals ))
471
+ copy (block .withdrawals , withdrawals )
450
472
}
451
- return b
473
+ return block
452
474
}
453
475
454
476
// Hash returns the keccak256 hash of b's header.
0 commit comments