Skip to content

Commit 9bbb9df

Browse files
fjlholiman
andauthored
core/types: transaction and receipt encoding/decoding optimizations (#27976)
Just some minor optimizations I figured out a while ago. By using ReadBytes instead of Bytes on the rlp stream, we can save the allocation of a temporary buffer for the typed tx payload. If kind == rlp.Byte, the size reported by Stream.Kind will be zero, but we need a buffer of size 1 for ReadBytes. Since typed txs always have to be longer than 1 byte, we can just return an error for kind == rlp.Byte. There is a also a small change for Log: since the first three fields of Log are the ones that should appear in the canon encoding, we can simply ignore the remaining fields via struct tag. Doing this removes an indirection through the rlpLog type. --------- Co-authored-by: Martin Holst Swende <[email protected]>
1 parent 6b98d18 commit 9bbb9df

File tree

6 files changed

+58
-53
lines changed

6 files changed

+58
-53
lines changed

core/types/gen_log_json.go

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/gen_log_rlp.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/hashing.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package types
1818

1919
import (
2020
"bytes"
21+
"fmt"
22+
"math"
2123
"sync"
2224

2325
"github.com/ethereum/go-ethereum/common"
@@ -36,6 +38,22 @@ var encodeBufferPool = sync.Pool{
3638
New: func() interface{} { return new(bytes.Buffer) },
3739
}
3840

41+
// getPooledBuffer retrieves a buffer from the pool and creates a byte slice of the
42+
// requested size from it.
43+
//
44+
// The caller should return the *bytes.Buffer object back into encodeBufferPool after use!
45+
// The returned byte slice must not be used after returning the buffer.
46+
func getPooledBuffer(size uint64) ([]byte, *bytes.Buffer, error) {
47+
if size > math.MaxInt {
48+
return nil, nil, fmt.Errorf("can't get buffer of size %d", size)
49+
}
50+
buf := encodeBufferPool.Get().(*bytes.Buffer)
51+
buf.Reset()
52+
buf.Grow(int(size))
53+
b := buf.Bytes()[:int(size)]
54+
return b, buf, nil
55+
}
56+
3957
// rlpHash encodes x and hashes the encoded bytes.
4058
func rlpHash(x interface{}) (h common.Hash) {
4159
sha := hasherPool.Get().(crypto.KeccakState)

core/types/log.go

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717
package types
1818

1919
import (
20-
"io"
21-
2220
"github.com/ethereum/go-ethereum/common"
2321
"github.com/ethereum/go-ethereum/common/hexutil"
24-
"github.com/ethereum/go-ethereum/rlp"
2522
)
2623

24+
//go:generate go run ../../rlp/rlpgen -type Log -out gen_log_rlp.go
2725
//go:generate go run github.com/fjl/gencodec -type Log -field-override logMarshaling -out gen_log_json.go
2826

2927
// Log represents a contract log event. These events are generated by the LOG opcode and
@@ -40,19 +38,19 @@ type Log struct {
4038
// Derived fields. These fields are filled in by the node
4139
// but not secured by consensus.
4240
// block in which the transaction was included
43-
BlockNumber uint64 `json:"blockNumber"`
41+
BlockNumber uint64 `json:"blockNumber" rlp:"-"`
4442
// hash of the transaction
45-
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
43+
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
4644
// index of the transaction in the block
47-
TxIndex uint `json:"transactionIndex"`
45+
TxIndex uint `json:"transactionIndex" rlp:"-"`
4846
// hash of the block in which the transaction was included
49-
BlockHash common.Hash `json:"blockHash"`
47+
BlockHash common.Hash `json:"blockHash" rlp:"-"`
5048
// index of the log in the block
51-
Index uint `json:"logIndex"`
49+
Index uint `json:"logIndex" rlp:"-"`
5250

5351
// The Removed field is true if this log was reverted due to a chain reorganisation.
5452
// You must pay attention to this field if you receive logs through a filter query.
55-
Removed bool `json:"removed"`
53+
Removed bool `json:"removed" rlp:"-"`
5654
}
5755

5856
type logMarshaling struct {
@@ -61,28 +59,3 @@ type logMarshaling struct {
6159
TxIndex hexutil.Uint
6260
Index hexutil.Uint
6361
}
64-
65-
//go:generate go run ../../rlp/rlpgen -type rlpLog -out gen_log_rlp.go
66-
67-
// rlpLog is used to RLP-encode both the consensus and storage formats.
68-
type rlpLog struct {
69-
Address common.Address
70-
Topics []common.Hash
71-
Data []byte
72-
}
73-
74-
// EncodeRLP implements rlp.Encoder.
75-
func (l *Log) EncodeRLP(w io.Writer) error {
76-
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
77-
return rlp.Encode(w, &rl)
78-
}
79-
80-
// DecodeRLP implements rlp.Decoder.
81-
func (l *Log) DecodeRLP(s *rlp.Stream) error {
82-
var dec rlpLog
83-
err := s.Decode(&dec)
84-
if err == nil {
85-
l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data
86-
}
87-
return err
88-
}

core/types/receipt.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func (r *Receipt) MarshalBinary() ([]byte, error) {
153153
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
154154
// from an RLP stream.
155155
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
156-
kind, _, err := s.Kind()
156+
kind, size, err := s.Kind()
157157
switch {
158158
case err != nil:
159159
return err
@@ -165,12 +165,18 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
165165
}
166166
r.Type = LegacyTxType
167167
return r.setFromRLP(dec)
168+
case kind == rlp.Byte:
169+
return errShortTypedReceipt
168170
default:
169171
// It's an EIP-2718 typed tx receipt.
170-
b, err := s.Bytes()
172+
b, buf, err := getPooledBuffer(size)
171173
if err != nil {
172174
return err
173175
}
176+
defer encodeBufferPool.Put(buf)
177+
if err := s.ReadBytes(b); err != nil {
178+
return err
179+
}
174180
return r.decodeTyped(b)
175181
}
176182
}
@@ -264,7 +270,7 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
264270
w.WriteUint64(r.CumulativeGasUsed)
265271
logList := w.List()
266272
for _, log := range r.Logs {
267-
if err := rlp.Encode(w, log); err != nil {
273+
if err := log.EncodeRLP(w); err != nil {
268274
return err
269275
}
270276
}

core/types/transaction.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,23 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
145145
tx.setDecoded(&inner, rlp.ListSize(size))
146146
}
147147
return err
148+
case kind == rlp.Byte:
149+
return errShortTypedTx
148150
default:
149151
// It's an EIP-2718 typed TX envelope.
150-
var b []byte
151-
if b, err = s.Bytes(); err != nil {
152+
// First read the tx payload bytes into a temporary buffer.
153+
b, buf, err := getPooledBuffer(size)
154+
if err != nil {
155+
return err
156+
}
157+
defer encodeBufferPool.Put(buf)
158+
if err := s.ReadBytes(b); err != nil {
152159
return err
153160
}
161+
// Now decode the inner transaction.
154162
inner, err := tx.decodeTyped(b)
155163
if err == nil {
156-
tx.setDecoded(inner, uint64(len(b)))
164+
tx.setDecoded(inner, size)
157165
}
158166
return err
159167
}

0 commit comments

Comments
 (0)