From 2c8003c6ef93df8a94464733654da23885cd7c8c Mon Sep 17 00:00:00 2001 From: racytech <82003208+racytech@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:26:23 +0600 Subject: [PATCH] RLP: remove allocations, replace buff with pooled buff (#12869) Preparation for RLP gen package. For now replacing stack allocations with pooled buff, and removing other allocations in RLP encoding logic --- core/types/access_list_tx.go | 32 ++--- core/types/authorization.go | 18 ++- core/types/blob_tx.go | 28 +++-- core/types/block.go | 39 +++--- core/types/dynamic_fee_tx.go | 20 +-- core/types/encdec_test.go | 228 ++++++++++++++++++++++++++++++----- core/types/legacy_tx.go | 18 +-- core/types/set_code_tx.go | 20 +-- core/types/withdrawal.go | 20 ++- erigon-lib/rlp/encode.go | 31 ++++- 10 files changed, 340 insertions(+), 114 deletions(-) diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index eeadb2949e6..e592f1ba4e2 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -168,26 +168,26 @@ func accessListSize(al AccessList) int { } func encodeAccessList(al AccessList, w io.Writer, b []byte) error { - for _, tuple := range al { + for i := 0; i < len(al); i++ { tupleLen := 21 // Each storage key takes 33 bytes - storageLen := 33 * len(tuple.StorageKeys) + storageLen := 33 * len(al[i].StorageKeys) tupleLen += rlp2.ListPrefixLen(storageLen) + storageLen if err := EncodeStructSizePrefix(tupleLen, w, b); err != nil { return err } - if err := rlp.EncodeOptionalAddress(&tuple.Address, w, b); err != nil { + if err := rlp.EncodeOptionalAddress(&al[i].Address, w, b); err != nil { // TODO(racytech): change addr to []byte? return err } if err := EncodeStructSizePrefix(storageLen, w, b); err != nil { return err } b[0] = 128 + 32 - for _, storageKey := range tuple.StorageKeys { + for idx := 0; idx < len(al[i].StorageKeys); idx++ { if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(storageKey.Bytes()); err != nil { + if _, err := w.Write(al[i].StorageKeys[idx][:]); err != nil { return err } } @@ -195,7 +195,7 @@ func encodeAccessList(al AccessList, w io.Writer, b []byte) error { return nil } -func EncodeStructSizePrefix(size int, w io.Writer, b []byte) error { +func EncodeStructSizePrefix(size int, w io.Writer, b []byte) error { // TODO(racytech): move it to rlp package? if size >= 56 { beSize := libcommon.BitLenToByteLen(bits.Len(uint(size))) binary.BigEndian.PutUint64(b[1:], uint64(size)) @@ -217,7 +217,8 @@ func EncodeStructSizePrefix(size int, w io.Writer, b []byte) error { // transactions, it returns the type and payload. func (tx *AccessListTx) MarshalBinary(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen := tx.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // encode TxType b[0] = AccessListTxType if _, err := w.Write(b[:1]); err != nil { @@ -235,7 +236,7 @@ func (tx *AccessListTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceL return err } // encode ChainID - if err := tx.ChainID.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.ChainID, w, b); err != nil { return err } // encode Nonce @@ -243,7 +244,7 @@ func (tx *AccessListTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceL return err } // encode GasPrice - if err := tx.GasPrice.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.GasPrice, w, b); err != nil { return err } // encode Gas @@ -260,12 +261,12 @@ func (tx *AccessListTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceL return err } if tx.To != nil { - if _, err := w.Write(tx.To.Bytes()); err != nil { + if _, err := w.Write(tx.To[:]); err != nil { return err } } // encode Value - if err := tx.Value.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.Value, w, b); err != nil { return err } // encode Data @@ -281,15 +282,15 @@ func (tx *AccessListTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceL return err } // encode V - if err := tx.V.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.V, w, b); err != nil { return err } // encode R - if err := tx.R.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.R, w, b); err != nil { return err } // encode S - if err := tx.S.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.S, w, b); err != nil { return err } return nil @@ -301,7 +302,8 @@ func (tx *AccessListTx) EncodeRLP(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen := tx.payloadSize() // size of struct prefix and TxType envelopeSize := 1 + rlp2.ListPrefixLen(payloadSize) + payloadSize - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // envelope if err := rlp.EncodeStringSizePrefix(envelopeSize, w, b[:]); err != nil { return err diff --git a/core/types/authorization.go b/core/types/authorization.go index 35a39e16d68..636c058c6d0 100644 --- a/core/types/authorization.go +++ b/core/types/authorization.go @@ -182,36 +182,34 @@ func decodeAuthorizations(auths *[]Authorization, s *rlp.Stream) error { } func encodeAuthorizations(authorizations []Authorization, w io.Writer, b []byte) error { - for _, auth := range authorizations { - // 0. encode length of individual Authorization - authLen := authorizationSize(auth) + for i := 0; i < len(authorizations); i++ { + authLen := authorizationSize(authorizations[i]) if err := EncodeStructSizePrefix(authLen, w, b); err != nil { return err } // 1. encode ChainId - if err := rlp.EncodeInt(auth.ChainID, w, b); err != nil { + if err := rlp.EncodeInt(authorizations[i].ChainID, w, b); err != nil { return err } // 2. encode Address - if err := rlp.EncodeOptionalAddress(&auth.Address, w, b); err != nil { + if err := rlp.EncodeOptionalAddress(&authorizations[i].Address, w, b); err != nil { return err } // 3. encode Nonce - if err := rlp.EncodeInt(auth.Nonce, w, b); err != nil { + if err := rlp.EncodeInt(authorizations[i].Nonce, w, b); err != nil { return err } // 4. encode YParity, R, S - if err := rlp.EncodeInt(uint64(auth.YParity), w, b); err != nil { + if err := rlp.EncodeInt(uint64(authorizations[i].YParity), w, b); err != nil { return err } - if err := auth.R.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&authorizations[i].R, w, b); err != nil { return err } - if err := auth.S.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&authorizations[i].S, w, b); err != nil { return err } } - return nil } diff --git a/core/types/blob_tx.go b/core/types/blob_tx.go index 9ac4228cafd..34036643df2 100644 --- a/core/types/blob_tx.go +++ b/core/types/blob_tx.go @@ -178,8 +178,8 @@ func blobVersionedHashesSize(hashes []libcommon.Hash) int { } func encodeBlobVersionedHashes(hashes []libcommon.Hash, w io.Writer, b []byte) error { - for _, h := range hashes { - if err := rlp.EncodeString(h[:], w, b); err != nil { + for i := 0; i < len(hashes); i++ { + if err := rlp.EncodeString(hashes[i][:], w, b); err != nil { return err } } @@ -192,7 +192,7 @@ func (stx *BlobTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, g return err } // encode ChainID - if err := stx.ChainID.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(stx.ChainID, w, b); err != nil { return err } // encode Nonce @@ -200,11 +200,11 @@ func (stx *BlobTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, g return err } // encode MaxPriorityFeePerGas - if err := stx.Tip.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(stx.Tip, w, b); err != nil { return err } // encode MaxFeePerGas - if err := stx.FeeCap.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(stx.FeeCap, w, b); err != nil { return err } // encode Gas @@ -216,11 +216,11 @@ func (stx *BlobTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, g if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(stx.To.Bytes()); err != nil { + if _, err := w.Write(stx.To[:]); err != nil { return err } // encode Value - if err := stx.Value.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(stx.Value, w, b); err != nil { return err } // encode Data @@ -236,7 +236,7 @@ func (stx *BlobTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, g return err } // encode MaxFeePerBlobGas - if err := stx.MaxFeePerBlobGas.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(stx.MaxFeePerBlobGas, w, b); err != nil { return err } // prefix @@ -248,15 +248,15 @@ func (stx *BlobTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, g return err } // encode V - if err := stx.V.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&stx.V, w, b); err != nil { return err } // encode R - if err := stx.R.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&stx.R, w, b); err != nil { return err } // encode S - if err := stx.S.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&stx.S, w, b); err != nil { return err } return nil @@ -266,7 +266,8 @@ func (stx *BlobTx) EncodeRLP(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen, blobHashesLen := stx.payloadSize() // size of struct prefix and TxType envelopeSize := 1 + rlp2.ListPrefixLen(payloadSize) + payloadSize - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // envelope if err := rlp.EncodeStringSizePrefix(envelopeSize, w, b[:]); err != nil { return err @@ -284,7 +285,8 @@ func (stx *BlobTx) EncodeRLP(w io.Writer) error { func (stx *BlobTx) MarshalBinary(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen, blobHashesLen := stx.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // encode TxType b[0] = BlobTxType if _, err := w.Write(b[:1]); err != nil { diff --git a/core/types/block.go b/core/types/block.go index 9c5d857f70b..3e6cf56c7e8 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -200,7 +200,8 @@ func (h *Header) EncodingSize() int { func (h *Header) EncodeRLP(w io.Writer) error { encodingSize := h.EncodingSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // Prefix if err := EncodeStructSizePrefix(encodingSize, w, b[:]); err != nil { return err @@ -209,39 +210,39 @@ func (h *Header) EncodeRLP(w io.Writer) error { if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.ParentHash.Bytes()); err != nil { + if _, err := w.Write(h.ParentHash[:]); err != nil { return err } if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.UncleHash.Bytes()); err != nil { + if _, err := w.Write(h.UncleHash[:]); err != nil { return err } b[0] = 128 + 20 if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.Coinbase.Bytes()); err != nil { + if _, err := w.Write(h.Coinbase[:]); err != nil { return err } b[0] = 128 + 32 if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.Root.Bytes()); err != nil { + if _, err := w.Write(h.Root[:]); err != nil { return err } if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.TxHash.Bytes()); err != nil { + if _, err := w.Write(h.TxHash[:]); err != nil { return err } if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.ReceiptHash.Bytes()); err != nil { + if _, err := w.Write(h.ReceiptHash[:]); err != nil { return err } b[0] = 183 + 2 @@ -250,7 +251,7 @@ func (h *Header) EncodeRLP(w io.Writer) error { if _, err := w.Write(b[:3]); err != nil { return err } - if _, err := w.Write(h.Bloom.Bytes()); err != nil { + if _, err := w.Write(h.Bloom[:]); err != nil { return err } if err := rlp.EncodeBigInt(h.Difficulty, w, b[:]); err != nil { @@ -284,7 +285,7 @@ func (h *Header) EncodeRLP(w io.Writer) error { if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.MixDigest.Bytes()); err != nil { + if _, err := w.Write(h.MixDigest[:]); err != nil { return err } b[0] = 128 + 8 @@ -307,7 +308,7 @@ func (h *Header) EncodeRLP(w io.Writer) error { if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.WithdrawalsHash.Bytes()); err != nil { + if _, err := w.Write(h.WithdrawalsHash[:]); err != nil { return err } } @@ -328,7 +329,7 @@ func (h *Header) EncodeRLP(w io.Writer) error { if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.ParentBeaconBlockRoot.Bytes()); err != nil { + if _, err := w.Write(h.ParentBeaconBlockRoot[:]); err != nil { return err } } @@ -338,7 +339,7 @@ func (h *Header) EncodeRLP(w io.Writer) error { if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(h.RequestsHash.Bytes()); err != nil { + if _, err := w.Write(h.RequestsHash[:]); err != nil { return err } } @@ -788,7 +789,8 @@ func (rb RawBody) payloadSize() (payloadSize, txsLen, unclesLen, withdrawalsLen func (rb RawBody) EncodeRLP(w io.Writer) error { payloadSize, txsLen, unclesLen, withdrawalsLen := rb.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // prefix if err := EncodeStructSizePrefix(payloadSize, w, b[:]); err != nil { return err @@ -873,7 +875,8 @@ func (bfs BodyForStorage) payloadSize() (payloadSize, unclesLen, withdrawalsLen func (bfs BodyForStorage) EncodeRLP(w io.Writer) error { payloadSize, unclesLen, withdrawalsLen := bfs.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // prefix if err := EncodeStructSizePrefix(payloadSize, w, b[:]); err != nil { @@ -956,7 +959,9 @@ func (bb Body) payloadSize() (payloadSize int, txsLen, unclesLen, withdrawalsLen func (bb Body) EncodeRLP(w io.Writer) error { payloadSize, txsLen, unclesLen, withdrawalsLen := bb.payloadSize() - var b [33]byte + + b := newEncodingBuf() + defer pooledBuf.Put(b) // prefix if err := EncodeStructSizePrefix(payloadSize, w, b[:]); err != nil { return err @@ -1201,7 +1206,9 @@ func (bb *Block) EncodingSize() int { // EncodeRLP serializes b into the Ethereum RLP block format. func (bb *Block) EncodeRLP(w io.Writer) error { payloadSize, txsLen, unclesLen, withdrawalsLen := bb.payloadSize() - var b [33]byte + + b := newEncodingBuf() + defer pooledBuf.Put(b) // prefix if err := EncodeStructSizePrefix(payloadSize, w, b[:]); err != nil { return err diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index d9a6ed7a54a..ed2206406c7 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -172,7 +172,8 @@ func (tx *DynamicFeeTransaction) WithSignature(signer Signer, sig []byte) (Trans // transactions, it returns the type and payload. func (tx *DynamicFeeTransaction) MarshalBinary(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen := tx.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // encode TxType b[0] = DynamicFeeTxType if _, err := w.Write(b[:1]); err != nil { @@ -190,7 +191,7 @@ func (tx *DynamicFeeTransaction) encodePayload(w io.Writer, b []byte, payloadSiz return err } // encode ChainID - if err := tx.ChainID.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.ChainID, w, b); err != nil { return err } // encode Nonce @@ -198,11 +199,11 @@ func (tx *DynamicFeeTransaction) encodePayload(w io.Writer, b []byte, payloadSiz return err } // encode MaxPriorityFeePerGas - if err := tx.Tip.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.Tip, w, b); err != nil { return err } // encode MaxFeePerGas - if err := tx.FeeCap.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.FeeCap, w, b); err != nil { return err } // encode Gas @@ -214,7 +215,7 @@ func (tx *DynamicFeeTransaction) encodePayload(w io.Writer, b []byte, payloadSiz return err } // encode Value - if err := tx.Value.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.Value, w, b); err != nil { return err } // encode Data @@ -230,15 +231,15 @@ func (tx *DynamicFeeTransaction) encodePayload(w io.Writer, b []byte, payloadSiz return err } // encode V - if err := tx.V.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.V, w, b); err != nil { return err } // encode R - if err := tx.R.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.R, w, b); err != nil { return err } // encode S - if err := tx.S.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.S, w, b); err != nil { return err } return nil @@ -248,7 +249,8 @@ func (tx *DynamicFeeTransaction) EncodeRLP(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen := tx.payloadSize() // size of struct prefix and TxType envelopeSize := 1 + rlp2.ListPrefixLen(payloadSize) + payloadSize - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // envelope if err := rlp.EncodeStringSizePrefix(envelopeSize, w, b[:]); err != nil { return err diff --git a/core/types/encdec_test.go b/core/types/encdec_test.go index e25c976068e..12230b69bea 100644 --- a/core/types/encdec_test.go +++ b/core/types/encdec_test.go @@ -29,9 +29,10 @@ import ( libcommon "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" ) -const RUNS = 100 // for local tests increase this number +const RUNS = 10000 // for local tests increase this number type TRand struct { rnd *rand.Rand @@ -52,6 +53,11 @@ func (tr *TRand) RandUint64() *uint64 { return &a } +func (tr *TRand) RandUint256() *uint256.Int { + a := new(uint256.Int).SetBytes(tr.RandBytes(tr.RandIntInRange(1, 32))) + return a +} + func (tr *TRand) RandBig() *big.Int { return big.NewInt(int64(tr.rnd.Int())) } @@ -140,15 +146,20 @@ func (tr *TRand) RandAuthorizations(size int) []Authorization { Address: tr.RandAddress(), Nonce: *tr.RandUint64(), YParity: uint8(*tr.RandUint64()), - R: *uint256.NewInt(*tr.RandUint64()), - S: *uint256.NewInt(*tr.RandUint64()), + R: *tr.RandUint256(), + S: *tr.RandUint256(), } } return auths } -func (tr *TRand) RandTransaction() Transaction { - txType := tr.RandIntInRange(0, 5) // LegacyTxType, AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType +func (tr *TRand) RandTransaction(_type int) Transaction { + var txType int + if _type == -1 { + txType = tr.RandIntInRange(0, 5) // LegacyTxType, AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType + } else { + txType = _type + } to := tr.RandAddress() commonTx := CommonTx{ Nonce: *tr.RandUint64(), @@ -156,9 +167,9 @@ func (tr *TRand) RandTransaction() Transaction { To: &to, Value: uint256.NewInt(*tr.RandUint64()), // wei amount Data: tr.RandBytes(tr.RandIntInRange(128, 1024)), - V: *uint256.NewInt(*tr.RandUint64()), - R: *uint256.NewInt(*tr.RandUint64()), - S: *uint256.NewInt(*tr.RandUint64()), + V: *tr.RandUint256(), + R: *tr.RandUint256(), + S: *tr.RandUint256(), } switch txType { case LegacyTxType: @@ -224,7 +235,7 @@ func (tr *TRand) RandHashes(size int) []libcommon.Hash { func (tr *TRand) RandTransactions(size int) []Transaction { txns := make([]Transaction, size) for i := 0; i < size; i++ { - txns[i] = tr.RandTransaction() + txns[i] = tr.RandTransaction(-1) } return txns } @@ -232,7 +243,18 @@ func (tr *TRand) RandTransactions(size int) []Transaction { func (tr *TRand) RandRawTransactions(size int) [][]byte { txns := make([][]byte, size) for i := 0; i < size; i++ { - txns[i] = tr.RandBytes(tr.RandIntInRange(1, 1023)) + txns[i] = tr.RandBytes(tr.RandIntInRange(1, 512)) + } + return txns +} + +func (tr *TRand) RandRLPTransactions(size int) [][]byte { + txns := make([][]byte, size) + for i := 0; i < size; i++ { + txn := make([]byte, 512) + txSize := tr.RandIntInRange(1, 500) + encodedSize := rlp2.EncodeString(tr.RandBytes(txSize), txn) + txns[i] = txn[:encodedSize] } return txns } @@ -255,7 +277,7 @@ func (tr *TRand) RandWithdrawals(size int) []*Withdrawal { func (tr *TRand) RandRawBody() *RawBody { return &RawBody{ - Transactions: tr.RandRawTransactions(tr.RandIntInRange(1, 6)), + Transactions: tr.RandRLPTransactions(tr.RandIntInRange(1, 6)), Uncles: tr.RandHeaders(tr.RandIntInRange(1, 6)), Withdrawals: tr.RandWithdrawals(tr.RandIntInRange(1, 6)), } @@ -413,29 +435,71 @@ func compareBodies(t *testing.T, a, b *Body) error { return nil } -// func TestRawBodyEncodeDecodeRLP(t *testing.T) { -// tr := NewTRand() -// var buf bytes.Buffer -// for i := 0; i < RUNS; i++ { -// enc := tr.RandRawBody() -// buf.Reset() -// if err := enc.EncodeRLP(&buf); err != nil { -// t.Errorf("error: RawBody.EncodeRLP(): %v", err) -// } +func TestTransactionEncodeDecodeRLP(t *testing.T) { + tr := NewTRand() + var buf bytes.Buffer + for i := 0; i < RUNS; i++ { + enc := tr.RandTransaction(-1) + buf.Reset() + if err := enc.EncodeRLP(&buf); err != nil { + t.Errorf("error: RawBody.EncodeRLP(): %v", err) + } -// s := rlp.NewStream(bytes.NewReader(buf.Bytes()), 0) + s := rlp.NewStream(bytes.NewReader(buf.Bytes()), 0) -// dec := &RawBody{} -// if err := dec.DecodeRLP(s); err != nil { -// t.Errorf("error: RawBody.DecodeRLP(): %v", err) -// panic(err) -// } + dec, err := DecodeRLPTransaction(s, false) + if err != nil { + t.Errorf("error: DecodeRLPTransaction: %v", err) + } + compareTransactions(t, enc, dec) + } +} + +func TestHeaderEncodeDecodeRLP(t *testing.T) { + tr := NewTRand() + var buf bytes.Buffer + for i := 0; i < RUNS; i++ { + enc := tr.RandHeader() + buf.Reset() + if err := enc.EncodeRLP(&buf); err != nil { + t.Errorf("error: Header.EncodeRLP(): %v", err) + } -// if err := compareRawBodies(t, enc, dec); err != nil { -// t.Errorf("error: compareRawBodies: %v", err) -// } -// } -// } + s := rlp.NewStream(bytes.NewReader(buf.Bytes()), 0) + + dec := &Header{} + if err := dec.DecodeRLP(s); err != nil { + t.Errorf("error: Header.DecodeRLP(): %v", err) + panic(err) + } + + checkHeaders(t, enc, dec) + } +} + +func TestRawBodyEncodeDecodeRLP(t *testing.T) { + tr := NewTRand() + var buf bytes.Buffer + for i := 0; i < RUNS; i++ { + enc := tr.RandRawBody() + buf.Reset() + if err := enc.EncodeRLP(&buf); err != nil { + t.Errorf("error: RawBody.EncodeRLP(): %v", err) + } + + s := rlp.NewStream(bytes.NewReader(buf.Bytes()), 0) + + dec := &RawBody{} + if err := dec.DecodeRLP(s); err != nil { + t.Errorf("error: RawBody.DecodeRLP(): %v", err) + panic(err) + } + + if err := compareRawBodies(t, enc, dec); err != nil { + t.Errorf("error: compareRawBodies: %v", err) + } + } +} func TestBodyEncodeDecodeRLP(t *testing.T) { tr := NewTRand() @@ -459,3 +523,105 @@ func TestBodyEncodeDecodeRLP(t *testing.T) { } } } + +func TestWithdrawalEncodeDecodeRLP(t *testing.T) { + tr := NewTRand() + var buf bytes.Buffer + for i := 0; i < RUNS; i++ { + enc := tr.RandWithdrawal() + buf.Reset() + if err := enc.EncodeRLP(&buf); err != nil { + t.Errorf("error: RawBody.EncodeRLP(): %v", err) + } + + s := rlp.NewStream(bytes.NewReader(buf.Bytes()), 0) + dec := &Withdrawal{} + if err := dec.DecodeRLP(s); err != nil { + t.Errorf("error: RawBody.DecodeRLP(): %v", err) + panic(err) + } + + checkWithdrawals(t, enc, dec) + } +} + +/* + Benchmarks +*/ + +func BenchmarkHeaderRLP(b *testing.B) { + tr := NewTRand() + header := tr.RandHeader() + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + header.EncodeRLP(&buf) + } +} + +func BenchmarkLegacyTxRLP(b *testing.B) { + tr := NewTRand() + txn := tr.RandTransaction(LegacyTxType) + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + txn.EncodeRLP(&buf) + } +} + +func BenchmarkAccessListTxRLP(b *testing.B) { + tr := NewTRand() + txn := tr.RandTransaction(AccessListTxType) + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + txn.EncodeRLP(&buf) + } +} + +func BenchmarkDynamicFeeTxRLP(b *testing.B) { + tr := NewTRand() + txn := tr.RandTransaction(DynamicFeeTxType) + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + txn.EncodeRLP(&buf) + } +} + +func BenchmarkBlobTxRLP(b *testing.B) { + tr := NewTRand() + txn := tr.RandTransaction(BlobTxType) + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + txn.EncodeRLP(&buf) + } +} + +func BenchmarkSetCodeTxRLP(b *testing.B) { + tr := NewTRand() + txn := tr.RandTransaction(SetCodeTxType) + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + txn.EncodeRLP(&buf) + } +} + +func BenchmarkWithdrawalRLP(b *testing.B) { + tr := NewTRand() + w := tr.RandWithdrawal() + var buf bytes.Buffer + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + w.EncodeRLP(&buf) + } +} diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 9696f97a4fb..634443806ec 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -219,7 +219,8 @@ func (tx *LegacyTx) payloadSize() (payloadSize int, nonceLen, gasLen int) { func (tx *LegacyTx) MarshalBinary(w io.Writer) error { payloadSize, nonceLen, gasLen := tx.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) if err := tx.encodePayload(w, b[:], payloadSize, nonceLen, gasLen); err != nil { return err } @@ -243,7 +244,7 @@ func (tx *LegacyTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, return err } } - if err := tx.GasPrice.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.GasPrice, w, b); err != nil { return err } if err := rlp.EncodeInt(tx.Gas, w, b); err != nil { @@ -258,23 +259,23 @@ func (tx *LegacyTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, return err } if tx.To != nil { - if _, err := w.Write(tx.To.Bytes()); err != nil { + if _, err := w.Write(tx.To[:]); err != nil { return err } } - if err := tx.Value.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.Value, w, b); err != nil { return err } if err := rlp.EncodeString(tx.Data, w, b); err != nil { return err } - if err := tx.V.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.V, w, b); err != nil { return err } - if err := tx.R.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.R, w, b); err != nil { return err } - if err := tx.S.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.S, w, b); err != nil { return err } return nil @@ -283,7 +284,8 @@ func (tx *LegacyTx) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, func (tx *LegacyTx) EncodeRLP(w io.Writer) error { payloadSize, nonceLen, gasLen := tx.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) if err := tx.encodePayload(w, b[:], payloadSize, nonceLen, gasLen); err != nil { return err } diff --git a/core/types/set_code_tx.go b/core/types/set_code_tx.go index be12f8aa6ef..b173de582f4 100644 --- a/core/types/set_code_tx.go +++ b/core/types/set_code_tx.go @@ -99,7 +99,8 @@ func (tx *SetCodeTransaction) WithSignature(signer Signer, sig []byte) (Transact func (tx *SetCodeTransaction) MarshalBinary(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen, authorizationsLen := tx.payloadSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // encode TxType b[0] = SetCodeTxType if _, err := w.Write(b[:1]); err != nil { @@ -203,7 +204,8 @@ func (tx *SetCodeTransaction) SigningHash(chainID *big.Int) libcommon.Hash { func (tx *SetCodeTransaction) EncodeRLP(w io.Writer) error { payloadSize, nonceLen, gasLen, accessListLen, authorizationsLen := tx.payloadSize() envelopSize := 1 + rlp2.ListPrefixLen(payloadSize) + payloadSize - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) // encode envelope size if err := rlp.EncodeStringSizePrefix(envelopSize, w, b[:]); err != nil { return err @@ -290,7 +292,7 @@ func (tx *SetCodeTransaction) encodePayload(w io.Writer, b []byte, payloadSize, return err } // encode ChainID - if err := tx.ChainID.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.ChainID, w, b); err != nil { return err } // encode Nonce @@ -298,11 +300,11 @@ func (tx *SetCodeTransaction) encodePayload(w io.Writer, b []byte, payloadSize, return err } // encode MaxPriorityFeePerGas - if err := tx.Tip.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.Tip, w, b); err != nil { return err } // encode MaxFeePerGas - if err := tx.FeeCap.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.FeeCap, w, b); err != nil { return err } // encode Gas @@ -314,7 +316,7 @@ func (tx *SetCodeTransaction) encodePayload(w io.Writer, b []byte, payloadSize, return err } // encode Value - if err := tx.Value.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(tx.Value, w, b); err != nil { return err } // encode Data @@ -338,15 +340,15 @@ func (tx *SetCodeTransaction) encodePayload(w io.Writer, b []byte, payloadSize, return err } // encode V - if err := tx.V.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.V, w, b); err != nil { return err } // encode R - if err := tx.R.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.R, w, b); err != nil { return err } // encode S - if err := tx.S.EncodeRLP(w); err != nil { + if err := rlp.EncodeUint256(&tx.S, w, b); err != nil { return err } return nil diff --git a/core/types/withdrawal.go b/core/types/withdrawal.go index 1dc0540d73a..fa2487b95df 100644 --- a/core/types/withdrawal.go +++ b/core/types/withdrawal.go @@ -23,6 +23,7 @@ import ( "bytes" "fmt" "io" + "sync" "github.com/erigontech/erigon-lib/common/hexutil" @@ -31,6 +32,18 @@ import ( "github.com/erigontech/erigon-lib/types/clonable" ) +type encodingBuf [32]byte + +var pooledBuf = sync.Pool{ + New: func() interface{} { return new(encodingBuf) }, +} + +func newEncodingBuf() *encodingBuf { + b := pooledBuf.Get().(*encodingBuf) + *b = encodingBuf([32]byte{}) // reset, do we need to? + return b +} + //go:generate gencodec -type Withdrawal -field-override withdrawalMarshaling -out gen_withdrawal_json.go // Withdrawal represents a validator withdrawal from the consensus layer. @@ -54,9 +67,12 @@ func (obj *Withdrawal) EncodingSize() int { } func (obj *Withdrawal) EncodeRLP(w io.Writer) error { + encodingSize := obj.EncodingSize() - var b [33]byte + b := newEncodingBuf() + defer pooledBuf.Put(b) + if err := EncodeStructSizePrefix(encodingSize, w, b[:]); err != nil { return err } @@ -72,7 +88,7 @@ func (obj *Withdrawal) EncodeRLP(w io.Writer) error { if _, err := w.Write(b[:1]); err != nil { return err } - if _, err := w.Write(obj.Address.Bytes()); err != nil { + if _, err := w.Write(obj.Address[:]); err != nil { return err } diff --git a/erigon-lib/rlp/encode.go b/erigon-lib/rlp/encode.go index 75c14a9913b..43a7ac9468b 100644 --- a/erigon-lib/rlp/encode.go +++ b/erigon-lib/rlp/encode.go @@ -616,6 +616,35 @@ func EncodeBigInt(i *big.Int, w io.Writer, buffer []byte) error { return err } +func EncodeUint256(i *uint256.Int, w io.Writer, buffer []byte) error { + buffer[0] = 0x80 + if i == nil { + _, err := w.Write(buffer[:1]) + return err + } + nBits := i.BitLen() + if nBits == 0 { + _, err := w.Write(buffer[:1]) + return err + } + buffer[0] = byte(i[0]) + if nBits <= 7 { + _, err := w.Write(buffer[:1]) + return err + } + nBytes := byte(libcommon.BitLenToByteLen(nBits)) + buffer[0] = 0x80 + nBytes + if _, err := w.Write(buffer[:1]); err != nil { + return err + } + binary.BigEndian.PutUint64(buffer[0:8], i[3]) + binary.BigEndian.PutUint64(buffer[8:16], i[2]) + binary.BigEndian.PutUint64(buffer[16:24], i[1]) + binary.BigEndian.PutUint64(buffer[24:32], i[0]) + _, err := w.Write(buffer[32-nBytes:]) + return err +} + func EncodeString(s []byte, w io.Writer, buffer []byte) error { switch len(s) { case 0: @@ -672,7 +701,7 @@ func EncodeOptionalAddress(addr *libcommon.Address, w io.Writer, buffer []byte) return err } if addr != nil { - if _, err := w.Write(addr.Bytes()); err != nil { + if _, err := w.Write(addr[:]); err != nil { return err } }