Merge pull request #31 from openrelayxyz/laconic-merge

Laconic merge
This commit is contained in:
Philip Morlier 2023-09-11 18:07:47 -07:00 committed by GitHub
commit a1ef353ae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 926 additions and 366 deletions

View File

@ -57,6 +57,7 @@ type Backend interface {
GetTrie(hash Hash) (Trie, error)
GetAccountTrie(stateRoot Hash, account Address) (Trie, error)
GetContractCode(Hash) ([]byte, error)
// ChainConfig() *params.ChainConfig
// Engine() consensus.Engine

View File

@ -49,6 +49,6 @@ A simple implimentation would look like so:
Access
******
As with pre-built plugins, a``.so`` will need to be built from``main.go`` and moved into ``~/.ethereum/plugins``. Geth will need to be started with with a ``http.api=mymamespace`` flag. Additionally you will need to include a ``--http`` flag in order to access the standard json rpc methods.
As with pre-built plugins, a``.so`` will need to be built from``main.go`` and moved into ``~/.ethereum/plugins``. Geth will need to be started with with a ``http.api=mynamespace`` flag. Additionally you will need to include a ``--http`` flag in order to access the standard json rpc methods.
The plugin can now be accessed with an rpc call to ``mynamespace_helloWorld``.

View File

@ -14,7 +14,7 @@ Flags
* **Name:** Flags
* **Type:** `flag.FlagSet`_
* **Behavior:** This FlagSet will be parsed and your plugin will be able to access the resulting flags. Flags will be passed to Geth from the command line and are intended to of the plugin. Note that if any flags are provided, certain checks are disabled within Geth to avoid failing due to unexpected flags.
* **Behavior:** This FlagSet will be parsed and your plugin will be able to access the resulting flags. Flags will be passed to Geth from the command line and are intended to configure the behavior of the plugin. Passed flags must follow ``--`` to be parsed by this FlagSet, which is necessary to avoid Geth failing due to unexpected flags.
Subcommands
-----------

View File

@ -26,7 +26,7 @@ Hooks are called from functions within the plugin. For example, if we wanted to
backend.ChainDb().Put(append([]byte("su"), blockRoot.Bytes()...), data)
}
Many hooks can be deployed in an one plugin as is the case with the **BlockUpdater** plugin.
Many hooks can be deployed in one plugin as is the case with the **BlockUpdater** plugin.
.. contents:: :local:
@ -87,9 +87,9 @@ NewHead
**Function Signature**:``func(*types.Block, common.Hash, []*types.Log)``
Invoked when a new block becomes the canonical latest block. Returns a block, hash, and log.
Invoked when a new block becomes the canonical latest block. Returns a block, hash, and logs.
.. note:: If severtal blocks are processed in a group (such as
.. note:: If several blocks are processed in a group (such as
during a reorg) this may not be called for each block. You should track the prior latest head if you need to process intermediate blocks.
NewSideBlock
@ -97,7 +97,7 @@ NewSideBlock
**Function Signature**:``func(*types.Block, common.Hash, []*types.Log)``
Invoked when a block is side-chained. Returns a block, has, and logs.
Invoked when a block is side-chained. Returns a block, hash, and logs.
.. note:: Blocks passed to this method are non-canonical blocks.

View File

@ -31,7 +31,7 @@ GetFeed
=======
``GetFeed() Feed``
Returns a new feed that the plugin can used for publish/subscribe models.
Returns a new feed that the plugin can use for publish/subscribe models.
For example:

View File

@ -32,7 +32,7 @@ A GetAPIs method is required in the body of the plugin in order to make the plug
Subscription Function
*********************
For subscriptions (supported on IPC and websockets), a function should take MyService as a reciever and a context.Context object as an argument and return a channel and an error. The following is a subscription function that impliments a timer.
For subscriptions (supported on IPC and websockets), a function should take MyService as a reciever and a context.Context object as an argument and return a channel and an error. The following is a subscription function that implements a timer.
.. code-block:: Go
@ -64,7 +64,7 @@ Access
.. Note:: Plugins providing subscriptions can be accessed via IPC
and websockets. In the below example we will be using `wscat`_ to connect a websocket to a local Geth node.
As with pre-built plugins, a ``.so`` will need to be built from ``main.go`` and moved into ``~/.ethereum/plugins``. Geth will need to be started with with ``--ws --ws.api=mynamespace``flags. Additionally you will need to include a ``--http`` flag in order to access the standard json rpc methods.
As with pre-built plugins, a ``.so`` will need to be built from ``main.go`` and moved into ``~/.ethereum/plugins``. Geth will need to be started with ``--ws --ws.api=mynamespace`` flags. Additionally you will need to include a ``--http`` flag in order to access the standard json rpc methods.
After starting Geth, from a seperate terminal run:

View File

@ -22,7 +22,7 @@ First an empty MyService Struct.
Map
***
Next, a map of tracers to functions returning a ``core.TracerResult`` which will be implimented like so:
Next, a map of tracers to functions returning a ``core.TracerResult`` which will be implemented like so:
.. code-block:: Go

View File

@ -15,7 +15,7 @@ These plugins provide new json rpc methods to access several objects containing
Subcommand
------------
A subcommand redifines the total behavior of Geth and could stand on its own. In contrast with the other plugin types which, in general, are meant to capture and manipulate information, a subcommand is meant to change the overall behavior of Geth. It may do this in order to capture information but the primary fuctionality is a modulation of geth behaviour.
A subcommand redefines the total behavior of Geth and could stand on its own. In contrast with the other plugin types which, in general, are meant to capture and manipulate information, a subcommand is meant to change the overall behavior of Geth. It may do this in order to capture information but the primary fuctionality is a modulation of geth behaviour.
Tracers
-------

1
go.mod
View File

@ -16,6 +16,7 @@ require (
require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

2
go.sum
View File

@ -17,6 +17,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View File

@ -1,21 +1,12 @@
package bls12381
import (
"encoding/hex"
"bytes"
"crypto/rand"
"math/big"
"testing"
)
func fromHex(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}
func (g *G1) one() *PointG1 {
one, _ := g.fromBytesUnchecked(
fromHex("" +

View File

@ -17,15 +17,34 @@
package bls12381
import (
"strings"
"encoding/hex"
"errors"
"math/big"
)
func bigFromHex(hex string) *big.Int {
b, _ := hex.DecodeString(strings.TrimPrefix(hex, "0x"))
return new(big.Int).SetBytes(b)
// fromHex returns the bytes represented by the hexadecimal string s.
// s may be prefixed with "0x".
func fromHex(s string) []byte {
if has0xPrefix(s) {
s = s[2:]
}
if len(s)%2 == 1 {
s = "0" + s
}
h, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return h
}
// has0xPrefix validates str begins with '0x' or '0X'.
func has0xPrefix(str string) bool {
return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
}
func bigFromHex(s string) *big.Int {
return new(big.Int).SetBytes(fromHex(s))
}
// decodeFieldElement expects 64 byte input with zero top 16 bytes,

View File

@ -17,7 +17,6 @@
package hasher
import (
"fmt"
"bufio"
"bytes"
"encoding/gob"
@ -213,9 +212,7 @@ func (st *StackTrie) TryUpdate(key, value []byte) error {
}
func (st *StackTrie) Update(key, value []byte) {
if err := st.TryUpdate(key, value); err != nil {
fmt.Errorf("Unhandled trie error in StackTrie.Update", "err", err)
}
st.TryUpdate(key, value)
}
func (st *StackTrie) Reset() {

View File

@ -34,6 +34,36 @@ var (
CalaverasGenesisHash = core.HexToHash("0xeb9233d066c275efcdfed8037f4fc082770176aefdbcb7691c71da412a5670f2")
)
var (
// TestChainConfig contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers for testing proposes.
TestChainConfig = &ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
MergeNetsplitBlock: nil,
ShanghaiTime: nil,
CancunTime: nil,
PragueTime: nil,
TerminalTotalDifficulty: nil,
TerminalTotalDifficultyPassed: false,
Ethash: new(EthashConfig),
Clique: nil,
}
)
// TrustedCheckpoint represents a set of post-processed trie roots (CHT and
// BloomTrie) associated with the appropriate section index and head hash. It is

View File

@ -22,6 +22,37 @@ import (
"testing"
)
var (
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Ethash consensus.
AllEthashProtocolChanges = &ChainConfig{
ChainID: big.NewInt(1337),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
MergeNetsplitBlock: nil,
ShanghaiTime: nil,
CancunTime: nil,
PragueTime: nil,
TerminalTotalDifficulty: nil,
TerminalTotalDifficultyPassed: false,
Ethash: new(EthashConfig),
Clique: nil,
}
)
func TestCheckCompatible(t *testing.T) {
type test struct {
stored, new *ChainConfig

View File

@ -0,0 +1,34 @@
package trie
// HexToCompact converts a hex path to the compact encoded format
func HexToCompact(hex []byte) []byte {
return hexToCompact(hex)
}
func hexToCompact(hex []byte) []byte {
terminator := byte(0)
if hasTerm(hex) {
terminator = 1
hex = hex[:len(hex)-1]
}
buf := make([]byte, len(hex)/2+1)
buf[0] = terminator << 5 // the flag byte
if len(hex)&1 == 1 {
buf[0] |= 1 << 4 // odd flag
buf[0] |= hex[0] // first nibble is contained in the first byte
hex = hex[1:]
}
decodeNibbles(hex, buf[1:])
return buf
}
func decodeNibbles(nibbles []byte, bytes []byte) {
for bi, ni := 0, 0; ni < len(nibbles); bi, ni = bi+1, ni+2 {
bytes[bi] = nibbles[ni]<<4 | nibbles[ni+1]
}
}
// hasTerm returns whether a hex key has the terminator flag.
func hasTerm(s []byte) bool {
return len(s) > 0 && s[len(s)-1] == 16
}

169
restricted/trie/iterator.go Normal file
View File

@ -0,0 +1,169 @@
package trie
import (
"bytes"
"github.com/openrelayxyz/plugeth-utils/core"
)
type NodeIterator = core.NodeIterator
// Iterator is a key-value trie iterator that traverses a Trie.
type Iterator struct {
nodeIt core.NodeIterator
Key []byte // Current data key on which the iterator is positioned on
Value []byte // Current data value on which the iterator is positioned on
Err error
}
// NewIterator creates a new key-value iterator from a node iterator.
// Note that the value returned by the iterator is raw. If the content is encoded
// (e.g. storage value is RLP-encoded), it's caller's duty to decode it.
func NewIterator(it core.NodeIterator) *Iterator {
return &Iterator{
nodeIt: it,
}
}
// Next moves the iterator forward one key-value entry.
func (it *Iterator) Next() bool {
for it.nodeIt.Next(true) {
if it.nodeIt.Leaf() {
it.Key = it.nodeIt.LeafKey()
it.Value = it.nodeIt.LeafBlob()
return true
}
}
it.Key = nil
it.Value = nil
it.Err = it.nodeIt.Error()
return false
}
// Prove generates the Merkle proof for the leaf node the iterator is currently
// positioned on.
func (it *Iterator) Prove() [][]byte {
return it.nodeIt.LeafProof()
}
type differenceIterator struct {
a, b core.NodeIterator // Nodes returned are those in b - a.
eof bool // Indicates a has run out of elements
count int // Number of nodes scanned on either trie
}
// NewDifferenceIterator constructs a NodeIterator that iterates over elements in b that
// are not in a. Returns the iterator, and a pointer to an integer recording the number
// of nodes seen.
func NewDifferenceIterator(a, b core.NodeIterator) (core.NodeIterator, *int) {
a.Next(true)
it := &differenceIterator{
a: a,
b: b,
}
return it, &it.count
}
func (it *differenceIterator) Hash() core.Hash {
return it.b.Hash()
}
func (it *differenceIterator) Parent() core.Hash {
return it.b.Parent()
}
func (it *differenceIterator) Leaf() bool {
return it.b.Leaf()
}
func (it *differenceIterator) LeafKey() []byte {
return it.b.LeafKey()
}
func (it *differenceIterator) LeafBlob() []byte {
return it.b.LeafBlob()
}
func (it *differenceIterator) LeafProof() [][]byte {
return it.b.LeafProof()
}
func (it *differenceIterator) Path() []byte {
return it.b.Path()
}
func (it *differenceIterator) NodeBlob() []byte {
return it.b.NodeBlob()
}
func (it *differenceIterator) AddResolver(resolver core.NodeResolver) {
panic("not implemented")
}
func (it *differenceIterator) Next(bool) bool {
// Invariants:
// - We always advance at least one element in b.
// - At the start of this function, a's path is lexically greater than b's.
if !it.b.Next(true) {
return false
}
it.count++
if it.eof {
// a has reached eof, so we just return all elements from b
return true
}
for {
switch compareNodes(it.a, it.b) {
case -1:
// b jumped past a; advance a
if !it.a.Next(true) {
it.eof = true
return true
}
it.count++
case 1:
// b is before a
return true
case 0:
// a and b are identical; skip this whole subtree if the nodes have hashes
hasHash := it.a.Hash() == core.Hash{}
if !it.b.Next(hasHash) {
return false
}
it.count++
if !it.a.Next(hasHash) {
it.eof = true
return true
}
it.count++
}
}
}
func (it *differenceIterator) Error() error {
if err := it.a.Error(); err != nil {
return err
}
return it.b.Error()
}
func compareNodes(a, b core.NodeIterator) int {
if cmp := bytes.Compare(a.Path(), b.Path()); cmp != 0 {
return cmp
}
if a.Leaf() && !b.Leaf() {
return -1
} else if b.Leaf() && !a.Leaf() {
return 1
}
if cmp := bytes.Compare(a.Hash().Bytes(), b.Hash().Bytes()); cmp != 0 {
return cmp
}
if a.Leaf() && b.Leaf() {
return bytes.Compare(a.LeafBlob(), b.LeafBlob())
}
return 0
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 The go-ethereum Authors
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@ -22,7 +22,7 @@ import (
"github.com/openrelayxyz/plugeth-utils/core"
)
//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
//go:generate go run github.com/fjl/gencodec -type AccessTuple -out gen_access_tuple.go
// AccessList is an EIP-2930 access list.
type AccessList []AccessTuple
@ -59,7 +59,7 @@ type AccessListTx struct {
func (tx *AccessListTx) copy() TxData {
cpy := &AccessListTx{
Nonce: tx.Nonce,
To: tx.To, // TODO: copy pointed-to address
To: copyAddressPtr(tx.To),
Data: core.CopyBytes(tx.Data),
Gas: tx.Gas,
// These are copied below.
@ -96,7 +96,6 @@ func (tx *AccessListTx) copy() TxData {
// accessors for innerTx.
func (tx *AccessListTx) txType() byte { return AccessListTxType }
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
func (tx *AccessListTx) protected() bool { return true }
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
func (tx *AccessListTx) data() []byte { return tx.Data }
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
@ -107,6 +106,10 @@ func (tx *AccessListTx) value() *big.Int { return tx.Value }
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
func (tx *AccessListTx) to() *core.Address { return tx.To }
func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return dst.Set(tx.GasPrice)
}
func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
return tx.V, tx.R, tx.S
}

View File

@ -25,8 +25,8 @@ import (
type DynamicFeeTx struct {
ChainID *big.Int
Nonce uint64
GasTipCap *big.Int
GasFeeCap *big.Int
GasTipCap *big.Int // a.k.a. maxPriorityFeePerGas
GasFeeCap *big.Int // a.k.a. maxFeePerGas
Gas uint64
To *core.Address `rlp:"nil"` // nil means contract creation
Value *big.Int
@ -43,7 +43,7 @@ type DynamicFeeTx struct {
func (tx *DynamicFeeTx) copy() TxData {
cpy := &DynamicFeeTx{
Nonce: tx.Nonce,
To: tx.To, // TODO: copy pointed-to address
To: copyAddressPtr(tx.To),
Data: core.CopyBytes(tx.Data),
Gas: tx.Gas,
// These are copied below.
@ -84,7 +84,6 @@ func (tx *DynamicFeeTx) copy() TxData {
// accessors for innerTx.
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
func (tx *DynamicFeeTx) protected() bool { return true }
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
func (tx *DynamicFeeTx) data() []byte { return tx.Data }
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
@ -95,6 +94,17 @@ func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
func (tx *DynamicFeeTx) to() *core.Address { return tx.To }
func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil {
return dst.Set(tx.GasFeeCap)
}
tip := dst.Sub(tx.GasFeeCap, baseFee)
if tip.Cmp(tx.GasTipCap) > 0 {
tip.Set(tx.GasTipCap)
}
return tip.Add(tip, baseFee)
}
func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) {
return tx.V, tx.R, tx.S
}

View File

@ -25,6 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
TxHash core.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress core.Address `json:"contractAddress"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
BlockHash core.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex hexutil.Uint `json:"transactionIndex"`
@ -39,6 +40,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
enc.TxHash = r.TxHash
enc.ContractAddress = r.ContractAddress
enc.GasUsed = hexutil.Uint64(r.GasUsed)
enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice)
enc.BlockHash = r.BlockHash
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
@ -57,6 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
TxHash *core.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress *core.Address `json:"contractAddress"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
BlockHash *core.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
@ -97,6 +100,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'gasUsed' for Receipt")
}
r.GasUsed = uint64(*dec.GasUsed)
if dec.EffectiveGasPrice != nil {
r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice)
}
if dec.BlockHash != nil {
r.BlockHash = *dec.BlockHash
}

View File

@ -26,10 +26,10 @@ import (
"testing"
"github.com/openrelayxyz/plugeth-utils/core"
"github.com/openrelayxyz/plugeth-utils/restricted/hexutil"
"github.com/openrelayxyz/plugeth-utils/restricted/types"
"github.com/openrelayxyz/plugeth-utils/restricted/crypto"
"github.com/openrelayxyz/plugeth-utils/restricted/hexutil"
"github.com/openrelayxyz/plugeth-utils/restricted/rlp"
"github.com/openrelayxyz/plugeth-utils/restricted/types"
)
func fromHex(data string) []byte {
@ -136,9 +136,10 @@ func (d *hashToHumanReadable) Reset() {
d.data = make([]byte, 0)
}
func (d *hashToHumanReadable) Update(i []byte, i2 []byte) {
func (d *hashToHumanReadable) Update(i []byte, i2 []byte) error {
l := fmt.Sprintf("%x %x\n", i, i2)
d.data = append(d.data, []byte(l)...)
return nil
}
func (d *hashToHumanReadable) Hash() core.Hash {

View File

@ -1,4 +1,4 @@
// Copyright 2020 The go-ethereum Authors
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@ -62,7 +62,7 @@ func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPric
func (tx *LegacyTx) copy() TxData {
cpy := &LegacyTx{
Nonce: tx.Nonce,
To: tx.To, // TODO: copy pointed-to address
To: copyAddressPtr(tx.To),
Data: core.CopyBytes(tx.Data),
Gas: tx.Gas,
// These are initialized below.
@ -103,6 +103,10 @@ func (tx *LegacyTx) value() *big.Int { return tx.Value }
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
func (tx *LegacyTx) to() *core.Address { return tx.To }
func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return dst.Set(tx.GasPrice)
}
func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
return tx.V, tx.R, tx.S
}

View File

@ -25,13 +25,13 @@ import (
"unsafe"
"github.com/openrelayxyz/plugeth-utils/core"
"github.com/openrelayxyz/plugeth-utils/restricted/hexutil"
"github.com/openrelayxyz/plugeth-utils/restricted/crypto"
"github.com/openrelayxyz/plugeth-utils/restricted/hexutil"
"github.com/openrelayxyz/plugeth-utils/restricted/params"
"github.com/openrelayxyz/plugeth-utils/restricted/rlp"
)
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
//go:generate go run github.com/fjl/gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
var (
receiptStatusFailedRLP = []byte{}
@ -60,10 +60,10 @@ type Receipt struct {
Logs []*Log `json:"logs" gencodec:"required"`
// Implementation fields: These fields are added by geth when processing a transaction.
// They are stored in the chain database.
TxHash core.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress core.Address `json:"contractAddress"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // required, but tag omitted for backwards compatibility
// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
@ -78,6 +78,7 @@ type receiptMarshaling struct {
Status hexutil.Uint64
CumulativeGasUsed hexutil.Uint64
GasUsed hexutil.Uint64
EffectiveGasPrice *hexutil.Big
BlockNumber *hexutil.Big
TransactionIndex hexutil.Uint
}
@ -115,6 +116,9 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
// into an RLP stream. If no post state is present, byzantium fork is assumed.
// For a legacy Receipt this returns RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs])
// For a EIP-2718 Receipt this returns RLP(TxType || ReceiptPayload)
// For a EIP-2930 Receipt, TxType == 0x01 and ReceiptPayload == RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs])
func (r *Receipt) EncodeRLP(w io.Writer) error {
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
if r.Type == LegacyTxType {
@ -123,13 +127,29 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
buf := encodeBufferPool.Get().(*bytes.Buffer)
defer encodeBufferPool.Put(buf)
buf.Reset()
buf.WriteByte(r.Type)
if err := rlp.Encode(buf, data); err != nil {
if err := r.encodeTyped(data, buf); err != nil {
return err
}
return rlp.Encode(w, buf.Bytes())
}
// encodeTyped writes the canonical encoding of a typed receipt to w.
func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
w.WriteByte(r.Type)
return rlp.Encode(w, data)
}
// MarshalBinary returns the consensus encoding of the receipt.
func (r *Receipt) MarshalBinary() ([]byte, error) {
if r.Type == LegacyTxType {
return rlp.EncodeToBytes(r)
}
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
var buf bytes.Buffer
err := r.encodeTyped(data, &buf)
return buf.Bytes(), err
}
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
@ -145,26 +165,49 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
}
r.Type = LegacyTxType
return r.setFromRLP(dec)
case kind == rlp.String:
default:
// It's an EIP-2718 typed tx receipt.
b, err := s.Bytes()
if err != nil {
return err
}
if len(b) == 0 {
return errEmptyTypedReceipt
return r.decodeTyped(b)
}
r.Type = b[0]
if r.Type == AccessListTxType || r.Type == DynamicFeeTxType {
var dec receiptRLP
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
}
// UnmarshalBinary decodes the consensus encoding of receipts.
// It supports legacy RLP receipts and EIP-2718 typed receipts.
func (r *Receipt) UnmarshalBinary(b []byte) error {
if len(b) > 0 && b[0] > 0x7f {
// It's a legacy receipt decode the RLP
var data receiptRLP
err := rlp.DecodeBytes(b, &data)
if err != nil {
return err
}
return r.setFromRLP(dec)
r.Type = LegacyTxType
return r.setFromRLP(data)
}
return ErrTxTypeNotSupported
// It's an EIP2718 typed transaction envelope.
return r.decodeTyped(b)
}
// decodeTyped decodes a typed receipt from the canonical format.
func (r *Receipt) decodeTyped(b []byte) error {
if len(b) <= 1 {
return errEmptyTypedReceipt
}
switch b[0] {
case DynamicFeeTxType, AccessListTxType:
var data receiptRLP
err := rlp.DecodeBytes(b[1:], &data)
if err != nil {
return err
}
r.Type = b[0]
return r.setFromRLP(data)
default:
return rlp.ErrExpectedList
return ErrTxTypeNotSupported
}
}
@ -208,8 +251,8 @@ func (r *Receipt) Size() float64 {
return size
}
// ReceiptForStorage is a wrapper around a Receipt that flattens and parses the
// entire content of a receipt, as opposed to only the consensus fields originally.
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
// that omits the Bloom field and deserialization that re-computes it.
type ReceiptForStorage Receipt
// EncodeRLP implements rlp.Encoder.
@ -222,7 +265,8 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, enc)
}
// DecodeRLP implements rlp.Decoder.
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
// fields of a receipt from an RLP stream.
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
var stored storedReceiptRLP
if err := s.Decode(&stored); err != nil {
@ -234,6 +278,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
r.CumulativeGasUsed = stored.CumulativeGasUsed
r.Logs = stored.Logs
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
return nil
}
@ -265,42 +310,48 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
// DeriveFields fills the receipts with their computed fields based on consensus
// data and contextual infos like containing block and transactions.
func (r Receipts) DeriveFields(config *params.ChainConfig, hash core.Hash, number uint64, txs Transactions) error {
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash core.Hash, number uint64, baseFee *big.Int, txs []*Transaction) error {
signer := MakeSigner(config, new(big.Int).SetUint64(number))
logIndex := uint(0)
if len(txs) != len(r) {
if len(txs) != len(rs) {
return errors.New("transaction and receipt count mismatch")
}
for i := 0; i < len(r); i++ {
for i := 0; i < len(rs); i++ {
// The transaction type and hash can be retrieved from the transaction itself
r[i].Type = txs[i].Type()
r[i].TxHash = txs[i].Hash()
rs[i].Type = txs[i].Type()
rs[i].TxHash = txs[i].Hash()
rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
// block location fields
r[i].BlockHash = hash
r[i].BlockNumber = new(big.Int).SetUint64(number)
r[i].TransactionIndex = uint(i)
rs[i].BlockHash = hash
rs[i].BlockNumber = new(big.Int).SetUint64(number)
rs[i].TransactionIndex = uint(i)
// The contract address can be derived from the transaction itself
if txs[i].To() == nil {
// Deriving the signer is expensive, only do if it's actually needed
from, _ := Sender(signer, txs[i])
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
} else {
rs[i].ContractAddress = core.Address{}
}
// The used gas can be calculated based on previous r
if i == 0 {
r[i].GasUsed = r[i].CumulativeGasUsed
rs[i].GasUsed = rs[i].CumulativeGasUsed
} else {
r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed
rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
}
// The derived log fields can simply be set from the block and transaction
for j := 0; j < len(r[i].Logs); j++ {
r[i].Logs[j].BlockNumber = number
r[i].Logs[j].BlockHash = hash
r[i].Logs[j].TxHash = r[i].TxHash
r[i].Logs[j].TxIndex = uint(i)
r[i].Logs[j].Index = logIndex
for j := 0; j < len(rs[i].Logs); j++ {
rs[i].Logs[j].BlockNumber = number
rs[i].Logs[j].BlockHash = hash
rs[i].Logs[j].TxHash = rs[i].TxHash
rs[i].Logs[j].TxIndex = uint(i)
rs[i].Logs[j].Index = logIndex
logIndex++
}
}

View File

@ -18,17 +18,233 @@ package types
import (
"bytes"
"encoding/hex"
"encoding/json"
"math"
"math/big"
"reflect"
"testing"
"encoding/hex"
"github.com/kylelemons/godebug/diff"
"github.com/openrelayxyz/plugeth-utils/core"
"github.com/openrelayxyz/plugeth-utils/restricted/crypto"
"github.com/openrelayxyz/plugeth-utils/restricted/params"
"github.com/openrelayxyz/plugeth-utils/restricted/rlp"
)
var (
legacyReceipt = &Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: core.BytesToAddress([]byte{0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
{
Address: core.BytesToAddress([]byte{0x01, 0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
},
}
accessListReceipt = &Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: core.BytesToAddress([]byte{0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
{
Address: core.BytesToAddress([]byte{0x01, 0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
},
Type: AccessListTxType,
}
eip1559Receipt = &Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: core.BytesToAddress([]byte{0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
{
Address: core.BytesToAddress([]byte{0x01, 0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
},
Type: DynamicFeeTxType,
}
// Create a few transactions to have receipts for
to2 = core.HexToAddress("0x2")
to3 = core.HexToAddress("0x3")
to4 = core.HexToAddress("0x4")
to5 = core.HexToAddress("0x5")
to6 = core.HexToAddress("0x6")
to7 = core.HexToAddress("0x7")
txs = Transactions{
NewTx(&LegacyTx{
Nonce: 1,
Value: big.NewInt(1),
Gas: 1,
GasPrice: big.NewInt(11),
}),
NewTx(&LegacyTx{
To: &to2,
Nonce: 2,
Value: big.NewInt(2),
Gas: 2,
GasPrice: big.NewInt(22),
}),
NewTx(&AccessListTx{
To: &to3,
Nonce: 3,
Value: big.NewInt(3),
Gas: 3,
GasPrice: big.NewInt(33),
}),
// EIP-1559 transactions.
NewTx(&DynamicFeeTx{
To: &to4,
Nonce: 4,
Value: big.NewInt(4),
Gas: 4,
GasTipCap: big.NewInt(44),
GasFeeCap: big.NewInt(1045),
}),
NewTx(&DynamicFeeTx{
To: &to5,
Nonce: 5,
Value: big.NewInt(5),
Gas: 5,
GasTipCap: big.NewInt(56),
GasFeeCap: big.NewInt(1055),
}),
}
blockNumber = big.NewInt(1)
blockHash = core.BytesToHash([]byte{0x03, 0x14})
// Create the corresponding receipts
receipts = Receipts{
&Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: core.BytesToAddress([]byte{0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 0,
},
{
Address: core.BytesToAddress([]byte{0x01, 0x11}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 1,
},
},
// derived fields:
TxHash: txs[0].Hash(),
ContractAddress: core.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
GasUsed: 1,
EffectiveGasPrice: big.NewInt(11),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 0,
},
&Receipt{
PostState: core.Hash{2}.Bytes(),
CumulativeGasUsed: 3,
Logs: []*Log{
{
Address: core.BytesToAddress([]byte{0x22}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
Index: 2,
},
{
Address: core.BytesToAddress([]byte{0x02, 0x22}),
Topics: []core.Hash{core.HexToHash("dead"), core.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
Index: 3,
},
},
// derived fields:
TxHash: txs[1].Hash(),
GasUsed: 2,
EffectiveGasPrice: big.NewInt(22),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 1,
},
&Receipt{
Type: AccessListTxType,
PostState: core.Hash{3}.Bytes(),
CumulativeGasUsed: 6,
Logs: []*Log{},
// derived fields:
TxHash: txs[2].Hash(),
GasUsed: 3,
EffectiveGasPrice: big.NewInt(33),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 2,
},
&Receipt{
Type: DynamicFeeTxType,
PostState: core.Hash{4}.Bytes(),
CumulativeGasUsed: 10,
Logs: []*Log{},
// derived fields:
TxHash: txs[3].Hash(),
GasUsed: 4,
EffectiveGasPrice: big.NewInt(1044),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 3,
},
&Receipt{
Type: DynamicFeeTxType,
PostState: core.Hash{5}.Bytes(),
CumulativeGasUsed: 15,
Logs: []*Log{},
// derived fields:
TxHash: txs[4].Hash(),
GasUsed: 5,
EffectiveGasPrice: big.NewInt(1055),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 4,
},
}
)
func TestDecodeEmptyTypedReceipt(t *testing.T) {
input := []byte{0x80}
var r Receipt
@ -40,128 +256,59 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
// Tests that receipt data can be correctly derived from the contextual infos
func TestDeriveFields(t *testing.T) {
// Create a few transactions to have receipts for
to2 := core.HexToAddress("0x2")
to3 := core.HexToAddress("0x3")
txs := Transactions{
NewTx(&LegacyTx{
Nonce: 1,
Value: big.NewInt(1),
Gas: 1,
GasPrice: big.NewInt(1),
}),
NewTx(&LegacyTx{
To: &to2,
Nonce: 2,
Value: big.NewInt(2),
Gas: 2,
GasPrice: big.NewInt(2),
}),
NewTx(&AccessListTx{
To: &to3,
Nonce: 3,
Value: big.NewInt(3),
Gas: 3,
GasPrice: big.NewInt(3),
}),
}
// Create the corresponding receipts
receipts := Receipts{
&Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{Address: core.BytesToAddress([]byte{0x11})},
{Address: core.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: txs[0].Hash(),
ContractAddress: core.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: 1,
},
&Receipt{
PostState: core.Hash{2}.Bytes(),
CumulativeGasUsed: 3,
Logs: []*Log{
{Address: core.BytesToAddress([]byte{0x22})},
{Address: core.BytesToAddress([]byte{0x02, 0x22})},
},
TxHash: txs[1].Hash(),
ContractAddress: core.BytesToAddress([]byte{0x02, 0x22, 0x22}),
GasUsed: 2,
},
&Receipt{
Type: AccessListTxType,
PostState: core.Hash{3}.Bytes(),
CumulativeGasUsed: 6,
Logs: []*Log{
{Address: core.BytesToAddress([]byte{0x33})},
{Address: core.BytesToAddress([]byte{0x03, 0x33})},
},
TxHash: txs[2].Hash(),
ContractAddress: core.BytesToAddress([]byte{0x03, 0x33, 0x33}),
GasUsed: 3,
},
}
// Clear all the computed fields and re-derive them
number := big.NewInt(1)
hash := core.BytesToHash([]byte{0x03, 0x14})
clearComputedFieldsOnReceipts(t, receipts)
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil {
// Re-derive receipts.
basefee := big.NewInt(1000)
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), basefee, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}
// Iterate over all the computed fields and check that they're correct
signer := MakeSigner(params.TestChainConfig, number)
logIndex := uint(0)
// Check diff of receipts against derivedReceipts.
r1, err := json.MarshalIndent(receipts, "", " ")
if err != nil {
t.Fatal("error marshaling input receipts:", err)
}
r2, err := json.MarshalIndent(derivedReceipts, "", " ")
if err != nil {
t.Fatal("error marshaling derived receipts:", err)
}
d := diff.Diff(string(r1), string(r2))
if d != "" {
t.Fatal("receipts differ:", d)
}
}
// Test that we can marshal/unmarshal receipts to/from json without errors.
// This also confirms that our test receipts contain all the required fields.
func TestReceiptJSON(t *testing.T) {
for i := range receipts {
if receipts[i].Type != txs[i].Type() {
t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type())
b, err := receipts[i].MarshalJSON()
if err != nil {
t.Fatal("error marshaling receipt to json:", err)
}
if receipts[i].TxHash != txs[i].Hash() {
t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String())
r := Receipt{}
err = r.UnmarshalJSON(b)
if err != nil {
t.Fatal("error unmarshaling receipt from json:", err)
}
if receipts[i].BlockHash != hash {
t.Errorf("receipts[%d].BlockHash = %s, want %s", i, receipts[i].BlockHash.String(), hash.String())
}
if receipts[i].BlockNumber.Cmp(number) != 0 {
t.Errorf("receipts[%c].BlockNumber = %s, want %s", i, receipts[i].BlockNumber.String(), number.String())
}
if receipts[i].TransactionIndex != uint(i) {
t.Errorf("receipts[%d].TransactionIndex = %d, want %d", i, receipts[i].TransactionIndex, i)
}
if receipts[i].GasUsed != txs[i].Gas() {
t.Errorf("receipts[%d].GasUsed = %d, want %d", i, receipts[i].GasUsed, txs[i].Gas())
}
if txs[i].To() != nil && receipts[i].ContractAddress != (core.Address{}) {
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), (core.Address{}).String())
}
from, _ := Sender(signer, txs[i])
contractAddress := crypto.CreateAddress(from, txs[i].Nonce())
if txs[i].To() == nil && receipts[i].ContractAddress != contractAddress {
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), contractAddress.String())
}
for j := range receipts[i].Logs {
if receipts[i].Logs[j].BlockNumber != number.Uint64() {
t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64())
}
if receipts[i].Logs[j].BlockHash != hash {
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
}
if receipts[i].Logs[j].TxHash != txs[i].Hash() {
t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
}
if receipts[i].Logs[j].TxHash != txs[i].Hash() {
t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
}
if receipts[i].Logs[j].TxIndex != uint(i) {
t.Errorf("receipts[%d].Logs[%d].TransactionIndex = %d, want %d", i, j, receipts[i].Logs[j].TxIndex, i)
}
if receipts[i].Logs[j].Index != logIndex {
t.Errorf("receipts[%d].Logs[%d].Index = %d, want %d", i, j, receipts[i].Logs[j].Index, logIndex)
}
logIndex++
}
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
// though it is required per the spec.
func TestEffectiveGasPriceNotRequired(t *testing.T) {
r := *receipts[0]
r.EffectiveGasPrice = nil
b, err := r.MarshalJSON()
if err != nil {
t.Fatal("error marshaling receipt to json:", err)
}
r2 := Receipt{}
err = r2.UnmarshalJSON(b)
if err != nil {
t.Fatal("error unmarshaling receipt from json:", err)
}
}
@ -193,41 +340,135 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
}
}
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
t.Helper()
func TestReceiptMarshalBinary(t *testing.T) {
// Legacy Receipt
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
have, err := legacyReceipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
}
legacyReceipts := Receipts{legacyReceipt}
buf := new(bytes.Buffer)
legacyReceipts.EncodeIndex(0, buf)
haveEncodeIndex := buf.Bytes()
if !bytes.Equal(have, haveEncodeIndex) {
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
}
buf.Reset()
if err := legacyReceipt.EncodeRLP(buf); err != nil {
t.Fatalf("encode rlp error: %v", err)
}
haveRLPEncode := buf.Bytes()
if !bytes.Equal(have, haveRLPEncode) {
t.Errorf("BinaryMarshal and EncodeRLP mismatch for legacy tx, got %x want %x", have, haveRLPEncode)
}
legacyWant, _ := hex.DecodeString("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
if !bytes.Equal(have, legacyWant) {
t.Errorf("encoded RLP mismatch, got %x want %x", have, legacyWant)
}
for _, receipt := range receipts {
clearComputedFieldsOnReceipt(t, receipt)
// 2930 Receipt
buf.Reset()
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
have, err = accessListReceipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
}
accessListReceipts := Receipts{accessListReceipt}
accessListReceipts.EncodeIndex(0, buf)
haveEncodeIndex = buf.Bytes()
if !bytes.Equal(have, haveEncodeIndex) {
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
}
accessListWant, _ := hex.DecodeString("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
if !bytes.Equal(have, accessListWant) {
t.Errorf("encoded RLP mismatch, got %x want %x", have, accessListWant)
}
// 1559 Receipt
buf.Reset()
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
have, err = eip1559Receipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
}
eip1559Receipts := Receipts{eip1559Receipt}
eip1559Receipts.EncodeIndex(0, buf)
haveEncodeIndex = buf.Bytes()
if !bytes.Equal(have, haveEncodeIndex) {
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
}
eip1559Want, _ := hex.DecodeString("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
if !bytes.Equal(have, eip1559Want) {
t.Errorf("encoded RLP mismatch, got %x want %x", have, eip1559Want)
}
}
func clearComputedFieldsOnReceipt(t *testing.T, receipt *Receipt) {
t.Helper()
func TestReceiptUnmarshalBinary(t *testing.T) {
// Legacy Receipt
legacyBinary, _ := hex.DecodeString("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
gotLegacyReceipt := new(Receipt)
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt)
}
receipt.TxHash = core.Hash{}
receipt.BlockHash = core.Hash{}
receipt.BlockNumber = big.NewInt(math.MaxUint32)
receipt.TransactionIndex = math.MaxUint32
receipt.ContractAddress = core.Address{}
receipt.GasUsed = 0
// 2930 Receipt
accessListBinary, _ := hex.DecodeString("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
gotAccessListReceipt := new(Receipt)
if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt)
}
clearComputedFieldsOnLogs(t, receipt.Logs)
}
func clearComputedFieldsOnLogs(t *testing.T, logs []*Log) {
t.Helper()
for _, log := range logs {
clearComputedFieldsOnLog(t, log)
// 1559 Receipt
eip1559RctBinary, _ := hex.DecodeString("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
got1559Receipt := new(Receipt)
if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt)
}
}
func clearComputedFieldsOnLog(t *testing.T, log *Log) {
t.Helper()
log.BlockNumber = math.MaxUint32
log.BlockHash = core.Hash{}
log.TxHash = core.Hash{}
log.TxIndex = math.MaxUint32
log.Index = math.MaxUint32
func clearComputedFieldsOnReceipts(receipts []*Receipt) []*Receipt {
r := make([]*Receipt, len(receipts))
for i, receipt := range receipts {
r[i] = clearComputedFieldsOnReceipt(receipt)
}
return r
}
func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
cpy := *receipt
cpy.TxHash = core.Hash{0xff, 0xff, 0x11}
cpy.BlockHash = core.Hash{0xff, 0xff, 0x22}
cpy.BlockNumber = big.NewInt(math.MaxUint32)
cpy.TransactionIndex = math.MaxUint32
cpy.ContractAddress = core.Address{0xff, 0xff, 0x33}
cpy.GasUsed = 0xffffffff
cpy.Logs = clearComputedFieldsOnLogs(receipt.Logs)
return &cpy
}
func clearComputedFieldsOnLogs(logs []*Log) []*Log {
l := make([]*Log, len(logs))
for i, log := range logs {
cpy := *log
cpy.BlockNumber = math.MaxUint32
cpy.BlockHash = core.Hash{}
cpy.TxHash = core.Hash{}
cpy.TxIndex = math.MaxUint32
cpy.Index = math.MaxUint32
l[i] = &cpy
}
return l
}

View File

@ -89,9 +89,20 @@ type TxData interface {
rawSignatureValues() (v, r, s *big.Int)
setSignatureValues(chainID, v, r, s *big.Int)
// effectiveGasPrice computes the gas price paid by the transaction, given
// the inclusion block baseFee.
//
// Unlike other TxData methods, the returned *big.Int should be an independent
// copy of the computed value, i.e. callers are allowed to mutate the result.
// Method implementations can use 'dst' to store the result.
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
}
// EncodeRLP implements rlp.Encoder
// For a legacy Transaction this returns RLP([AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, V, R, S])
// For a EIP-2718 Transaction this returns RLP(TxType || TxPayload)
// For a EIP-2930 Transaction, TxType == 0x01 and TxPayload == RLP([ChainID, AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, AccessList, V, R, S]
func (tx *Transaction) EncodeRLP(w io.Writer) error {
if tx.Type() == LegacyTxType {
return rlp.Encode(w, tx.inner)
@ -112,9 +123,10 @@ func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
return rlp.Encode(w, tx.inner)
}
// MarshalBinary returns the canonical encoding of the transaction.
// For legacy transactions, it returns the RLP encoding. For EIP-2718 typed
// transactions, it returns the type and payload.
// MarshalBinary returns the canonical consensus encoding of the transaction.
// For a legacy Transaction this returns RLP([AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, V, R, S])
// For a EIP-2718 Transaction this returns TxType || TxPayload
// For a EIP-2930 Transaction, TxType == 0x01 and TxPayload == RLP([ChainID, AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, AccessList, V, R, S]
func (tx *Transaction) MarshalBinary() ([]byte, error) {
if tx.Type() == LegacyTxType {
return rlp.EncodeToBytes(tx.inner)
@ -135,10 +147,10 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
var inner LegacyTx
err := s.Decode(&inner)
if err == nil {
tx.setDecoded(&inner, int(rlp.ListSize(size)))
tx.setDecoded(&inner, rlp.ListSize(size))
}
return err
case kind == rlp.String:
default:
// It's an EIP-2718 typed TX envelope.
var b []byte
if b, err = s.Bytes(); err != nil {
@ -146,11 +158,9 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
}
inner, err := tx.decodeTyped(b)
if err == nil {
tx.setDecoded(inner, len(b))
tx.setDecoded(inner, uint64(len(b)))
}
return err
default:
return rlp.ErrExpectedList
}
}
@ -164,7 +174,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
if err != nil {
return err
}
tx.setDecoded(&data, len(b))
tx.setDecoded(&data, uint64(len(b)))
return nil
}
// It's an EIP2718 typed transaction envelope.
@ -172,13 +182,13 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
if err != nil {
return err
}
tx.setDecoded(inner, len(b))
tx.setDecoded(inner, uint64(len(b)))
return nil
}
// decodeTyped decodes a typed transaction from the canonical format.
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
if len(b) == 0 {
if len(b) <= 1 {
return nil, errEmptyTypedTx
}
switch b[0] {
@ -196,11 +206,11 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
}
// setDecoded sets the inner transaction and size after decoding.
func (tx *Transaction) setDecoded(inner TxData, size int) {
func (tx *Transaction) setDecoded(inner TxData, size uint64) {
tx.inner = inner
tx.time = time.Now()
if size > 0 {
tx.size.Store(float64(size))
tx.size.Store(size)
}
}
@ -288,13 +298,7 @@ func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
// To returns the recipient address of the transaction.
// For contract-creation transactions, To returns nil.
func (tx *Transaction) To() *core.Address {
// Copy the pointed-to address.
ito := tx.inner.to()
if ito == nil {
return nil
}
cpy := *ito
return &cpy
return copyAddressPtr(tx.inner.to())
}
// Cost returns gas * gasPrice + value.
@ -384,16 +388,21 @@ func (tx *Transaction) Hash() core.Hash {
return h
}
// Size returns the true RLP encoded storage size of the transaction, either by
// encoding and returning it, or returning a previously cached value.
func (tx *Transaction) Size() float64 {
// Size returns the true encoded storage size of the transaction, either by encoding
// and returning it, or returning a previously cached value.
func (tx *Transaction) Size() uint64 {
if size := tx.size.Load(); size != nil {
return size.(float64)
return size.(uint64)
}
c := writeCounter(0)
rlp.Encode(&c, &tx.inner)
tx.size.Store(float64(c))
return float64(c)
size := uint64(c)
if tx.Type() != LegacyTxType {
size += 1 // type byte
}
tx.size.Store(size)
return size
}
// WithSignature returns a new transaction with the given signature.
@ -444,6 +453,24 @@ func TxDifference(a, b Transactions) Transactions {
return keep
}
// HashDifference returns a new set which is the difference between a and b.
func HashDifference(a, b []core.Hash) []core.Hash {
keep := make([]core.Hash, 0, len(a))
remove := make(map[core.Hash]struct{})
for _, hash := range b {
remove[hash] = struct{}{}
}
for _, hash := range a {
if _, ok := remove[hash]; !ok {
keep = append(keep, hash)
}
}
return keep
}
// TxByNonce implements the sort interface to allow sorting a list of transactions
// by their nonces. This is usually only useful for sorting transactions from a
// single account, otherwise a nonce comparison doesn't make much sense.
@ -497,6 +524,7 @@ func (s *TxByPriceAndTime) Pop() interface{} {
old := *s
n := len(old)
x := old[n-1]
old[n-1] = nil
*s = old[0 : n-1]
return x
}
@ -569,70 +597,11 @@ func (t *TransactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads)
}
// Message is a fully derived transaction and implements core.Message
//
// NOTE: In a future PR this will be removed.
type Message struct {
to *core.Address
from core.Address
nonce uint64
amount *big.Int
gasLimit uint64
gasPrice *big.Int
gasFeeCap *big.Int
gasTipCap *big.Int
data []byte
accessList AccessList
checkNonce bool
}
func NewMessage(from core.Address, to *core.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, checkNonce bool) Message {
return Message{
from: from,
to: to,
nonce: nonce,
amount: amount,
gasLimit: gasLimit,
gasPrice: gasPrice,
gasFeeCap: gasFeeCap,
gasTipCap: gasTipCap,
data: data,
accessList: accessList,
checkNonce: checkNonce,
// copyAddressPtr copies an address.
func copyAddressPtr(a *core.Address) *core.Address {
if a == nil {
return nil
}
cpy := *a
return &cpy
}
// AsMessage returns the transaction as a core.Message.
func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
msg := Message{
nonce: tx.Nonce(),
gasLimit: tx.Gas(),
gasPrice: new(big.Int).Set(tx.GasPrice()),
gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
gasTipCap: new(big.Int).Set(tx.GasTipCap()),
to: tx.To(),
amount: tx.Value(),
data: tx.Data(),
accessList: tx.AccessList(),
checkNonce: true,
}
// If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil {
msg.gasPrice = bigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)
}
var err error
msg.from, err = Sender(s, tx)
return msg, err
}
func (m Message) From() core.Address { return m.from }
func (m Message) To() *core.Address { return m.to }
func (m Message) GasPrice() *big.Int { return m.gasPrice }
func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap }
func (m Message) GasTipCap() *big.Int { return m.gasTipCap }
func (m Message) Value() *big.Int { return m.amount }
func (m Message) Gas() uint64 { return m.gasLimit }
func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
func (m Message) AccessList() AccessList { return m.accessList }
func (m Message) CheckNonce() bool { return m.checkNonce }