mirror of https://github.com/ethereum/go-ethereum
all: add support for EIP-2718, EIP-2930 transactions (#21502)
This adds support for EIP-2718 typed transactions as well as EIP-2930 access list transactions (tx type 1). These EIPs are scheduled for the Berlin fork. There very few changes to existing APIs in core/types, and several new APIs to deal with access list transactions. In particular, there are two new constructor functions for transactions: types.NewTx and types.SignNewTx. Since the canonical encoding of typed transactions is not RLP-compatible, Transaction now has new methods for encoding and decoding: MarshalBinary and UnmarshalBinary. The existing EIP-155 signer does not support the new transaction types. All code dealing with transaction signatures should be updated to use the newer EIP-2930 signer. To make this easier for future updates, we have added new constructor functions for types.Signer: types.LatestSigner and types.LatestSignerForChainID. This change also adds support for the YoloV3 testnet. Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Felix Lange <fjl@twurst.com> Co-authored-by: Ryan Schneider <ryanleeschneider@gmail.com>pull/22380/head
parent
7a3c890009
commit
bbfb1e4008
@ -0,0 +1,11 @@ |
||||
{ |
||||
"0x000000000000000000000000000000000000aaaa": { |
||||
"balance": "0x03", |
||||
"code": "0x5854505854", |
||||
"nonce": "0x1" |
||||
}, |
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { |
||||
"balance": "0x100000", |
||||
"nonce": "0x00" |
||||
} |
||||
} |
@ -0,0 +1,7 @@ |
||||
{ |
||||
"currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", |
||||
"currentDifficulty": "0x20000", |
||||
"currentGasLimit": "0x1000000000", |
||||
"currentNumber": "0x1000000", |
||||
"currentTimestamp": "0x04" |
||||
} |
@ -0,0 +1,63 @@ |
||||
## EIP-2930 testing |
||||
|
||||
This test contains testcases for EIP-2930, which uses transactions with access lists. |
||||
|
||||
### Prestate |
||||
|
||||
The alloc portion contains one contract (`0x000000000000000000000000000000000000aaaa`), containing the |
||||
following code: `0x5854505854`: `PC ;SLOAD; POP; PC; SLOAD`. |
||||
|
||||
Essentialy, this contract does `SLOAD(0)` and `SLOAD(3)`. |
||||
|
||||
The alloc also contains some funds on `0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b`. |
||||
|
||||
## Transactions |
||||
|
||||
There are three transactions, each invokes the contract above. |
||||
|
||||
1. ACL-transaction, which contains some non-used slots |
||||
2. Regular transaction |
||||
3. ACL-transaction, which contains the slots `1` and `3` in `0x000000000000000000000000000000000000aaaa` |
||||
|
||||
## Execution |
||||
|
||||
Running it yields: |
||||
``` |
||||
dir=./testdata/8 && ./evm t8n --state.fork=Berlin --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --trace && cat trace-* | grep SLOAD |
||||
{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":4,"op":84,"gas":"0x47c86","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":1,"op":84,"gas":"0x49cf6","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":4,"op":84,"gas":"0x494be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":4,"op":84,"gas":"0x48456","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
|
||||
``` |
||||
|
||||
Simlarly, we can provide the input transactions via `stdin` instead of as file: |
||||
|
||||
``` |
||||
dir=./testdata/8 \ |
||||
&& cat $dir/txs.json | jq "{txs: .}" \ |
||||
| ./evm t8n --state.fork=Berlin \ |
||||
--input.alloc=$dir/alloc.json \ |
||||
--input.txs=stdin \ |
||||
--input.env=$dir/env.json \ |
||||
--trace \ |
||||
&& cat trace-* | grep SLOAD |
||||
|
||||
{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":4,"op":84,"gas":"0x47c86","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":1,"op":84,"gas":"0x49cf6","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":4,"op":84,"gas":"0x494be","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":1,"op":84,"gas":"0x484be","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
{"pc":4,"op":84,"gas":"0x48456","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x3"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} |
||||
``` |
||||
|
||||
If we try to execute it on older rules: |
||||
``` |
||||
dir=./testdata/8 && ./evm t8n --state.fork=Istanbul --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json |
||||
INFO [01-21|23:21:51.265] rejected tx index=0 hash="d2818d…6ab3da" error="tx type not supported" |
||||
INFO [01-21|23:21:51.265] rejected tx index=1 hash="26ea00…81c01b" from=0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B error="nonce too high: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B, tx: 1 state: 0" |
||||
INFO [01-21|23:21:51.265] rejected tx index=2 hash="698d01…369cee" error="tx type not supported" |
||||
``` |
||||
Number `1` and `3` are not applicable, and therefore number `2` has wrong nonce. |
@ -0,0 +1,58 @@ |
||||
[ |
||||
{ |
||||
"gas": "0x4ef00", |
||||
"gasPrice": "0x1", |
||||
"chainId": "0x1", |
||||
"input": "0x", |
||||
"nonce": "0x0", |
||||
"to": "0x000000000000000000000000000000000000aaaa", |
||||
"value": "0x1", |
||||
"type" : "0x1", |
||||
"accessList": [ |
||||
{"address": "0x0000000000000000000000000000000000000000", |
||||
"storageKeys": [ |
||||
"0x0000000000000000000000000000000000000000000000000000000000000000", |
||||
"0x0000000000000000000000000000000000000000000000000000000000000000" |
||||
] |
||||
} |
||||
], |
||||
"v": "0x0", |
||||
"r": "0x0", |
||||
"s": "0x0", |
||||
"secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" |
||||
}, |
||||
{ |
||||
"gas": "0x4ef00", |
||||
"gasPrice": "0x1", |
||||
"input": "0x", |
||||
"nonce": "0x1", |
||||
"to": "0x000000000000000000000000000000000000aaaa", |
||||
"value": "0x2", |
||||
"v": "0x0", |
||||
"r": "0x0", |
||||
"s": "0x0", |
||||
"secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" |
||||
}, |
||||
{ |
||||
"gas": "0x4ef00", |
||||
"gasPrice": "0x1", |
||||
"chainId": "0x1", |
||||
"input": "0x", |
||||
"nonce": "0x2", |
||||
"to": "0x000000000000000000000000000000000000aaaa", |
||||
"value": "0x1", |
||||
"type" : "0x1", |
||||
"accessList": [ |
||||
{"address": "0x000000000000000000000000000000000000aaaa", |
||||
"storageKeys": [ |
||||
"0x0000000000000000000000000000000000000000000000000000000000000000", |
||||
"0x0000000000000000000000000000000000000000000000000000000000000003" |
||||
] |
||||
} |
||||
], |
||||
"v": "0x0", |
||||
"r": "0x0", |
||||
"s": "0x0", |
||||
"secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" |
||||
} |
||||
] |
@ -0,0 +1,115 @@ |
||||
// Copyright 2020 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
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types |
||||
|
||||
import ( |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
) |
||||
|
||||
//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
|
||||
|
||||
// AccessList is an EIP-2930 access list.
|
||||
type AccessList []AccessTuple |
||||
|
||||
// AccessTuple is the element type of an access list.
|
||||
type AccessTuple struct { |
||||
Address common.Address `json:"address" gencodec:"required"` |
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` |
||||
} |
||||
|
||||
// StorageKeys returns the total number of storage keys in the access list.
|
||||
func (al AccessList) StorageKeys() int { |
||||
sum := 0 |
||||
for _, tuple := range al { |
||||
sum += len(tuple.StorageKeys) |
||||
} |
||||
return sum |
||||
} |
||||
|
||||
// AccessListTx is the data of EIP-2930 access list transactions.
|
||||
type AccessListTx struct { |
||||
ChainID *big.Int // destination chain ID
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
AccessList AccessList // EIP-2930 access list
|
||||
V, R, S *big.Int // signature values
|
||||
} |
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *AccessListTx) copy() TxData { |
||||
cpy := &AccessListTx{ |
||||
Nonce: tx.Nonce, |
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
Data: common.CopyBytes(tx.Data), |
||||
Gas: tx.Gas, |
||||
// These are copied below.
|
||||
AccessList: make(AccessList, len(tx.AccessList)), |
||||
Value: new(big.Int), |
||||
ChainID: new(big.Int), |
||||
GasPrice: new(big.Int), |
||||
V: new(big.Int), |
||||
R: new(big.Int), |
||||
S: new(big.Int), |
||||
} |
||||
copy(cpy.AccessList, tx.AccessList) |
||||
if tx.Value != nil { |
||||
cpy.Value.Set(tx.Value) |
||||
} |
||||
if tx.ChainID != nil { |
||||
cpy.ChainID.Set(tx.ChainID) |
||||
} |
||||
if tx.GasPrice != nil { |
||||
cpy.GasPrice.Set(tx.GasPrice) |
||||
} |
||||
if tx.V != nil { |
||||
cpy.V.Set(tx.V) |
||||
} |
||||
if tx.R != nil { |
||||
cpy.R.Set(tx.R) |
||||
} |
||||
if tx.S != nil { |
||||
cpy.S.Set(tx.S) |
||||
} |
||||
return cpy |
||||
} |
||||
|
||||
// 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 } |
||||
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } |
||||
func (tx *AccessListTx) value() *big.Int { return tx.Value } |
||||
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } |
||||
func (tx *AccessListTx) to() *common.Address { return tx.To } |
||||
|
||||
func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) { |
||||
return tx.V, tx.R, tx.S |
||||
} |
||||
|
||||
func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) { |
||||
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s |
||||
} |
@ -1,58 +0,0 @@ |
||||
// Copyright 2014 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
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types |
||||
|
||||
import ( |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
) |
||||
|
||||
type DerivableList interface { |
||||
Len() int |
||||
GetRlp(i int) []byte |
||||
} |
||||
|
||||
// Hasher is the tool used to calculate the hash of derivable list.
|
||||
type Hasher interface { |
||||
Reset() |
||||
Update([]byte, []byte) |
||||
Hash() common.Hash |
||||
} |
||||
|
||||
func DeriveSha(list DerivableList, hasher Hasher) common.Hash { |
||||
hasher.Reset() |
||||
|
||||
// StackTrie requires values to be inserted in increasing
|
||||
// hash order, which is not the order that `list` provides
|
||||
// hashes in. This insertion sequence ensures that the
|
||||
// order is correct.
|
||||
|
||||
var buf []byte |
||||
for i := 1; i < list.Len() && i <= 0x7f; i++ { |
||||
buf = rlp.AppendUint64(buf[:0], uint64(i)) |
||||
hasher.Update(buf, list.GetRlp(i)) |
||||
} |
||||
if list.Len() > 0 { |
||||
buf = rlp.AppendUint64(buf[:0], 0) |
||||
hasher.Update(buf, list.GetRlp(0)) |
||||
} |
||||
for i := 0x80; i < list.Len(); i++ { |
||||
buf = rlp.AppendUint64(buf[:0], uint64(i)) |
||||
hasher.Update(buf, list.GetRlp(i)) |
||||
} |
||||
return hasher.Hash() |
||||
} |
@ -0,0 +1,43 @@ |
||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
) |
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (a AccessTuple) MarshalJSON() ([]byte, error) { |
||||
type AccessTuple struct { |
||||
Address common.Address `json:"address" gencodec:"required"` |
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` |
||||
} |
||||
var enc AccessTuple |
||||
enc.Address = a.Address |
||||
enc.StorageKeys = a.StorageKeys |
||||
return json.Marshal(&enc) |
||||
} |
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (a *AccessTuple) UnmarshalJSON(input []byte) error { |
||||
type AccessTuple struct { |
||||
Address *common.Address `json:"address" gencodec:"required"` |
||||
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` |
||||
} |
||||
var dec AccessTuple |
||||
if err := json.Unmarshal(input, &dec); err != nil { |
||||
return err |
||||
} |
||||
if dec.Address == nil { |
||||
return errors.New("missing required field 'address' for AccessTuple") |
||||
} |
||||
a.Address = *dec.Address |
||||
if dec.StorageKeys == nil { |
||||
return errors.New("missing required field 'storageKeys' for AccessTuple") |
||||
} |
||||
a.StorageKeys = dec.StorageKeys |
||||
return nil |
||||
} |
@ -1,101 +0,0 @@ |
||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/common/hexutil" |
||||
) |
||||
|
||||
var _ = (*txdataMarshaling)(nil) |
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (t txdata) MarshalJSON() ([]byte, error) { |
||||
type txdata struct { |
||||
AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"` |
||||
Price *hexutil.Big `json:"gasPrice" gencodec:"required"` |
||||
GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"` |
||||
Recipient *common.Address `json:"to" rlp:"nil"` |
||||
Amount *hexutil.Big `json:"value" gencodec:"required"` |
||||
Payload hexutil.Bytes `json:"input" gencodec:"required"` |
||||
V *hexutil.Big `json:"v" gencodec:"required"` |
||||
R *hexutil.Big `json:"r" gencodec:"required"` |
||||
S *hexutil.Big `json:"s" gencodec:"required"` |
||||
Hash *common.Hash `json:"hash" rlp:"-"` |
||||
} |
||||
var enc txdata |
||||
enc.AccountNonce = hexutil.Uint64(t.AccountNonce) |
||||
enc.Price = (*hexutil.Big)(t.Price) |
||||
enc.GasLimit = hexutil.Uint64(t.GasLimit) |
||||
enc.Recipient = t.Recipient |
||||
enc.Amount = (*hexutil.Big)(t.Amount) |
||||
enc.Payload = t.Payload |
||||
enc.V = (*hexutil.Big)(t.V) |
||||
enc.R = (*hexutil.Big)(t.R) |
||||
enc.S = (*hexutil.Big)(t.S) |
||||
enc.Hash = t.Hash |
||||
return json.Marshal(&enc) |
||||
} |
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (t *txdata) UnmarshalJSON(input []byte) error { |
||||
type txdata struct { |
||||
AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"` |
||||
Price *hexutil.Big `json:"gasPrice" gencodec:"required"` |
||||
GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"` |
||||
Recipient *common.Address `json:"to" rlp:"nil"` |
||||
Amount *hexutil.Big `json:"value" gencodec:"required"` |
||||
Payload *hexutil.Bytes `json:"input" gencodec:"required"` |
||||
V *hexutil.Big `json:"v" gencodec:"required"` |
||||
R *hexutil.Big `json:"r" gencodec:"required"` |
||||
S *hexutil.Big `json:"s" gencodec:"required"` |
||||
Hash *common.Hash `json:"hash" rlp:"-"` |
||||
} |
||||
var dec txdata |
||||
if err := json.Unmarshal(input, &dec); err != nil { |
||||
return err |
||||
} |
||||
if dec.AccountNonce == nil { |
||||
return errors.New("missing required field 'nonce' for txdata") |
||||
} |
||||
t.AccountNonce = uint64(*dec.AccountNonce) |
||||
if dec.Price == nil { |
||||
return errors.New("missing required field 'gasPrice' for txdata") |
||||
} |
||||
t.Price = (*big.Int)(dec.Price) |
||||
if dec.GasLimit == nil { |
||||
return errors.New("missing required field 'gas' for txdata") |
||||
} |
||||
t.GasLimit = uint64(*dec.GasLimit) |
||||
if dec.Recipient != nil { |
||||
t.Recipient = dec.Recipient |
||||
} |
||||
if dec.Amount == nil { |
||||
return errors.New("missing required field 'value' for txdata") |
||||
} |
||||
t.Amount = (*big.Int)(dec.Amount) |
||||
if dec.Payload == nil { |
||||
return errors.New("missing required field 'input' for txdata") |
||||
} |
||||
t.Payload = *dec.Payload |
||||
if dec.V == nil { |
||||
return errors.New("missing required field 'v' for txdata") |
||||
} |
||||
t.V = (*big.Int)(dec.V) |
||||
if dec.R == nil { |
||||
return errors.New("missing required field 'r' for txdata") |
||||
} |
||||
t.R = (*big.Int)(dec.R) |
||||
if dec.S == nil { |
||||
return errors.New("missing required field 's' for txdata") |
||||
} |
||||
t.S = (*big.Int)(dec.S) |
||||
if dec.Hash != nil { |
||||
t.Hash = dec.Hash |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,112 @@ |
||||
// Copyright 2014 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
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types |
||||
|
||||
import ( |
||||
"bytes" |
||||
"sync" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"golang.org/x/crypto/sha3" |
||||
) |
||||
|
||||
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
||||
var hasherPool = sync.Pool{ |
||||
New: func() interface{} { return sha3.NewLegacyKeccak256() }, |
||||
} |
||||
|
||||
// deriveBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||
var encodeBufferPool = sync.Pool{ |
||||
New: func() interface{} { return new(bytes.Buffer) }, |
||||
} |
||||
|
||||
func rlpHash(x interface{}) (h common.Hash) { |
||||
sha := hasherPool.Get().(crypto.KeccakState) |
||||
defer hasherPool.Put(sha) |
||||
sha.Reset() |
||||
rlp.Encode(sha, x) |
||||
sha.Read(h[:]) |
||||
return h |
||||
} |
||||
|
||||
// prefixedRlpHash writes the prefix into the hasher before rlp-encoding the
|
||||
// given interface. It's used for typed transactions.
|
||||
func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) { |
||||
sha := hasherPool.Get().(crypto.KeccakState) |
||||
defer hasherPool.Put(sha) |
||||
sha.Reset() |
||||
sha.Write([]byte{prefix}) |
||||
rlp.Encode(sha, x) |
||||
sha.Read(h[:]) |
||||
return h |
||||
} |
||||
|
||||
// TrieHasher is the tool used to calculate the hash of derivable list.
|
||||
// This is internal, do not use.
|
||||
type TrieHasher interface { |
||||
Reset() |
||||
Update([]byte, []byte) |
||||
Hash() common.Hash |
||||
} |
||||
|
||||
// DerivableList is the input to DeriveSha.
|
||||
// It is implemented by the 'Transactions' and 'Receipts' types.
|
||||
// This is internal, do not use these methods.
|
||||
type DerivableList interface { |
||||
Len() int |
||||
EncodeIndex(int, *bytes.Buffer) |
||||
} |
||||
|
||||
func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte { |
||||
buf.Reset() |
||||
list.EncodeIndex(i, buf) |
||||
// It's really unfortunate that we need to do perform this copy.
|
||||
// StackTrie holds onto the values until Hash is called, so the values
|
||||
// written to it must not alias.
|
||||
return common.CopyBytes(buf.Bytes()) |
||||
} |
||||
|
||||
// DeriveSha creates the tree hashes of transactions and receipts in a block header.
|
||||
func DeriveSha(list DerivableList, hasher TrieHasher) common.Hash { |
||||
hasher.Reset() |
||||
|
||||
valueBuf := encodeBufferPool.Get().(*bytes.Buffer) |
||||
defer encodeBufferPool.Put(valueBuf) |
||||
|
||||
// StackTrie requires values to be inserted in increasing hash order, which is not the
|
||||
// order that `list` provides hashes in. This insertion sequence ensures that the
|
||||
// order is correct.
|
||||
var indexBuf []byte |
||||
for i := 1; i < list.Len() && i <= 0x7f; i++ { |
||||
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) |
||||
value := encodeForDerive(list, i, valueBuf) |
||||
hasher.Update(indexBuf, value) |
||||
} |
||||
if list.Len() > 0 { |
||||
indexBuf = rlp.AppendUint64(indexBuf[:0], 0) |
||||
value := encodeForDerive(list, 0, valueBuf) |
||||
hasher.Update(indexBuf, value) |
||||
} |
||||
for i := 0x80; i < list.Len(); i++ { |
||||
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) |
||||
value := encodeForDerive(list, i, valueBuf) |
||||
hasher.Update(indexBuf, value) |
||||
} |
||||
return hasher.Hash() |
||||
} |
@ -0,0 +1,212 @@ |
||||
package types_test |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"io" |
||||
"math/big" |
||||
mrand "math/rand" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/common/hexutil" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/ethereum/go-ethereum/trie" |
||||
) |
||||
|
||||
func TestDeriveSha(t *testing.T) { |
||||
txs, err := genTxs(0) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
for len(txs) < 1000 { |
||||
exp := types.DeriveSha(txs, new(trie.Trie)) |
||||
got := types.DeriveSha(txs, trie.NewStackTrie(nil)) |
||||
if !bytes.Equal(got[:], exp[:]) { |
||||
t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp) |
||||
} |
||||
newTxs, err := genTxs(uint64(len(txs) + 1)) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
txs = append(txs, newTxs...) |
||||
} |
||||
} |
||||
|
||||
// TestEIP2718DeriveSha tests that the input to the DeriveSha function is correct.
|
||||
func TestEIP2718DeriveSha(t *testing.T) { |
||||
for _, tc := range []struct { |
||||
rlpData string |
||||
exp string |
||||
}{ |
||||
{ |
||||
rlpData: "0xb8a701f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9", |
||||
exp: "01 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n80 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n", |
||||
}, |
||||
} { |
||||
d := &hashToHumanReadable{} |
||||
var t1, t2 types.Transaction |
||||
rlp.DecodeBytes(common.FromHex(tc.rlpData), &t1) |
||||
rlp.DecodeBytes(common.FromHex(tc.rlpData), &t2) |
||||
txs := types.Transactions{&t1, &t2} |
||||
types.DeriveSha(txs, d) |
||||
if tc.exp != string(d.data) { |
||||
t.Fatalf("Want\n%v\nhave:\n%v", tc.exp, string(d.data)) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func BenchmarkDeriveSha200(b *testing.B) { |
||||
txs, err := genTxs(200) |
||||
if err != nil { |
||||
b.Fatal(err) |
||||
} |
||||
var exp common.Hash |
||||
var got common.Hash |
||||
b.Run("std_trie", func(b *testing.B) { |
||||
b.ResetTimer() |
||||
b.ReportAllocs() |
||||
for i := 0; i < b.N; i++ { |
||||
exp = types.DeriveSha(txs, new(trie.Trie)) |
||||
} |
||||
}) |
||||
|
||||
b.Run("stack_trie", func(b *testing.B) { |
||||
b.ResetTimer() |
||||
b.ReportAllocs() |
||||
for i := 0; i < b.N; i++ { |
||||
got = types.DeriveSha(txs, trie.NewStackTrie(nil)) |
||||
} |
||||
}) |
||||
if got != exp { |
||||
b.Errorf("got %x exp %x", got, exp) |
||||
} |
||||
} |
||||
|
||||
func TestFuzzDeriveSha(t *testing.T) { |
||||
// increase this for longer runs -- it's set to quite low for travis
|
||||
rndSeed := mrand.Int() |
||||
for i := 0; i < 10; i++ { |
||||
seed := rndSeed + i |
||||
exp := types.DeriveSha(newDummy(i), new(trie.Trie)) |
||||
got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil)) |
||||
if !bytes.Equal(got[:], exp[:]) { |
||||
printList(newDummy(seed)) |
||||
t.Fatalf("seed %d: got %x exp %x", seed, got, exp) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// TestDerivableList contains testcases found via fuzzing
|
||||
func TestDerivableList(t *testing.T) { |
||||
type tcase []string |
||||
tcs := []tcase{ |
||||
{ |
||||
"0xc041", |
||||
}, |
||||
{ |
||||
"0xf04cf757812428b0763112efb33b6f4fad7deb445e", |
||||
"0xf04cf757812428b0763112efb33b6f4fad7deb445e", |
||||
}, |
||||
{ |
||||
"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", |
||||
"0x6cd850eca0a7ac46bb1748d7b9cb88aa3bd21c57d852c28198ad8fa422c4595032e88a4494b4778b36b944fe47a52b8c5cd312910139dfcb4147ab8e972cc456bcb063f25dd78f54c4d34679e03142c42c662af52947d45bdb6e555751334ace76a5080ab5a0256a1d259855dfc5c0b8023b25befbb13fd3684f9f755cbd3d63544c78ee2001452dd54633a7593ade0b183891a0a4e9c7844e1254005fbe592b1b89149a502c24b6e1dca44c158aebedf01beae9c30cabe16a", |
||||
"0x14abd5c47c0be87b0454596baad2", |
||||
"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", |
||||
}, |
||||
} |
||||
for i, tc := range tcs[1:] { |
||||
exp := types.DeriveSha(flatList(tc), new(trie.Trie)) |
||||
got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil)) |
||||
if !bytes.Equal(got[:], exp[:]) { |
||||
t.Fatalf("case %d: got %x exp %x", i, got, exp) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func genTxs(num uint64) (types.Transactions, error) { |
||||
key, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
var addr = crypto.PubkeyToAddress(key.PublicKey) |
||||
newTx := func(i uint64) (*types.Transaction, error) { |
||||
signer := types.NewEIP155Signer(big.NewInt(18)) |
||||
utx := types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil) |
||||
tx, err := types.SignTx(utx, signer, key) |
||||
return tx, err |
||||
} |
||||
var txs types.Transactions |
||||
for i := uint64(0); i < num; i++ { |
||||
tx, err := newTx(i) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
txs = append(txs, tx) |
||||
} |
||||
return txs, nil |
||||
} |
||||
|
||||
type dummyDerivableList struct { |
||||
len int |
||||
seed int |
||||
} |
||||
|
||||
func newDummy(seed int) *dummyDerivableList { |
||||
d := &dummyDerivableList{} |
||||
src := mrand.NewSource(int64(seed)) |
||||
// don't use lists longer than 4K items
|
||||
d.len = int(src.Int63() & 0x0FFF) |
||||
d.seed = seed |
||||
return d |
||||
} |
||||
|
||||
func (d *dummyDerivableList) Len() int { |
||||
return d.len |
||||
} |
||||
|
||||
func (d *dummyDerivableList) EncodeIndex(i int, w *bytes.Buffer) { |
||||
src := mrand.NewSource(int64(d.seed + i)) |
||||
// max item size 256, at least 1 byte per item
|
||||
size := 1 + src.Int63()&0x00FF |
||||
io.CopyN(w, mrand.New(src), size) |
||||
} |
||||
|
||||
func printList(l types.DerivableList) { |
||||
fmt.Printf("list length: %d\n", l.Len()) |
||||
fmt.Printf("{\n") |
||||
for i := 0; i < l.Len(); i++ { |
||||
var buf bytes.Buffer |
||||
l.EncodeIndex(i, &buf) |
||||
fmt.Printf("\"0x%x\",\n", buf.Bytes()) |
||||
} |
||||
fmt.Printf("},\n") |
||||
} |
||||
|
||||
type flatList []string |
||||
|
||||
func (f flatList) Len() int { |
||||
return len(f) |
||||
} |
||||
func (f flatList) EncodeIndex(i int, w *bytes.Buffer) { |
||||
w.Write(hexutil.MustDecode(f[i])) |
||||
} |
||||
|
||||
type hashToHumanReadable struct { |
||||
data []byte |
||||
} |
||||
|
||||
func (d *hashToHumanReadable) Reset() { |
||||
d.data = make([]byte, 0) |
||||
} |
||||
|
||||
func (d *hashToHumanReadable) Update(i []byte, i2 []byte) { |
||||
l := fmt.Sprintf("%x %x\n", i, i2) |
||||
d.data = append(d.data, []byte(l)...) |
||||
} |
||||
|
||||
func (d *hashToHumanReadable) Hash() common.Hash { |
||||
return common.Hash{} |
||||
} |
@ -0,0 +1,111 @@ |
||||
// Copyright 2020 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
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types |
||||
|
||||
import ( |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
) |
||||
|
||||
// LegacyTx is the transaction data of regular Ethereum transactions.
|
||||
type LegacyTx struct { |
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
V, R, S *big.Int // signature values
|
||||
} |
||||
|
||||
// NewTransaction creates an unsigned legacy transaction.
|
||||
// Deprecated: use NewTx instead.
|
||||
func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { |
||||
return NewTx(&LegacyTx{ |
||||
Nonce: nonce, |
||||
To: &to, |
||||
Value: amount, |
||||
Gas: gasLimit, |
||||
GasPrice: gasPrice, |
||||
Data: data, |
||||
}) |
||||
} |
||||
|
||||
// NewContractCreation creates an unsigned legacy transaction.
|
||||
// Deprecated: use NewTx instead.
|
||||
func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { |
||||
return NewTx(&LegacyTx{ |
||||
Nonce: nonce, |
||||
Value: amount, |
||||
Gas: gasLimit, |
||||
GasPrice: gasPrice, |
||||
Data: data, |
||||
}) |
||||
} |
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *LegacyTx) copy() TxData { |
||||
cpy := &LegacyTx{ |
||||
Nonce: tx.Nonce, |
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
Data: common.CopyBytes(tx.Data), |
||||
Gas: tx.Gas, |
||||
// These are initialized below.
|
||||
Value: new(big.Int), |
||||
GasPrice: new(big.Int), |
||||
V: new(big.Int), |
||||
R: new(big.Int), |
||||
S: new(big.Int), |
||||
} |
||||
if tx.Value != nil { |
||||
cpy.Value.Set(tx.Value) |
||||
} |
||||
if tx.GasPrice != nil { |
||||
cpy.GasPrice.Set(tx.GasPrice) |
||||
} |
||||
if tx.V != nil { |
||||
cpy.V.Set(tx.V) |
||||
} |
||||
if tx.R != nil { |
||||
cpy.R.Set(tx.R) |
||||
} |
||||
if tx.S != nil { |
||||
cpy.S.Set(tx.S) |
||||
} |
||||
return cpy |
||||
} |
||||
|
||||
// accessors for innerTx.
|
||||
|
||||
func (tx *LegacyTx) txType() byte { return LegacyTxType } |
||||
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) } |
||||
func (tx *LegacyTx) accessList() AccessList { return nil } |
||||
func (tx *LegacyTx) data() []byte { return tx.Data } |
||||
func (tx *LegacyTx) gas() uint64 { return tx.Gas } |
||||
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } |
||||
func (tx *LegacyTx) value() *big.Int { return tx.Value } |
||||
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } |
||||
func (tx *LegacyTx) to() *common.Address { return tx.To } |
||||
|
||||
func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) { |
||||
return tx.V, tx.R, tx.S |
||||
} |
||||
|
||||
func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) { |
||||
tx.V, tx.R, tx.S = v, r, s |
||||
} |
@ -0,0 +1,187 @@ |
||||
package types |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/common/hexutil" |
||||
) |
||||
|
||||
// txJSON is the JSON representation of transactions.
|
||||
type txJSON struct { |
||||
Type hexutil.Uint64 `json:"type"` |
||||
|
||||
// Common transaction fields:
|
||||
Nonce *hexutil.Uint64 `json:"nonce"` |
||||
GasPrice *hexutil.Big `json:"gasPrice"` |
||||
Gas *hexutil.Uint64 `json:"gas"` |
||||
Value *hexutil.Big `json:"value"` |
||||
Data *hexutil.Bytes `json:"input"` |
||||
V *hexutil.Big `json:"v"` |
||||
R *hexutil.Big `json:"r"` |
||||
S *hexutil.Big `json:"s"` |
||||
To *common.Address `json:"to"` |
||||
|
||||
// Access list transaction fields:
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"` |
||||
AccessList *AccessList `json:"accessList,omitempty"` |
||||
|
||||
// Only used for encoding:
|
||||
Hash common.Hash `json:"hash"` |
||||
} |
||||
|
||||
// MarshalJSON marshals as JSON with a hash.
|
||||
func (t *Transaction) MarshalJSON() ([]byte, error) { |
||||
var enc txJSON |
||||
// These are set for all tx types.
|
||||
enc.Hash = t.Hash() |
||||
enc.Type = hexutil.Uint64(t.Type()) |
||||
|
||||
// Other fields are set conditionally depending on tx type.
|
||||
switch tx := t.inner.(type) { |
||||
case *LegacyTx: |
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) |
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas) |
||||
enc.GasPrice = (*hexutil.Big)(tx.GasPrice) |
||||
enc.Value = (*hexutil.Big)(tx.Value) |
||||
enc.Data = (*hexutil.Bytes)(&tx.Data) |
||||
enc.To = t.To() |
||||
enc.V = (*hexutil.Big)(tx.V) |
||||
enc.R = (*hexutil.Big)(tx.R) |
||||
enc.S = (*hexutil.Big)(tx.S) |
||||
case *AccessListTx: |
||||
enc.ChainID = (*hexutil.Big)(tx.ChainID) |
||||
enc.AccessList = &tx.AccessList |
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) |
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas) |
||||
enc.GasPrice = (*hexutil.Big)(tx.GasPrice) |
||||
enc.Value = (*hexutil.Big)(tx.Value) |
||||
enc.Data = (*hexutil.Bytes)(&tx.Data) |
||||
enc.To = t.To() |
||||
enc.V = (*hexutil.Big)(tx.V) |
||||
enc.R = (*hexutil.Big)(tx.R) |
||||
enc.S = (*hexutil.Big)(tx.S) |
||||
} |
||||
return json.Marshal(&enc) |
||||
} |
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (t *Transaction) UnmarshalJSON(input []byte) error { |
||||
var dec txJSON |
||||
if err := json.Unmarshal(input, &dec); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Decode / verify fields according to transaction type.
|
||||
var inner TxData |
||||
switch dec.Type { |
||||
case LegacyTxType: |
||||
var itx LegacyTx |
||||
inner = &itx |
||||
if dec.To != nil { |
||||
itx.To = dec.To |
||||
} |
||||
if dec.Nonce == nil { |
||||
return errors.New("missing required field 'nonce' in transaction") |
||||
} |
||||
itx.Nonce = uint64(*dec.Nonce) |
||||
if dec.GasPrice == nil { |
||||
return errors.New("missing required field 'gasPrice' in transaction") |
||||
} |
||||
itx.GasPrice = (*big.Int)(dec.GasPrice) |
||||
if dec.Gas == nil { |
||||
return errors.New("missing required field 'gas' in transaction") |
||||
} |
||||
itx.Gas = uint64(*dec.Gas) |
||||
if dec.Value == nil { |
||||
return errors.New("missing required field 'value' in transaction") |
||||
} |
||||
itx.Value = (*big.Int)(dec.Value) |
||||
if dec.Data == nil { |
||||
return errors.New("missing required field 'input' in transaction") |
||||
} |
||||
itx.Data = *dec.Data |
||||
if dec.V == nil { |
||||
return errors.New("missing required field 'v' in transaction") |
||||
} |
||||
itx.V = (*big.Int)(dec.V) |
||||
if dec.R == nil { |
||||
return errors.New("missing required field 'r' in transaction") |
||||
} |
||||
itx.R = (*big.Int)(dec.R) |
||||
if dec.S == nil { |
||||
return errors.New("missing required field 's' in transaction") |
||||
} |
||||
itx.S = (*big.Int)(dec.S) |
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 |
||||
if withSignature { |
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
case AccessListTxType: |
||||
var itx AccessListTx |
||||
inner = &itx |
||||
// Access list is optional for now.
|
||||
if dec.AccessList != nil { |
||||
itx.AccessList = *dec.AccessList |
||||
} |
||||
if dec.ChainID == nil { |
||||
return errors.New("missing required field 'chainId' in transaction") |
||||
} |
||||
itx.ChainID = (*big.Int)(dec.ChainID) |
||||
if dec.To != nil { |
||||
itx.To = dec.To |
||||
} |
||||
if dec.Nonce == nil { |
||||
return errors.New("missing required field 'nonce' in transaction") |
||||
} |
||||
itx.Nonce = uint64(*dec.Nonce) |
||||
if dec.GasPrice == nil { |
||||
return errors.New("missing required field 'gasPrice' in transaction") |
||||
} |
||||
itx.GasPrice = (*big.Int)(dec.GasPrice) |
||||
if dec.Gas == nil { |
||||
return errors.New("missing required field 'gas' in transaction") |
||||
} |
||||
itx.Gas = uint64(*dec.Gas) |
||||
if dec.Value == nil { |
||||
return errors.New("missing required field 'value' in transaction") |
||||
} |
||||
itx.Value = (*big.Int)(dec.Value) |
||||
if dec.Data == nil { |
||||
return errors.New("missing required field 'input' in transaction") |
||||
} |
||||
itx.Data = *dec.Data |
||||
if dec.V == nil { |
||||
return errors.New("missing required field 'v' in transaction") |
||||
} |
||||
itx.V = (*big.Int)(dec.V) |
||||
if dec.R == nil { |
||||
return errors.New("missing required field 'r' in transaction") |
||||
} |
||||
itx.R = (*big.Int)(dec.R) |
||||
if dec.S == nil { |
||||
return errors.New("missing required field 's' in transaction") |
||||
} |
||||
itx.S = (*big.Int)(dec.S) |
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 |
||||
if withSignature { |
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
default: |
||||
return ErrTxTypeNotSupported |
||||
} |
||||
|
||||
// Now set the inner transaction.
|
||||
t.setDecoded(inner, 0) |
||||
|
||||
// TODO: check hash here?
|
||||
return nil |
||||
} |
Loading…
Reference in new issue