diff --git a/cmd/hack/hack.go b/cmd/hack/hack.go index bf454f1a7..d18bde2dc 100644 --- a/cmd/hack/hack.go +++ b/cmd/hack/hack.go @@ -979,7 +979,7 @@ func scanTxs(chaindata string) error { return err } var tr types.Transaction - if tr, err = types.DecodeTransaction(rlp.NewStream(bytes.NewReader(v), 0)); err != nil { + if tr, err = types.DecodeTransaction(v); err != nil { return err } if _, ok := trTypes[tr.Type()]; !ok { diff --git a/cmd/rpcdaemon/commands/eth_txs.go b/cmd/rpcdaemon/commands/eth_txs.go index 21fe98462..4831e4d92 100644 --- a/cmd/rpcdaemon/commands/eth_txs.go +++ b/cmd/rpcdaemon/commands/eth_txs.go @@ -14,7 +14,6 @@ import ( "github.com/ledgerwatch/erigon/common/hexutil" "github.com/ledgerwatch/erigon/core/rawdb" types2 "github.com/ledgerwatch/erigon/core/types" - "github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/rpc" "github.com/ledgerwatch/erigon/turbo/rpchelper" ) @@ -99,8 +98,7 @@ func (api *APIImpl) GetTransactionByHash(ctx context.Context, txnHash common.Has return nil, err } if len(reply.RlpTxs[0]) > 0 { - s := rlp.NewStream(bytes.NewReader(reply.RlpTxs[0]), uint64(len(reply.RlpTxs[0]))) - txn, err := types2.DecodeTransaction(s) + txn, err := types2.DecodeTransaction(reply.RlpTxs[0]) if err != nil { return nil, err } diff --git a/cmd/rpcdaemon/commands/send_transaction.go b/cmd/rpcdaemon/commands/send_transaction.go index 2db43dde0..4c852598f 100644 --- a/cmd/rpcdaemon/commands/send_transaction.go +++ b/cmd/rpcdaemon/commands/send_transaction.go @@ -1,7 +1,6 @@ package commands import ( - "bytes" "context" "errors" "fmt" @@ -17,12 +16,11 @@ import ( "github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/eth/ethconfig" "github.com/ledgerwatch/erigon/params" - "github.com/ledgerwatch/erigon/rlp" ) // SendRawTransaction implements eth_sendRawTransaction. Creates new message call transaction or a contract creation for previously-signed transactions. func (api *APIImpl) SendRawTransaction(ctx context.Context, encodedTx hexutility.Bytes) (common.Hash, error) { - txn, err := types.DecodeTransaction(rlp.NewStream(bytes.NewReader(encodedTx), uint64(len(encodedTx)))) + txn, err := types.DecodeWrappedTransaction(encodedTx) if err != nil { return common.Hash{}, err } diff --git a/cmd/rpcdaemon/commands/txpool_api.go b/cmd/rpcdaemon/commands/txpool_api.go index e75e79268..371c0065f 100644 --- a/cmd/rpcdaemon/commands/txpool_api.go +++ b/cmd/rpcdaemon/commands/txpool_api.go @@ -1,7 +1,6 @@ package commands import ( - "bytes" "context" "fmt" @@ -13,7 +12,6 @@ import ( "github.com/ledgerwatch/erigon/common/hexutil" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" - "github.com/ledgerwatch/erigon/rlp" ) // NetAPI the interface for the net_ RPC commands @@ -53,8 +51,7 @@ func (api *TxPoolAPIImpl) Content(ctx context.Context) (map[string]map[string]ma baseFee := make(map[libcommon.Address][]types.Transaction, 8) queued := make(map[libcommon.Address][]types.Transaction, 8) for i := range reply.Txs { - stream := rlp.NewStream(bytes.NewReader(reply.Txs[i].RlpTx), 0) - txn, err := types.DecodeTransaction(stream) + txn, err := types.DecodeTransaction(reply.Txs[i].RlpTx) if err != nil { return nil, err } diff --git a/cmd/sentry/sentry/broadcast.go b/cmd/sentry/sentry/broadcast.go index e9e2a796d..3ec9d8245 100644 --- a/cmd/sentry/sentry/broadcast.go +++ b/cmd/sentry/sentry/broadcast.go @@ -1,7 +1,6 @@ package sentry import ( - "bytes" "context" "errors" "math" @@ -72,7 +71,7 @@ func (cs *MultiClient) BroadcastNewBlock(ctx context.Context, header *types.Head txs := make([]types.Transaction, len(body.Transactions)) for i, tx := range body.Transactions { var err error - if txs[i], err = types.DecodeTransaction(rlp.NewStream(bytes.NewReader(tx), 0)); err != nil { + if txs[i], err = types.DecodeTransaction(tx); err != nil { log.Error("broadcastNewBlock", "err", err) return } diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 6cfe67ea4..adbf54f1e 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -404,7 +404,7 @@ func CanonicalTxnByID(db kv.Getter, id uint64, blockHash libcommon.Hash, transac if len(v) == 0 { return nil, nil } - txn, err := types.DecodeTransaction(rlp.NewStream(bytes.NewReader(v), uint64(len(v)))) + txn, err := types.DecodeTransaction(v) if err != nil { return nil, err } @@ -416,17 +416,13 @@ func CanonicalTransactions(db kv.Getter, baseTxId uint64, amount uint32) ([]type return []types.Transaction{}, nil } txIdKey := make([]byte, 8) - reader := bytes.NewReader(nil) - stream := rlp.NewStream(reader, 0) txs := make([]types.Transaction, amount) binary.BigEndian.PutUint64(txIdKey, baseTxId) i := uint32(0) if err := db.ForAmount(kv.EthTx, txIdKey, amount, func(k, v []byte) error { var decodeErr error - reader.Reset(v) - stream.Reset(reader, 0) - if txs[i], decodeErr = types.DecodeTransaction(stream); decodeErr != nil { + if txs[i], decodeErr = types.UnmarshalTransactionFromBinary(v); decodeErr != nil { return decodeErr } i++ @@ -443,17 +439,13 @@ func NonCanonicalTransactions(db kv.Getter, baseTxId uint64, amount uint32) ([]t return []types.Transaction{}, nil } txIdKey := make([]byte, 8) - reader := bytes.NewReader(nil) - stream := rlp.NewStream(reader, 0) txs := make([]types.Transaction, amount) binary.BigEndian.PutUint64(txIdKey, baseTxId) i := uint32(0) if err := db.ForAmount(kv.NonCanonicalTxs, txIdKey, amount, func(k, v []byte) error { var decodeErr error - reader.Reset(v) - stream.Reset(reader, 0) - if txs[i], decodeErr = types.DecodeTransaction(stream); decodeErr != nil { + if txs[i], decodeErr = types.DecodeTransaction(v); decodeErr != nil { return decodeErr } i++ diff --git a/core/types/block.go b/core/types/block.go index 0aa1b38f5..daa449322 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -1080,7 +1080,7 @@ func (bb *Body) DecodeRLP(s *rlp.Stream) error { return err } var tx Transaction - for tx, err = DecodeTransaction(s); err == nil; tx, err = DecodeTransaction(s) { + for tx, err = DecodeRLPTransaction(s); err == nil; tx, err = DecodeRLPTransaction(s) { bb.Transactions = append(bb.Transactions, tx) } if !errors.Is(err, rlp.EOL) { @@ -1259,7 +1259,7 @@ func (bb *Block) DecodeRLP(s *rlp.Stream) error { return err } var tx Transaction - for tx, err = DecodeTransaction(s); err == nil; tx, err = DecodeTransaction(s) { + for tx, err = DecodeRLPTransaction(s); err == nil; tx, err = DecodeRLPTransaction(s) { bb.transactions = append(bb.transactions, tx) } if !errors.Is(err, rlp.EOL) { diff --git a/core/types/transaction.go b/core/types/transaction.go index 01f752263..f405b4bb1 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -126,7 +126,7 @@ func (tm TransactionMisc) From() *atomic.Value { return &tm.from } -func DecodeTransaction(s *rlp.Stream) (Transaction, error) { +func DecodeRLPTransaction(s *rlp.Stream) (Transaction, error) { kind, size, err := s.Kind() if err != nil { return nil, err @@ -138,44 +138,98 @@ func DecodeTransaction(s *rlp.Stream) (Transaction, error) { } return tx, nil } - if rlp.String == kind { - s.NewList(size) // Hack - convert String (envelope) into List + if rlp.String != kind { + return nil, fmt.Errorf("Not an RLP encoded transaction. If this is a canonical encoded transaction, use UnmarshalTransactionFromBinary instead. Got %v for kind, expected String", kind) } + // Decode the EIP-2718 typed TX envelope. var b []byte if b, err = s.Bytes(); err != nil { return nil, err } - if len(b) != 1 { - return nil, fmt.Errorf("%w, got %d bytes", rlp.ErrWrongTxTypePrefix, len(b)) + if len(b) == 0 { + return nil, rlp.EOL } - var tx Transaction - switch b[0] { - case AccessListTxType: - t := &AccessListTx{} - if err = t.DecodeRLP(s); err != nil { - return nil, err - } - tx = t - case DynamicFeeTxType: - t := &DynamicFeeTransaction{} - if err = t.DecodeRLP(s); err != nil { - return nil, err - } - tx = t - default: - return nil, fmt.Errorf("%w, got: %d", rlp.ErrUnknownTxTypePrefix, b[0]) - } - if kind == rlp.String { - if err = s.ListEnd(); err != nil { - return nil, err - } - } - return tx, nil + return UnmarshalTransactionFromBinary(b) } -func UnmarshalTransactionFromBinary(data []byte) (Transaction, error) { +// DecodeWrappedTransaction decodes network encoded transaction with or without +// envelope. When transaction is not network encoded use DecodeTransaction. +func DecodeWrappedTransaction(data []byte) (Transaction, error) { + if len(data) == 0 { + return nil, io.EOF + } + if data[0] < 0x80 { // the encoding is canonical, not RLP + + // EIP-4844 tx differs from previous types of transactions in network + // encoding. It's SSZ encoded and includes blobs and kzgs. + // Previous types have no different encoding. + return UnmarshalWrappedTransactionFromBinary(data) + } s := rlp.NewStream(bytes.NewReader(data), uint64(len(data))) - return DecodeTransaction(s) + return DecodeRLPTransaction(s) +} + +// DecodeTransaction decodes a transaction either in RLP or canonical format +func DecodeTransaction(data []byte) (Transaction, error) { + if len(data) == 0 { + return nil, io.EOF + } + if data[0] < 0x80 { + // the encoding is canonical, not RLP + return UnmarshalTransactionFromBinary(data) + } + s := rlp.NewStream(bytes.NewReader(data), uint64(len(data))) + return DecodeRLPTransaction(s) +} + +// Parse transaction without envelope. +func UnmarshalTransactionFromBinary(data []byte) (Transaction, error) { + if len(data) <= 1 { + return nil, fmt.Errorf("short input: %v", len(data)) + } + switch data[0] { + case BlobTxType: + t := &SignedBlobTx{} + if err := DecodeSSZ(data[1:], t); err != nil { + return nil, err + } + return t, nil + case AccessListTxType: + s := rlp.NewStream(bytes.NewReader(data[1:]), uint64(len(data)-1)) + t := &AccessListTx{} + if err := t.DecodeRLP(s); err != nil { + return nil, err + } + return t, nil + case DynamicFeeTxType: + s := rlp.NewStream(bytes.NewReader(data[1:]), uint64(len(data)-1)) + t := &DynamicFeeTransaction{} + if err := t.DecodeRLP(s); err != nil { + return nil, err + } + return t, nil + default: + if data[0] >= 0x80 { + // Tx is type legacy which is RLP encoded + return DecodeTransaction(data) + } + return nil, ErrTxTypeNotSupported + } +} + +// Parse network encoded transaction without envelope. +func UnmarshalWrappedTransactionFromBinary(data []byte) (Transaction, error) { + if len(data) <= 1 { + return nil, fmt.Errorf("short input: %v", len(data)) + } + if data[0] != BlobTxType { + return UnmarshalTransactionFromBinary(data) + } + t := &BlobTxWrapper{} + if err := DecodeSSZ(data[1:], t); err != nil { + return nil, err + } + return t, nil } func MarshalTransactionsBinary(txs Transactions) ([][]byte, error) { @@ -201,8 +255,7 @@ func DecodeTransactions(txs [][]byte) ([]Transaction, error) { result := make([]Transaction, len(txs)) var err error for i := range txs { - s := rlp.NewStream(bytes.NewReader(txs[i]), uint64(len(txs[i]))) - result[i], err = DecodeTransaction(s) + result[i], err = UnmarshalTransactionFromBinary(txs[i]) if err != nil { return nil, err } diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go index 166437561..76e306528 100644 --- a/core/types/transaction_signing_test.go +++ b/core/types/transaction_signing_test.go @@ -17,7 +17,6 @@ package types import ( - "bytes" "math/big" "testing" @@ -26,7 +25,6 @@ import ( "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/crypto" - "github.com/ledgerwatch/erigon/rlp" ) func TestEIP1559Signing(t *testing.T) { @@ -118,7 +116,7 @@ func TestEIP155SigningVitalik(t *testing.T) { } { signer := LatestSignerForChainID(big.NewInt(1)) - tx, err := DecodeTransaction(rlp.NewStream(bytes.NewReader(common.Hex2Bytes(test.txRlp)), 0)) + tx, err := DecodeTransaction(common.Hex2Bytes(test.txRlp)) if err != nil { t.Errorf("%d: %v", i, err) continue diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 25ef06828..81ba8cba1 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -103,7 +103,7 @@ var ( func TestDecodeEmptyInput(t *testing.T) { input := []byte{} - _, err := DecodeTransaction(rlp.NewStream(bytes.NewReader(input), 0)) + _, err := DecodeTransaction(input) if !errors.Is(err, io.EOF) { t.Fatal("wrong error:", err) } @@ -111,7 +111,7 @@ func TestDecodeEmptyInput(t *testing.T) { func TestDecodeEmptyTypedTx(t *testing.T) { input := []byte{0x80} - _, err := DecodeTransaction(rlp.NewStream(bytes.NewReader(input), 0)) + _, err := DecodeTransaction(input) if !errors.Is(err, rlp.EOL) { t.Fatal("wrong error:", err) } @@ -267,7 +267,7 @@ func TestEIP1559TransactionEncode(t *testing.T) { if !bytes.Equal(have, want) { t.Errorf("encoded RLP mismatch, got %x", have) } - _, err := DecodeTransaction(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)) + _, err := DecodeTransaction(buf.Bytes()) if err != nil { t.Fatalf("decode error: %v", err) } @@ -276,7 +276,7 @@ func TestEIP1559TransactionEncode(t *testing.T) { } func decodeTx(data []byte) (Transaction, error) { - return DecodeTransaction(rlp.NewStream(bytes.NewReader(data), 0)) + return DecodeTransaction(data) } func defaultTestKey() (*ecdsa.PrivateKey, libcommon.Address) { diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index 909ccac71..464c54b79 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -242,7 +242,7 @@ func (tp *TransactionsPacket) DecodeRLP(s *rlp.Stream) error { return err } var tx types.Transaction - for tx, err = types.DecodeTransaction(s); err == nil; tx, err = types.DecodeTransaction(s) { + for tx, err = types.DecodeRLPTransaction(s); err == nil; tx, err = types.DecodeRLPTransaction(s) { *tp = append(*tp, tx) } if !errors.Is(err, rlp.EOL) { @@ -557,7 +557,7 @@ func (ptp *PooledTransactionsPacket) DecodeRLP(s *rlp.Stream) error { return err } var tx types.Transaction - for tx, err = types.DecodeTransaction(s); err == nil; tx, err = types.DecodeTransaction(s) { + for tx, err = types.DecodeRLPTransaction(s); err == nil; tx, err = types.DecodeRLPTransaction(s) { *ptp = append(*ptp, tx) } if !errors.Is(err, rlp.EOL) { @@ -654,7 +654,7 @@ func (ptp66 *PooledTransactionsPacket66) DecodeRLP(s *rlp.Stream) error { return err } var tx types.Transaction - for tx, err = types.DecodeTransaction(s); err == nil; tx, err = types.DecodeTransaction(s) { + for tx, err = types.DecodeRLPTransaction(s); err == nil; tx, err = types.DecodeRLPTransaction(s) { ptp66.PooledTransactionsPacket = append(ptp66.PooledTransactionsPacket, tx) } if !errors.Is(err, rlp.EOL) { diff --git a/eth/protocols/eth/protocol_test.go b/eth/protocols/eth/protocol_test.go index f27ec322c..747665335 100644 --- a/eth/protocols/eth/protocol_test.go +++ b/eth/protocols/eth/protocol_test.go @@ -153,7 +153,7 @@ func TestEth66Messages(t *testing.T) { } { var tx types.Transaction rlpdata := common.FromHex(hexrlp) - tx, err1 := types.DecodeTransaction(rlp.NewStream(bytes.NewReader(rlpdata), 0)) + tx, err1 := types.DecodeTransaction(rlpdata) if err1 != nil { t.Fatal(err1) } diff --git a/eth/stagedsync/stage_mining_exec.go b/eth/stagedsync/stage_mining_exec.go index 337b3d96f..49a9009c1 100644 --- a/eth/stagedsync/stage_mining_exec.go +++ b/eth/stagedsync/stage_mining_exec.go @@ -1,7 +1,6 @@ package stagedsync import ( - "bytes" "errors" "fmt" "io" @@ -22,7 +21,6 @@ import ( types2 "github.com/ledgerwatch/erigon-lib/types" "github.com/ledgerwatch/erigon/core/types/accounts" - "github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/turbo/services" @@ -219,13 +217,8 @@ func getNextTransactions( } var txs []types.Transaction //nolint:prealloc - reader := bytes.NewReader([]byte{}) - stream := new(rlp.Stream) for i := range txSlots.Txs { - reader.Reset(txSlots.Txs[i]) - stream.Reset(reader, uint64(len(txSlots.Txs[i]))) - - transaction, err := types.DecodeTransaction(stream) + transaction, err := types.DecodeWrappedTransaction(txSlots.Txs[i]) if err == io.EOF { continue } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 50dfa0157..4e4f707ff 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -17,7 +17,6 @@ package tracetest import ( - "bytes" "encoding/json" "math/big" "os" @@ -42,7 +41,6 @@ import ( "github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/eth/tracers" "github.com/ledgerwatch/erigon/params" - "github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/tests" // Force-load native and js packages, to trigger registration @@ -234,7 +232,7 @@ func BenchmarkTracers(b *testing.B) { func benchTracer(b *testing.B, tracerName string, test *callTracerTest) { // Configure a blockchain with the given prestate - tx, err := types.DecodeTransaction(rlp.NewStream(bytes.NewReader(common.FromHex(test.Input)), 0)) + tx, err := types.DecodeTransaction(common.FromHex(test.Input)) if err != nil { b.Fatalf("failed to parse testcase input: %v", err) } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index f2d063650..6551c43d7 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -17,7 +17,6 @@ package tests import ( - "bytes" "errors" "fmt" "math/big" @@ -31,7 +30,6 @@ import ( "github.com/ledgerwatch/erigon/common/math" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/types" - "github.com/ledgerwatch/erigon/rlp" ) // TransactionTest checks RLP decoding and sender derivation of transactions. @@ -62,7 +60,7 @@ type ttFork struct { func (tt *TransactionTest) Run(chainID *big.Int) error { validateTx := func(rlpData hexutility.Bytes, signer types.Signer, rules *chain.Rules) (*libcommon.Address, *libcommon.Hash, uint64, error) { - tx, err := types.DecodeTransaction(rlp.NewStream(bytes.NewReader(rlpData), 0)) + tx, err := types.DecodeTransaction(rlpData) if err != nil { return nil, nil, 0, err } diff --git a/turbo/rpchelper/filters.go b/turbo/rpchelper/filters.go index e330bbeeb..690010902 100644 --- a/turbo/rpchelper/filters.go +++ b/turbo/rpchelper/filters.go @@ -526,8 +526,7 @@ func (ff *Filters) OnNewTx(reply *txpool.OnAddReply) { if len(rlpTx) == 0 { continue } - s := rlp.NewStream(bytes.NewReader(rlpTx), uint64(len(rlpTx))) - txs[i], decodeErr = types.DecodeTransaction(s) + txs[i], decodeErr = types.DecodeWrappedTransaction(rlpTx) if decodeErr != nil { // ignoring what we can't unmarshal log.Warn("OnNewTx rpc filters, unprocessable payload", "err", decodeErr, "data", hex.EncodeToString(rlpTx)) diff --git a/turbo/snapshotsync/block_reader.go b/turbo/snapshotsync/block_reader.go index ccd820952..0a5ac5bb3 100644 --- a/turbo/snapshotsync/block_reader.go +++ b/turbo/snapshotsync/block_reader.go @@ -528,8 +528,6 @@ func (back *BlockReaderWithSnapshots) txsFromSnapshot(baseTxnID uint64, txsAmoun txnOffset := txsSeg.IdxTxnHash.OrdinalLookup(baseTxnID - txsSeg.IdxTxnHash.BaseDataID()) gg := txsSeg.Seg.MakeGetter() gg.Reset(txnOffset) - reader := bytes.NewReader(buf) - stream := rlp.NewStream(reader, 0) for i := uint32(0); i < txsAmount; i++ { if !gg.HasNext() { return nil, nil, nil @@ -540,9 +538,7 @@ func (back *BlockReaderWithSnapshots) txsFromSnapshot(baseTxnID uint64, txsAmoun } senders[i].SetBytes(buf[1 : 1+20]) txRlp := buf[1+20:] - reader.Reset(txRlp) - stream.Reset(reader, 0) - txs[i], err = types.DecodeTransaction(stream) + txs[i], err = types.DecodeTransaction(txRlp) if err != nil { return nil, nil, err } @@ -562,7 +558,7 @@ func (back *BlockReaderWithSnapshots) txnByID(txnID uint64, sn *TxnSegment, buf buf, _ = gg.Next(buf[:0]) sender, txnRlp := buf[1:1+20], buf[1+20:] - txn, err = types.DecodeTransaction(rlp.NewStream(bytes.NewReader(txnRlp), uint64(len(txnRlp)))) + txn, err = types.DecodeTransaction(txnRlp) if err != nil { return } @@ -590,7 +586,7 @@ func (back *BlockReaderWithSnapshots) txnByHash(txnHash libcommon.Hash, segments senderByte, txnRlp := buf[1:1+20], buf[1+20:] sender := *(*libcommon.Address)(senderByte) - txn, err = types.DecodeTransaction(rlp.NewStream(bytes.NewReader(txnRlp), uint64(len(txnRlp)))) + txn, err = types.DecodeTransaction(txnRlp) if err != nil { return }