Skip to content

Commit bbfb1e4

Browse files
lightclientholimanfjlryanschneider
authored
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>
1 parent 7a3c890 commit bbfb1e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2446
-909
lines changed

accounts/abi/bind/auth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
120120
if chainID == nil {
121121
return nil, ErrNoChainID
122122
}
123-
signer := types.NewEIP155Signer(chainID)
123+
signer := types.LatestSignerForChainID(chainID)
124124
return &TransactOpts{
125125
From: account.Address,
126126
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
@@ -143,7 +143,7 @@ func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*Tr
143143
if chainID == nil {
144144
return nil, ErrNoChainID
145145
}
146-
signer := types.NewEIP155Signer(chainID)
146+
signer := types.LatestSignerForChainID(chainID)
147147
return &TransactOpts{
148148
From: keyAddr,
149149
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {

accounts/abi/bind/backends/simulated.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
559559
b.mu.Lock()
560560
defer b.mu.Unlock()
561561

562-
sender, err := types.Sender(types.NewEIP155Signer(b.config.ChainID), tx)
562+
// Check transaction validity.
563+
block := b.blockchain.CurrentBlock()
564+
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
565+
sender, err := types.Sender(signer, tx)
563566
if err != nil {
564567
panic(fmt.Errorf("invalid transaction: %v", err))
565568
}
@@ -568,7 +571,8 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
568571
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
569572
}
570573

571-
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
574+
// Include tx in chain.
575+
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
572576
for _, tx := range b.pendingBlock.Transactions() {
573577
block.AddTxWithChain(b.blockchain, tx)
574578
}
@@ -707,14 +711,15 @@ type callMsg struct {
707711
ethereum.CallMsg
708712
}
709713

710-
func (m callMsg) From() common.Address { return m.CallMsg.From }
711-
func (m callMsg) Nonce() uint64 { return 0 }
712-
func (m callMsg) CheckNonce() bool { return false }
713-
func (m callMsg) To() *common.Address { return m.CallMsg.To }
714-
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
715-
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
716-
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
717-
func (m callMsg) Data() []byte { return m.CallMsg.Data }
714+
func (m callMsg) From() common.Address { return m.CallMsg.From }
715+
func (m callMsg) Nonce() uint64 { return 0 }
716+
func (m callMsg) CheckNonce() bool { return false }
717+
func (m callMsg) To() *common.Address { return m.CallMsg.To }
718+
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
719+
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
720+
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
721+
func (m callMsg) Data() []byte { return m.CallMsg.Data }
722+
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
718723

719724
// filterBackend implements filters.Backend to support filtering for logs without
720725
// taking bloom-bits acceleration structures into account.

accounts/keystore/keystore.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,9 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
283283
if !found {
284284
return nil, ErrLocked
285285
}
286-
// Depending on the presence of the chain ID, sign with EIP155 or homestead
287-
if chainID != nil {
288-
return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
289-
}
290-
return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
286+
// Depending on the presence of the chain ID, sign with 2718 or homestead
287+
signer := types.LatestSignerForChainID(chainID)
288+
return types.SignTx(tx, signer, unlockedKey.PrivateKey)
291289
}
292290

293291
// SignHashWithPassphrase signs hash if the private key matching the given address
@@ -310,12 +308,9 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
310308
return nil, err
311309
}
312310
defer zeroKey(key.PrivateKey)
313-
314-
// Depending on the presence of the chain ID, sign with EIP155 or homestead
315-
if chainID != nil {
316-
return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
317-
}
318-
return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey)
311+
// Depending on the presence of the chain ID, sign with or without replay protection.
312+
signer := types.LatestSignerForChainID(chainID)
313+
return types.SignTx(tx, signer, key.PrivateKey)
319314
}
320315

321316
// Unlock unlocks the given account indefinitely.

