From 353c1d3ee0be954b70060e3cf54968cdfb7fb1dc Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 22 Aug 2023 18:41:31 +0200 Subject: [PATCH] core/types: reduce allocation when decoding typed transactions and receipts --- core/types/hashing.go | 17 +++++++++++++++++ core/types/receipt.go | 11 +++++++++-- core/types/transaction.go | 10 ++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/core/types/hashing.go b/core/types/hashing.go index fbdeaf0d07..e127855ca8 100644 --- a/core/types/hashing.go +++ b/core/types/hashing.go @@ -18,6 +18,8 @@ package types import ( "bytes" + "fmt" + "math" "sync" "github.com/ethereum/go-ethereum/common" @@ -36,6 +38,21 @@ var encodeBufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } +// getPooledBuffer retrieves a buffer from the pool and creates a byte slice +// of the requested size from it. +// +// The caller should return the Buffer object back into encodeBufferPool after use! +func getPooledBuffer(size uint64) ([]byte, *bytes.Buffer, error) { + if size > math.MaxInt { + return nil, nil, fmt.Errorf("can't get buffer of size %d", size) + } + buf := encodeBufferPool.Get().(*bytes.Buffer) + buf.Reset() + buf.Grow(int(size)) + b := buf.Bytes()[:int(size)] + return b, buf, nil +} + // rlpHash encodes x and hashes the encoded bytes. func rlpHash(x interface{}) (h common.Hash) { sha := hasherPool.Get().(crypto.KeccakState) diff --git a/core/types/receipt.go b/core/types/receipt.go index a96eb6b8d6..28522f8d07 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -153,7 +153,7 @@ func (r *Receipt) MarshalBinary() ([]byte, error) { // DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt // from an RLP stream. func (r *Receipt) DecodeRLP(s *rlp.Stream) error { - kind, _, err := s.Kind() + kind, size, err := s.Kind() switch { case err != nil: return err @@ -167,7 +167,14 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return r.setFromRLP(dec) default: // It's an EIP-2718 typed tx receipt. - b, err := s.Bytes() + b, buf, err := getPooledBuffer(size) + if err != nil { + return err + } + defer encodeBufferPool.Put(buf) + if err := s.ReadBytes(b); err != nil { + return err + } if err != nil { return err } diff --git a/core/types/transaction.go b/core/types/transaction.go index bf6dfb3428..44b588a012 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -147,10 +147,16 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { return err default: // It's an EIP-2718 typed TX envelope. - var b []byte - if b, err = s.Bytes(); err != nil { + // First read the tx payload bytes into a temporary buffer. + b, buf, err := getPooledBuffer(size) + if err != nil { return err } + defer encodeBufferPool.Put(buf) + if err := s.ReadBytes(b); err != nil { + return err + } + // Now decode the inner transaction. inner, err := tx.decodeTyped(b) if err == nil { tx.setDecoded(inner, uint64(len(b)))