diff --git a/ChangeLog.md b/ChangeLog.md index ad5981794db..47b3a86805c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -36,6 +36,7 @@ ChangeLog ### New features: - Reduced `.idx` and `.efi` files size by 25% (require re-sync) +- Support: `debug_getRawReceipts` - debian packages - `--externalcl` support - bor-mainnet can work on 32G machine diff --git a/core/types/hashing.go b/core/types/hashing.go index 6a632b2907d..f7df717da2a 100644 --- a/core/types/hashing.go +++ b/core/types/hashing.go @@ -23,6 +23,7 @@ import ( "bytes" "fmt" "io" + "sync" "github.com/protolambda/ztyp/codec" @@ -34,6 +35,11 @@ import ( "github.com/erigontech/erigon-lib/trie" ) +// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding. +var encodeBufferPool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, +} + type DerivableList interface { Len() int EncodeIndex(i int, w *bytes.Buffer) diff --git a/core/types/receipt.go b/core/types/receipt.go index 271b25507b8..2e9669f231b 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -37,6 +37,8 @@ import ( //(go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go) +var errShortTypedReceipt = errors.New("typed receipt too short") + var ( receiptStatusFailedRLP = []byte{} receiptStatusSuccessfulRLP = []byte{0x01} @@ -123,14 +125,73 @@ func (r Receipt) EncodeRLP(w io.Writer) error { if r.Type == LegacyTxType { return rlp.Encode(w, data) } - buf := new(bytes.Buffer) - buf.WriteByte(r.Type) - if err := rlp.Encode(buf, data); err != nil { + buf := encodeBufferPool.Get().(*bytes.Buffer) + defer encodeBufferPool.Put(buf) + buf.Reset() + if err := r.encodeTyped(data, buf); err != nil { return err } return rlp.Encode(w, buf.Bytes()) } +// encodeTyped writes the canonical encoding of a typed receipt to w. +func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error { + w.WriteByte(r.Type) + return rlp.Encode(w, data) +} + +// MarshalBinary returns the consensus encoding of the receipt. +func (r *Receipt) MarshalBinary() ([]byte, error) { + if r.Type == LegacyTxType { + return rlp.EncodeToBytes(r) + } + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + var buf bytes.Buffer + err := r.encodeTyped(data, &buf) + return buf.Bytes(), err +} + +// UnmarshalBinary decodes the consensus encoding of receipts. +// It supports legacy RLP receipts and EIP-2718 typed receipts. +func (r *Receipt) UnmarshalBinary(b []byte) error { + if len(b) > 0 && b[0] > 0x7f { + // It's a legacy receipt decode the RLP + var data receiptRLP + err := rlp.DecodeBytes(b, &data) + if err != nil { + return err + } + r.Type = LegacyTxType + return r.setFromRLP(data) + } + // It's an EIP2718 typed transaction envelope. + return r.decodeTyped(b) +} + +func (r *Receipt) setFromRLP(data receiptRLP) error { + r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs + return r.setStatus(data.PostStateOrStatus) +} + +// decodeTyped decodes a typed receipt from the canonical format. +func (r *Receipt) decodeTyped(b []byte) error { + if len(b) <= 1 { + return errShortTypedReceipt + } + switch b[0] { + case DynamicFeeTxType, AccessListTxType, BlobTxType: + var data receiptRLP + err := rlp.DecodeBytes(b[1:], &data) + if err != nil { + return err + } + r.Type = b[0] + return r.setFromRLP(data) + default: + return ErrTxTypeNotSupported + } +} + func (r *Receipt) decodePayload(s *rlp.Stream) error { _, err := s.List() if err != nil { diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 262e35d92b8..b15f7737847 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -24,6 +24,7 @@ import ( "errors" "math" "math/big" + "reflect" "testing" "github.com/holiman/uint256" @@ -405,3 +406,155 @@ func clearComputedFieldsOnLog(t *testing.T, log *Log) { log.TxIndex = math.MaxUint32 log.Index = math.MaxUint32 } + +func TestReceiptUnmarshalBinary(t *testing.T) { + legacyReceipt := &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: libcommon.BytesToAddress([]byte{0x11}), + Topics: []libcommon.Hash{libcommon.HexToHash("dead"), libcommon.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: libcommon.BytesToAddress([]byte{0x01, 0x11}), + Topics: []libcommon.Hash{libcommon.HexToHash("dead"), libcommon.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + } + accessListReceipt := &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: libcommon.BytesToAddress([]byte{0x11}), + Topics: []libcommon.Hash{libcommon.HexToHash("dead"), libcommon.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: libcommon.BytesToAddress([]byte{0x01, 0x11}), + Topics: []libcommon.Hash{libcommon.HexToHash("dead"), libcommon.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + Type: AccessListTxType, + } + eip1559Receipt := &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: libcommon.BytesToAddress([]byte{0x11}), + Topics: []libcommon.Hash{libcommon.HexToHash("dead"), libcommon.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: libcommon.BytesToAddress([]byte{0x01, 0x11}), + Topics: []libcommon.Hash{libcommon.HexToHash("dead"), libcommon.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + Type: DynamicFeeTxType, + } + + t.Run("MarshalBinary", func(t *testing.T) { + // Legacy Receipt + legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt}) + have, err := legacyReceipt.MarshalBinary() + if err != nil { + t.Fatalf("marshal binary error: %v", err) + } + legacyReceipts := Receipts{legacyReceipt} + buf := new(bytes.Buffer) + legacyReceipts.EncodeIndex(0, buf) + haveEncodeIndex := buf.Bytes() + if !bytes.Equal(have, haveEncodeIndex) { + t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex) + } + buf.Reset() + if err := legacyReceipt.EncodeRLP(buf); err != nil { + t.Fatalf("encode rlp error: %v", err) + } + haveRLPEncode := buf.Bytes() + if !bytes.Equal(have, haveRLPEncode) { + t.Errorf("BinaryMarshal and EncodeRLP mismatch for legacy tx, got %x want %x", have, haveRLPEncode) + } + legacyWant := libcommon.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + if !bytes.Equal(have, legacyWant) { + t.Errorf("encoded RLP mismatch, got %x want %x", have, legacyWant) + } + + // 2930 Receipt + buf.Reset() + accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt}) + have, err = accessListReceipt.MarshalBinary() + if err != nil { + t.Fatalf("marshal binary error: %v", err) + } + accessListReceipts := Receipts{accessListReceipt} + accessListReceipts.EncodeIndex(0, buf) + haveEncodeIndex = buf.Bytes() + if !bytes.Equal(have, haveEncodeIndex) { + t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex) + } + accessListWant := libcommon.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + if !bytes.Equal(have, accessListWant) { + t.Errorf("encoded RLP mismatch, got %x want %x", have, accessListWant) + } + + // 1559 Receipt + buf.Reset() + eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt}) + have, err = eip1559Receipt.MarshalBinary() + if err != nil { + t.Fatalf("marshal binary error: %v", err) + } + eip1559Receipts := Receipts{eip1559Receipt} + eip1559Receipts.EncodeIndex(0, buf) + haveEncodeIndex = buf.Bytes() + if !bytes.Equal(have, haveEncodeIndex) { + t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex) + } + eip1559Want := libcommon.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + if !bytes.Equal(have, eip1559Want) { + t.Errorf("encoded RLP mismatch, got %x want %x", have, eip1559Want) + } + }) + + t.Run("UnmarshalBinary", func(t *testing.T) { + // Legacy Receipt + legacyBinary := libcommon.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + gotLegacyReceipt := new(Receipt) + if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil { + t.Fatalf("unmarshal binary error: %v", err) + } + legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt}) + if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) { + t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt) + } + + // 2930 Receipt + accessListBinary := libcommon.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + gotAccessListReceipt := new(Receipt) + if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil { + t.Fatalf("unmarshal binary error: %v", err) + } + accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt}) + if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) { + t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt) + } + + // 1559 Receipt + eip1559RctBinary := libcommon.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + got1559Receipt := new(Receipt) + if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil { + t.Fatalf("unmarshal binary error: %v", err) + } + eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt}) + if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) { + t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt) + } + }) +} diff --git a/turbo/jsonrpc/debug_api.go b/turbo/jsonrpc/debug_api.go index 6db377eccea..c6ddbe6a60d 100644 --- a/turbo/jsonrpc/debug_api.go +++ b/turbo/jsonrpc/debug_api.go @@ -61,6 +61,7 @@ type PrivateDebugAPI interface { AccountAt(ctx context.Context, blockHash common.Hash, txIndex uint64, account common.Address) (*AccountResult, error) GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutility.Bytes, error) GetRawBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutility.Bytes, error) + GetRawReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]hexutility.Bytes, error) } // PrivateDebugAPIImpl is implementation of the PrivateDebugAPI interface based on remote Db access @@ -393,3 +394,55 @@ func (api *PrivateDebugAPIImpl) GetRawBlock(ctx context.Context, blockNrOrHash r } return rlp.EncodeToBytes(block) } + +// GetRawReceipts retrieves the binary-encoded receipts of a single block. +func (api *PrivateDebugAPIImpl) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]hexutility.Bytes, error) { + tx, err := api.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + + blockNum, blockHash, _, err := rpchelper.GetBlockNumber(ctx, blockNrOrHash, tx, api._blockReader, api.filters) + if err != nil { + return nil, err + } + block, err := api.blockWithSenders(ctx, tx, blockHash, blockNum) + if err != nil { + return nil, err + } + if block == nil { + return nil, nil + } + receipts, err := api.getReceipts(ctx, tx, block) + if err != nil { + return nil, fmt.Errorf("getReceipts error: %w", err) + } + chainConfig, err := api.chainConfig(ctx, tx) + if err != nil { + return nil, err + } + if chainConfig.Bor != nil { + events, err := api.stateSyncEvents(ctx, tx, block.Hash(), blockNum, chainConfig) + if err != nil { + return nil, err + } + if len(events) != 0 { + borReceipt, err := api.borReceiptGenerator.GenerateBorReceipt(ctx, tx, block, events, chainConfig, receipts) + if err != nil { + return nil, err + } + receipts = append(receipts, borReceipt) + } + } + + result := make([]hexutility.Bytes, len(receipts)) + for i, receipt := range receipts { + b, err := receipt.MarshalBinary() + if err != nil { + return nil, err + } + result[i] = b + } + return result, nil +} diff --git a/turbo/jsonrpc/erigon_api.go b/turbo/jsonrpc/erigon_api.go index a200daf367f..ba869607c8d 100644 --- a/turbo/jsonrpc/erigon_api.go +++ b/turbo/jsonrpc/erigon_api.go @@ -19,15 +19,11 @@ package jsonrpc import ( "context" - "github.com/erigontech/erigon-lib/common/hexutil" - "github.com/erigontech/erigon-lib/common" - - "github.com/erigontech/erigon/eth/filters" - + "github.com/erigontech/erigon-lib/common/hexutil" "github.com/erigontech/erigon-lib/kv" - "github.com/erigontech/erigon/core/types" + "github.com/erigontech/erigon/eth/filters" "github.com/erigontech/erigon/p2p" "github.com/erigontech/erigon/rpc" "github.com/erigontech/erigon/turbo/rpchelper"