accounts/scwallet/wallet.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ func (w *Wallet) signHash(account accounts.Account, hash []byte) ([]byte, error)
699699
// the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
700700
// the account in a keystore).
701701
func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
702-
signer := types.NewEIP155Signer(chainID)
702+
signer := types.LatestSignerForChainID(chainID)
703703
hash := signer.Hash(tx)
704704
sig, err := w.signHash(account, hash[:])
705705
if err != nil {

accounts/usbwallet/trezor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,11 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
255255
if chainID == nil {
256256
signer = new(types.HomesteadSigner)
257257
} else {
258+
// Trezor backend does not support typed transactions yet.
258259
signer = types.NewEIP155Signer(chainID)
259260
signature[64] -= byte(chainID.Uint64()*2 + 35)
260261
}
262+
261263
// Inject the final signature into the transaction and sanity check the sender
262264
signed, err := tx.WithSignature(signer, signature)
263265
if err != nil {

cmd/clef/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ func GenDoc(ctx *cli.Context) {
11061106

11071107
rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed")
11081108
var tx types.Transaction
1109-
rlp.DecodeBytes(rlpdata, &tx)
1109+
tx.UnmarshalBinary(rlpdata)
11101110
add("OnApproved - SignTransactionResult", desc, &ethapi.SignTransactionResult{Raw: rlpdata, Tx: &tx})
11111111

11121112
}

cmd/evm/README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Command line params that has to be supported are
3030
--trace.nomemory Disable full memory dump in traces
3131
--trace.nostack Disable stack output in traces
3232
--trace.noreturndata Disable return data output in traces
33-
--output.basedir value Specifies where output files are placed. Will be created if it does not exist. (default: ".")
33+
--output.basedir value Specifies where output files are placed. Will be created if it does not exist.
3434
--output.alloc alloc Determines where to put the alloc of the post-state.
3535
`stdout` - into the stdout output
3636
`stderr` - into the stderr output
@@ -237,10 +237,10 @@ Example where blockhashes are provided:
237237
cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
238238
```
239239
```
240-
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"PUSH1","error":""}
241-
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
242-
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"STOP","error":""}
243-
{"output":"","gasUsed":"0x17","time":112885}
240+
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
241+
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
242+
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STOP","error":""}
243+
{"output":"","gasUsed":"0x17","time":142709}
244244
```
245245

246246
In this example, the caller has not provided the required blockhash:
@@ -256,9 +256,9 @@ Error code: 4
256256
Another thing that can be done, is to chain invocations:
257257
```
258258
./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json
259-
INFO [08-03|15:25:15.168] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
260-
INFO [08-03|15:25:15.169] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
261-
INFO [08-03|15:25:15.169] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
259+
INFO [01-21|22:41:22.963] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
260+
INFO [01-21|22:41:22.966] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
261+
INFO [01-21|22:41:22.967] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
262262
263263
```
264264
What happened here, is that we first applied two identical transactions, so the second one was rejected.
@@ -267,4 +267,3 @@ the same two transactions: this time, both failed due to too low nonce.
267267

268268
In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the
269269
actual blocknumber (exposed to the EVM) would not increase.
270-

cmd/evm/internal/t8ntool/execution.go

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -143,19 +143,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
143143
vmConfig.Debug = (tracer != nil)
144144
statedb.Prepare(tx.Hash(), blockHash, txIndex)
145145
txContext := core.NewEVMTxContext(msg)
146-
147-
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
148-
if chainConfig.IsYoloV3(vmContext.BlockNumber) {
149-
statedb.AddAddressToAccessList(msg.From())
150-
if dst := msg.To(); dst != nil {
151-
statedb.AddAddressToAccessList(*dst)
152-
// If it's a create-tx, the destination will be added inside evm.create
153-
}
154-
for _, addr := range evm.ActivePrecompiles() {
155-
statedb.AddAddressToAccessList(addr)
156-
}
157-
}
158146
snapshot := statedb.Snapshot()
147+
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
148+
159149
// (ret []byte, usedGas uint64, failed bool, err error)
160150
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
161151
if err != nil {
@@ -169,7 +159,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
169159
return nil, nil, NewError(ErrorMissingBlockhash, hashError)
170160
}
171161
gasUsed += msgResult.UsedGas
172-
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
162+
163+
// Receipt:
173164
{
174165
var root []byte
175166
if chainConfig.IsByzantium(vmContext.BlockNumber) {
@@ -178,22 +169,32 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
178169
root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes()
179170
}
180171

181-
receipt := types.NewReceipt(root, msgResult.Failed(), gasUsed)
172+
// Create a new receipt for the transaction, storing the intermediate root and
173+
// gas used by the tx.
174+
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: gasUsed}
175+
if msgResult.Failed() {
176+
receipt.Status = types.ReceiptStatusFailed
177+
} else {
178+
receipt.Status = types.ReceiptStatusSuccessful
179+
}
182180
receipt.TxHash = tx.Hash()
183181
receipt.GasUsed = msgResult.UsedGas
184-
// if the transaction created a contract, store the creation address in the receipt.
182+
183+
// If the transaction created a contract, store the creation address in the receipt.
185184
if msg.To() == nil {
186185
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
187186
}
188-
// Set the receipt logs and create a bloom for filtering
187+
188+
// Set the receipt logs and create the bloom filter.
189189
receipt.Logs = statedb.GetLogs(tx.Hash())
190190
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
191-
// These three are non-consensus fields
191+
// These three are non-consensus fields:
192192
//receipt.BlockHash
193-
//receipt.BlockNumber =
193+
//receipt.BlockNumber
194194
receipt.TransactionIndex = uint(txIndex)
195195
receipts = append(receipts, receipt)
196196
}
197+
197198
txIndex++
198199
}
199200
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))

cmd/evm/internal/t8ntool/flags.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ var (
4747
Usage: "Specifies where output files are placed. Will be created if it does not exist.",
4848
Value: "",
4949
}
50+
OutputBodyFlag = cli.StringFlag{
51+
Name: "output.body",
52+
Usage: "If set, the RLP of the transactions (block body) will be written to this file.",
53+
Value: "",
54+
}
5055
OutputAllocFlag = cli.StringFlag{
5156
Name: "output.alloc",
5257
Usage: "Determines where to put the `alloc` of the post-state.\n" +

0 commit comments

Comments
 (0)