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 <martin@swende.se>
This commit is contained in:
parent
6b98d18789
commit
9bbb9df185
@ -18,12 +18,12 @@ func (l Log) MarshalJSON() ([]byte, error) {
|
|||||||
Address common.Address `json:"address" gencodec:"required"`
|
Address common.Address `json:"address" gencodec:"required"`
|
||||||
Topics []common.Hash `json:"topics" gencodec:"required"`
|
Topics []common.Hash `json:"topics" gencodec:"required"`
|
||||||
Data hexutil.Bytes `json:"data" gencodec:"required"`
|
Data hexutil.Bytes `json:"data" gencodec:"required"`
|
||||||
BlockNumber hexutil.Uint64 `json:"blockNumber"`
|
BlockNumber hexutil.Uint64 `json:"blockNumber" rlp:"-"`
|
||||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||||
TxIndex hexutil.Uint `json:"transactionIndex"`
|
TxIndex hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
||||||
BlockHash common.Hash `json:"blockHash"`
|
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
||||||
Index hexutil.Uint `json:"logIndex"`
|
Index hexutil.Uint `json:"logIndex" rlp:"-"`
|
||||||
Removed bool `json:"removed"`
|
Removed bool `json:"removed" rlp:"-"`
|
||||||
}
|
}
|
||||||
var enc Log
|
var enc Log
|
||||||
enc.Address = l.Address
|
enc.Address = l.Address
|
||||||
@ -44,12 +44,12 @@ func (l *Log) UnmarshalJSON(input []byte) error {
|
|||||||
Address *common.Address `json:"address" gencodec:"required"`
|
Address *common.Address `json:"address" gencodec:"required"`
|
||||||
Topics []common.Hash `json:"topics" gencodec:"required"`
|
Topics []common.Hash `json:"topics" gencodec:"required"`
|
||||||
Data *hexutil.Bytes `json:"data" gencodec:"required"`
|
Data *hexutil.Bytes `json:"data" gencodec:"required"`
|
||||||
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
|
BlockNumber *hexutil.Uint64 `json:"blockNumber" rlp:"-"`
|
||||||
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
TxHash *common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||||
TxIndex *hexutil.Uint `json:"transactionIndex"`
|
TxIndex *hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
||||||
BlockHash *common.Hash `json:"blockHash"`
|
BlockHash *common.Hash `json:"blockHash" rlp:"-"`
|
||||||
Index *hexutil.Uint `json:"logIndex"`
|
Index *hexutil.Uint `json:"logIndex" rlp:"-"`
|
||||||
Removed *bool `json:"removed"`
|
Removed *bool `json:"removed" rlp:"-"`
|
||||||
}
|
}
|
||||||
var dec Log
|
var dec Log
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
@ -8,7 +8,7 @@ package types
|
|||||||
import "github.com/ethereum/go-ethereum/rlp"
|
import "github.com/ethereum/go-ethereum/rlp"
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
func (obj *rlpLog) EncodeRLP(_w io.Writer) error {
|
func (obj *Log) EncodeRLP(_w io.Writer) error {
|
||||||
w := rlp.NewEncoderBuffer(_w)
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
_tmp0 := w.List()
|
_tmp0 := w.List()
|
||||||
w.WriteBytes(obj.Address[:])
|
w.WriteBytes(obj.Address[:])
|
||||||
|
@ -18,6 +18,8 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -36,6 +38,22 @@ var encodeBufferPool = sync.Pool{
|
|||||||
New: func() interface{} { return new(bytes.Buffer) },
|
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 *bytes.Buffer object back into encodeBufferPool after use!
|
||||||
|
// The returned byte slice must not be used after returning the buffer.
|
||||||
|
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.
|
// rlpHash encodes x and hashes the encoded bytes.
|
||||||
func rlpHash(x interface{}) (h common.Hash) {
|
func rlpHash(x interface{}) (h common.Hash) {
|
||||||
sha := hasherPool.Get().(crypto.KeccakState)
|
sha := hasherPool.Get().(crypto.KeccakState)
|
||||||
|
@ -17,13 +17,11 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate go run ../../rlp/rlpgen -type Log -out gen_log_rlp.go
|
||||||
//go:generate go run github.com/fjl/gencodec -type Log -field-override logMarshaling -out gen_log_json.go
|
//go:generate go run github.com/fjl/gencodec -type Log -field-override logMarshaling -out gen_log_json.go
|
||||||
|
|
||||||
// Log represents a contract log event. These events are generated by the LOG opcode and
|
// Log represents a contract log event. These events are generated by the LOG opcode and
|
||||||
@ -40,19 +38,19 @@ type Log struct {
|
|||||||
// Derived fields. These fields are filled in by the node
|
// Derived fields. These fields are filled in by the node
|
||||||
// but not secured by consensus.
|
// but not secured by consensus.
|
||||||
// block in which the transaction was included
|
// block in which the transaction was included
|
||||||
BlockNumber uint64 `json:"blockNumber"`
|
BlockNumber uint64 `json:"blockNumber" rlp:"-"`
|
||||||
// hash of the transaction
|
// hash of the transaction
|
||||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||||
// index of the transaction in the block
|
// index of the transaction in the block
|
||||||
TxIndex uint `json:"transactionIndex"`
|
TxIndex uint `json:"transactionIndex" rlp:"-"`
|
||||||
// hash of the block in which the transaction was included
|
// hash of the block in which the transaction was included
|
||||||
BlockHash common.Hash `json:"blockHash"`
|
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
||||||
// index of the log in the block
|
// index of the log in the block
|
||||||
Index uint `json:"logIndex"`
|
Index uint `json:"logIndex" rlp:"-"`
|
||||||
|
|
||||||
// The Removed field is true if this log was reverted due to a chain reorganisation.
|
// The Removed field is true if this log was reverted due to a chain reorganisation.
|
||||||
// You must pay attention to this field if you receive logs through a filter query.
|
// You must pay attention to this field if you receive logs through a filter query.
|
||||||
Removed bool `json:"removed"`
|
Removed bool `json:"removed" rlp:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type logMarshaling struct {
|
type logMarshaling struct {
|
||||||
@ -61,28 +59,3 @@ type logMarshaling struct {
|
|||||||
TxIndex hexutil.Uint
|
TxIndex hexutil.Uint
|
||||||
Index hexutil.Uint
|
Index hexutil.Uint
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate go run ../../rlp/rlpgen -type rlpLog -out gen_log_rlp.go
|
|
||||||
|
|
||||||
// rlpLog is used to RLP-encode both the consensus and storage formats.
|
|
||||||
type rlpLog struct {
|
|
||||||
Address common.Address
|
|
||||||
Topics []common.Hash
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder.
|
|
||||||
func (l *Log) EncodeRLP(w io.Writer) error {
|
|
||||||
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
|
|
||||||
return rlp.Encode(w, &rl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeRLP implements rlp.Decoder.
|
|
||||||
func (l *Log) DecodeRLP(s *rlp.Stream) error {
|
|
||||||
var dec rlpLog
|
|
||||||
err := s.Decode(&dec)
|
|
||||||
if err == nil {
|
|
||||||
l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
@ -153,7 +153,7 @@ func (r *Receipt) MarshalBinary() ([]byte, error) {
|
|||||||
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||||
// from an RLP stream.
|
// from an RLP stream.
|
||||||
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||||
kind, _, err := s.Kind()
|
kind, size, err := s.Kind()
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
@ -165,12 +165,18 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
|||||||
}
|
}
|
||||||
r.Type = LegacyTxType
|
r.Type = LegacyTxType
|
||||||
return r.setFromRLP(dec)
|
return r.setFromRLP(dec)
|
||||||
|
case kind == rlp.Byte:
|
||||||
|
return errShortTypedReceipt
|
||||||
default:
|
default:
|
||||||
// It's an EIP-2718 typed tx receipt.
|
// It's an EIP-2718 typed tx receipt.
|
||||||
b, err := s.Bytes()
|
b, buf, err := getPooledBuffer(size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer encodeBufferPool.Put(buf)
|
||||||
|
if err := s.ReadBytes(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return r.decodeTyped(b)
|
return r.decodeTyped(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,7 +270,7 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
|
|||||||
w.WriteUint64(r.CumulativeGasUsed)
|
w.WriteUint64(r.CumulativeGasUsed)
|
||||||
logList := w.List()
|
logList := w.List()
|
||||||
for _, log := range r.Logs {
|
for _, log := range r.Logs {
|
||||||
if err := rlp.Encode(w, log); err != nil {
|
if err := log.EncodeRLP(w); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,15 +145,23 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|||||||
tx.setDecoded(&inner, rlp.ListSize(size))
|
tx.setDecoded(&inner, rlp.ListSize(size))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
case kind == rlp.Byte:
|
||||||
|
return errShortTypedTx
|
||||||
default:
|
default:
|
||||||
// It's an EIP-2718 typed TX envelope.
|
// It's an EIP-2718 typed TX envelope.
|
||||||
var b []byte
|
// First read the tx payload bytes into a temporary buffer.
|
||||||
if b, err = s.Bytes(); err != nil {
|
b, buf, err := getPooledBuffer(size)
|
||||||
|
if err != nil {
|
||||||
return err
|
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)
|
inner, err := tx.decodeTyped(b)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tx.setDecoded(inner, uint64(len(b)))
|
tx.setDecoded(inner, size)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user