@@ -28,8 +28,11 @@ import (
2828
2929 "github.com/ethereum/go-ethereum/common"
3030 "github.com/ethereum/go-ethereum/core/types"
31+ "github.com/ethereum/go-ethereum/ethdb"
32+ "github.com/ethereum/go-ethereum/log"
3133 "github.com/ethereum/go-ethereum/params"
3234 "github.com/ethereum/go-ethereum/rlp"
35+ "github.com/stretchr/testify/assert"
3336 "golang.org/x/crypto/sha3"
3437)
3538
@@ -323,6 +326,262 @@ func TestBlockReceiptStorage(t *testing.T) {
323326 if err := checkReceiptsRLP (rs , receipts ); err != nil {
324327 t .Fatalf (err .Error ())
325328 }
329+ // check that the raw data does not contain the quorumExtraData array (since the prepared receipts do not have any quorumExtraData)
330+ receiptData := ReadReceiptsRLP (db , hash , 0 )
331+ _ , extraData , err := rlp .SplitList (receiptData )
332+ assert .NoError (t , err )
333+ assert .Empty (t , extraData )
334+ }
335+ // Delete the body and ensure that the receipts are no longer returned (metadata can't be recomputed)
336+ DeleteBody (db , hash , 0 )
337+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); rs != nil {
338+ t .Fatalf ("receipts returned when body was deleted: %v" , rs )
339+ }
340+ // Ensure that receipts without metadata can be returned without the block body too
341+ if err := checkReceiptsRLP (ReadRawReceipts (db , hash , 0 ), receipts ); err != nil {
342+ t .Fatalf (err .Error ())
343+ }
344+ // Sanity check that body alone without the receipt is a full purge
345+ WriteBody (db , hash , 0 , body )
346+
347+ DeleteReceipts (db , hash , 0 )
348+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); len (rs ) != 0 {
349+ t .Fatalf ("deleted receipts returned: %v" , rs )
350+ }
351+ }
352+
353+ func TestBlockReceiptStorageWithLegacyMPSV1EncodingWithMPSData (t * testing.T ) {
354+ db := NewMemoryDatabase ()
355+
356+ // Create a live block since we need metadata to reconstruct the receipt
357+ tx1 := types .NewTransaction (1 , common .HexToAddress ("0x1" ), big .NewInt (1 ), 1 , big .NewInt (1 ), nil )
358+ tx2 := types .NewTransaction (2 , common .HexToAddress ("0x2" ), big .NewInt (2 ), 2 , big .NewInt (2 ), nil )
359+ tx2 .SetPrivate ()
360+
361+ body := & types.Body {Transactions : types.Transactions {tx1 , tx2 }}
362+
363+ // Create the two receipts to manage afterwards
364+ receipt1 := & types.Receipt {
365+ Status : types .ReceiptStatusFailed ,
366+ CumulativeGasUsed : 1 ,
367+ Logs : []* types.Log {
368+ {Address : common .BytesToAddress ([]byte {0x11 })},
369+ {Address : common .BytesToAddress ([]byte {0x01 , 0x11 })},
370+ },
371+ TxHash : tx1 .Hash (),
372+ ContractAddress : common .BytesToAddress ([]byte {0x01 , 0x11 , 0x11 }),
373+ GasUsed : 111111 ,
374+ }
375+ receipt1 .Bloom = types .CreateBloom (types.Receipts {receipt1 })
376+
377+ psiReceipt2 := & types.Receipt {
378+ PostState : common.Hash {2 }.Bytes (),
379+ CumulativeGasUsed : 2 ,
380+ Logs : []* types.Log {
381+ {Address : common .BytesToAddress ([]byte {0x22 })},
382+ {Address : common .BytesToAddress ([]byte {0x02 , 0x22 })},
383+ },
384+ TxHash : tx2 .Hash (),
385+ ContractAddress : common .BytesToAddress ([]byte {0x02 , 0x22 , 0x22 }),
386+ GasUsed : 222222 ,
387+ }
388+
389+ receipt2 := & types.Receipt {
390+ PostState : common.Hash {2 }.Bytes (),
391+ CumulativeGasUsed : 2 ,
392+ Logs : []* types.Log {
393+ {Address : common .BytesToAddress ([]byte {0x22 })},
394+ {Address : common .BytesToAddress ([]byte {0x02 , 0x22 })},
395+ },
396+ TxHash : tx2 .Hash (),
397+ ContractAddress : common .BytesToAddress ([]byte {0x02 , 0x22 , 0x22 }),
398+ GasUsed : 222222 ,
399+ QuorumReceiptExtraData : types.QuorumReceiptExtraData {
400+ PSReceipts : map [types.PrivateStateIdentifier ]* types.Receipt {types .PrivateStateIdentifier ("psi1" ): psiReceipt2 },
401+ },
402+ }
403+ receipt2 .Bloom = types .CreateBloom (types.Receipts {receipt2 })
404+ receipts := []* types.Receipt {receipt1 , receipt2 }
405+
406+ // Check that no receipt entries are in a pristine database
407+ hash := common .BytesToHash ([]byte {0x03 , 0x14 })
408+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); len (rs ) != 0 {
409+ t .Fatalf ("non existent receipts returned: %v" , rs )
410+ }
411+ // Insert the body that corresponds to the receipts
412+ WriteBody (db , hash , 0 , body )
413+
414+ // Insert the receipt slice into the database and check presence
415+ WriteReceiptsMPSV1 (db , hash , 0 , receipts )
416+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); len (rs ) == 0 {
417+ t .Fatalf ("no receipts returned" )
418+ } else {
419+ if err := checkReceiptsRLP (rs , receipts ); err != nil {
420+ t .Fatalf (err .Error ())
421+ }
422+ rec2 := rs [1 ]
423+ assert .Len (t , rec2 .PSReceipts , 1 )
424+ psRec2 := rec2 .PSReceipts [types .PrivateStateIdentifier ("psi1" )]
425+ assert .NotNil (t , psRec2 )
426+ }
427+ // Delete the body and ensure that the receipts are no longer returned (metadata can't be recomputed)
428+ DeleteBody (db , hash , 0 )
429+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); rs != nil {
430+ t .Fatalf ("receipts returned when body was deleted: %v" , rs )
431+ }
432+ // Ensure that receipts without metadata can be returned without the block body too
433+ if err := checkReceiptsRLP (ReadRawReceipts (db , hash , 0 ), receipts ); err != nil {
434+ t .Fatalf (err .Error ())
435+ }
436+ }
437+
438+ func TestBlockReceiptStorageWithLegacyMPSV1EncodingWithoutMPSData (t * testing.T ) {
439+ db := NewMemoryDatabase ()
440+
441+ // Create a live block since we need metadata to reconstruct the receipt
442+ tx1 := types .NewTransaction (1 , common .HexToAddress ("0x1" ), big .NewInt (1 ), 1 , big .NewInt (1 ), nil )
443+ tx2 := types .NewTransaction (2 , common .HexToAddress ("0x2" ), big .NewInt (2 ), 2 , big .NewInt (2 ), nil )
444+ tx2 .SetPrivate ()
445+
446+ body := & types.Body {Transactions : types.Transactions {tx1 , tx2 }}
447+
448+ // Create the two receipts to manage afterwards
449+ receipt1 := & types.Receipt {
450+ Status : types .ReceiptStatusFailed ,
451+ CumulativeGasUsed : 1 ,
452+ Logs : []* types.Log {
453+ {Address : common .BytesToAddress ([]byte {0x11 })},
454+ {Address : common .BytesToAddress ([]byte {0x01 , 0x11 })},
455+ },
456+ TxHash : tx1 .Hash (),
457+ ContractAddress : common .BytesToAddress ([]byte {0x01 , 0x11 , 0x11 }),
458+ GasUsed : 111111 ,
459+ }
460+ receipt1 .Bloom = types .CreateBloom (types.Receipts {receipt1 })
461+
462+ receipt2 := & types.Receipt {
463+ PostState : common.Hash {2 }.Bytes (),
464+ CumulativeGasUsed : 2 ,
465+ Logs : []* types.Log {
466+ {Address : common .BytesToAddress ([]byte {0x22 })},
467+ {Address : common .BytesToAddress ([]byte {0x02 , 0x22 })},
468+ },
469+ TxHash : tx2 .Hash (),
470+ ContractAddress : common .BytesToAddress ([]byte {0x02 , 0x22 , 0x22 }),
471+ GasUsed : 222222 ,
472+ }
473+ receipt2 .Bloom = types .CreateBloom (types.Receipts {receipt2 })
474+ receipts := []* types.Receipt {receipt1 , receipt2 }
475+
476+ // Check that no receipt entries are in a pristine database
477+ hash := common .BytesToHash ([]byte {0x03 , 0x14 })
478+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); len (rs ) != 0 {
479+ t .Fatalf ("non existent receipts returned: %v" , rs )
480+ }
481+ // Insert the body that corresponds to the receipts
482+ WriteBody (db , hash , 0 , body )
483+
484+ // Insert the receipt slice into the database and check presence
485+ WriteReceiptsMPSV1 (db , hash , 0 , receipts )
486+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); len (rs ) == 0 {
487+ t .Fatalf ("no receipts returned" )
488+ } else {
489+ if err := checkReceiptsRLP (rs , receipts ); err != nil {
490+ t .Fatalf (err .Error ())
491+ }
492+ rec2 := rs [1 ]
493+ assert .Len (t , rec2 .PSReceipts , 0 )
494+ }
495+ // Delete the body and ensure that the receipts are no longer returned (metadata can't be recomputed)
496+ DeleteBody (db , hash , 0 )
497+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); rs != nil {
498+ t .Fatalf ("receipts returned when body was deleted: %v" , rs )
499+ }
500+ // Ensure that receipts without metadata can be returned without the block body too
501+ if err := checkReceiptsRLP (ReadRawReceipts (db , hash , 0 ), receipts ); err != nil {
502+ t .Fatalf (err .Error ())
503+ }
504+ }
505+
506+ // Tests that receipts associated with a single block can be stored and retrieved.
507+ func TestBlockReceiptStorageWithQuorumExtraData (t * testing.T ) {
508+ db := NewMemoryDatabase ()
509+
510+ // Create a live block since we need metadata to reconstruct the receipt
511+ tx1 := types .NewTransaction (1 , common .HexToAddress ("0x1" ), big .NewInt (1 ), 1 , big .NewInt (1 ), nil )
512+ tx2 := types .NewTransaction (2 , common .HexToAddress ("0x2" ), big .NewInt (2 ), 2 , big .NewInt (2 ), nil )
513+ tx2 .SetPrivate ()
514+
515+ body := & types.Body {Transactions : types.Transactions {tx1 , tx2 }}
516+
517+ // Create the two receipts to manage afterwards
518+ receipt1 := & types.Receipt {
519+ Status : types .ReceiptStatusFailed ,
520+ CumulativeGasUsed : 1 ,
521+ Logs : []* types.Log {
522+ {Address : common .BytesToAddress ([]byte {0x11 })},
523+ {Address : common .BytesToAddress ([]byte {0x01 , 0x11 })},
524+ },
525+ TxHash : tx1 .Hash (),
526+ ContractAddress : common .BytesToAddress ([]byte {0x01 , 0x11 , 0x11 }),
527+ GasUsed : 111111 ,
528+ }
529+ receipt1 .Bloom = types .CreateBloom (types.Receipts {receipt1 })
530+
531+ psiReceipt2 := & types.Receipt {
532+ PostState : common.Hash {2 }.Bytes (),
533+ CumulativeGasUsed : 2 ,
534+ Logs : []* types.Log {
535+ {Address : common .BytesToAddress ([]byte {0x22 })},
536+ {Address : common .BytesToAddress ([]byte {0x02 , 0x22 })},
537+ },
538+ TxHash : tx2 .Hash (),
539+ ContractAddress : common .BytesToAddress ([]byte {0x02 , 0x22 , 0x22 }),
540+ GasUsed : 222222 ,
541+ QuorumReceiptExtraData : types.QuorumReceiptExtraData {
542+ RevertReason : []byte ("arbitraryvalue" ),
543+ },
544+ }
545+
546+ receipt2 := & types.Receipt {
547+ PostState : common.Hash {2 }.Bytes (),
548+ CumulativeGasUsed : 2 ,
549+ Logs : []* types.Log {
550+ {Address : common .BytesToAddress ([]byte {0x22 })},
551+ {Address : common .BytesToAddress ([]byte {0x02 , 0x22 })},
552+ },
553+ TxHash : tx2 .Hash (),
554+ ContractAddress : common .BytesToAddress ([]byte {0x02 , 0x22 , 0x22 }),
555+ GasUsed : 222222 ,
556+ QuorumReceiptExtraData : types.QuorumReceiptExtraData {
557+ RevertReason : []byte ("arbitraryvalue" ),
558+ PSReceipts : map [types.PrivateStateIdentifier ]* types.Receipt {types .PrivateStateIdentifier ("psi1" ): psiReceipt2 },
559+ },
560+ }
561+ receipt2 .Bloom = types .CreateBloom (types.Receipts {receipt2 })
562+ receipts := []* types.Receipt {receipt1 , receipt2 }
563+
564+ // Check that no receipt entries are in a pristine database
565+ hash := common .BytesToHash ([]byte {0x03 , 0x14 })
566+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); len (rs ) != 0 {
567+ t .Fatalf ("non existent receipts returned: %v" , rs )
568+ }
569+ // Insert the body that corresponds to the receipts
570+ WriteBody (db , hash , 0 , body )
571+
572+ // Insert the receipt slice into the database and check presence
573+ WriteReceipts (db , hash , 0 , receipts )
574+ if rs := ReadReceipts (db , hash , 0 , params .TestChainConfig ); len (rs ) == 0 {
575+ t .Fatalf ("no receipts returned" )
576+ } else {
577+ if err := checkReceiptsRLP (rs , receipts ); err != nil {
578+ t .Fatalf (err .Error ())
579+ }
580+ rec2 := rs [1 ]
581+ assert .Len (t , rec2 .PSReceipts , 1 )
582+ assert .Equal (t , rec2 .RevertReason , []byte ("arbitraryvalue" ))
583+ psRec2 := rec2 .PSReceipts [types .PrivateStateIdentifier ("psi1" )]
584+ assert .Equal (t , psRec2 .RevertReason , []byte ("arbitraryvalue" ))
326585 }
327586 // Delete the body and ensure that the receipts are no longer returned (metadata can't be recomputed)
328587 DeleteBody (db , hash , 0 )
@@ -457,3 +716,19 @@ func TestCanonicalHashIteration(t *testing.T) {
457716 }
458717 }
459718}
719+
720+ func WriteReceiptsMPSV1 (db ethdb.KeyValueWriter , hash common.Hash , number uint64 , receipts types.Receipts ) {
721+ // Convert the receipts into their storage form and serialize them
722+ storageReceipts := make ([]* types.ReceiptForStorageMPSV1 , len (receipts ))
723+ for i , receipt := range receipts {
724+ storageReceipts [i ] = (* types .ReceiptForStorageMPSV1 )(receipt )
725+ }
726+ bytes , err := rlp .EncodeToBytes (storageReceipts )
727+ if err != nil {
728+ log .Crit ("Failed to encode block receipts" , "err" , err )
729+ }
730+ // Store the flattened receipt slice
731+ if err := db .Put (blockReceiptsKey (number , hash ), bytes ); err != nil {
732+ log .Crit ("Failed to store block receipts" , "err" , err )
733+ }
734+ }
0 commit comments