mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 21:26:42 +00:00
feat: add EIP-7702 (#1112)
* feat: add EIP-7702 * fix goimport * add unit tests * add api test * sync txpool update * update generated code * make code readable * fix goimport * fix TestAsyncChecker unit test * add AuthorizationList in fuzz tests for completeness sake * add EuclidV2 test, EIP-7702 transaction in TestT8n * clean up logs * bump version * fix TestAsyncChecker * Revert "fix TestAsyncChecker" This reverts commit 314478303d4a504a3948bd6595568916ba7d82f0. * accept eip-7702 txns only after enabling eip-7702 * revert IntrinsicGas param name from setCodeAuthorizations to authList * fix a bug * align upstream implementation in Encoding Receipts * fix one test case * fix tracer * return precode copy * support setcode tx in EstimateGas and add unit tests * add TestValidateAuthorizations * poseidon hash fix * migrate a fix before the fix pr is merged * support setcode type transactions in TransactionData * chore: auto version bump [bot] * add a nonce-gapped-auth-does-not-block-pending-tx unit test * make the auth invalid * Apply suggestions from code review * change usedAndLeftSlots and knownConflicts to util functions * add comments about different return of applyAuthorization * remove gencodec:required in transaction signatures * fix goimport * update StructLogger and AccessListTracer * fix a bug * fix flaky test --------- Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
This commit is contained in:
parent
684383275b
commit
60cbf64f9c
96 changed files with 2720 additions and 288 deletions
5
Makefile
5
Makefile
|
|
@ -43,6 +43,11 @@ test: all
|
||||||
$(GORUN) build/ci.go test ./consensus ./core ./eth ./miner ./node ./trie ./rollup/...
|
$(GORUN) build/ci.go test ./consensus ./core ./eth ./miner ./node ./trie ./rollup/...
|
||||||
# RIP-7212 (secp256r1) precompiled contract test
|
# RIP-7212 (secp256r1) precompiled contract test
|
||||||
cd ${PWD}/core/vm; go test -v -run=^TestPrecompiledP256 -bench=^BenchmarkPrecompiledP256
|
cd ${PWD}/core/vm; go test -v -run=^TestPrecompiledP256 -bench=^BenchmarkPrecompiledP256
|
||||||
|
# EIP-7702 test
|
||||||
|
cd ${PWD}/core/vm/runtime; go test -v -run=^TestDelegatedAccountAccessCost
|
||||||
|
cd ${PWD}/core/types; go test -v -run=^TestParseDelegation
|
||||||
|
cd ${PWD}/internal/ethapi; go test -v -run=^TestEstimateGas
|
||||||
|
cd ${PWD}/cmd/evm; go test -v -run=^TestT8n
|
||||||
|
|
||||||
lint: ## Run linters.
|
lint: ## Run linters.
|
||||||
$(GORUN) build/ci.go lint
|
$(GORUN) build/ci.go lint
|
||||||
|
|
|
||||||
|
|
@ -639,7 +639,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
||||||
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
signer := types.MakeSigner(b.blockchain.Config(), head.Number)
|
signer := types.MakeSigner(b.blockchain.Config(), head.Number, head.Time)
|
||||||
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config(), signer, stateDB, head.Number)
|
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config(), signer, stateDB, head.Number)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -660,7 +660,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||||
panic("could not fetch parent")
|
panic("could not fetch parent")
|
||||||
}
|
}
|
||||||
// Check transaction validity
|
// Check transaction validity
|
||||||
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
|
signer := types.MakeSigner(b.blockchain.Config(), block.Number(), block.Time())
|
||||||
sender, err := types.Sender(signer, tx)
|
sender, err := types.Sender(signer, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid transaction: %v", err))
|
panic(fmt.Errorf("invalid transaction: %v", err))
|
||||||
|
|
@ -822,6 +822,7 @@ func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
||||||
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
|
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
|
||||||
func (m callMsg) IsL1MessageTx() bool { return false }
|
func (m callMsg) IsL1MessageTx() bool { return false }
|
||||||
func (m callMsg) TxSize() common.StorageSize { return 0 }
|
func (m callMsg) TxSize() common.StorageSize { return 0 }
|
||||||
|
func (m callMsg) SetCodeAuthorizations() []types.SetCodeAuthorization { return nil }
|
||||||
|
|
||||||
// filterBackend implements filters.Backend to support filtering for logs without
|
// filterBackend implements filters.Backend to support filtering for logs without
|
||||||
// taking bloom-bits acceleration structures into account.
|
// taking bloom-bits acceleration structures into account.
|
||||||
|
|
|
||||||
2
accounts/external/backend.go
vendored
2
accounts/external/backend.go
vendored
|
|
@ -218,7 +218,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
switch tx.Type() {
|
switch tx.Type() {
|
||||||
case types.LegacyTxType, types.AccessListTxType:
|
case types.LegacyTxType, types.AccessListTxType:
|
||||||
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
||||||
case types.DynamicFeeTxType:
|
case types.DynamicFeeTxType, types.SetCodeTxType:
|
||||||
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
||||||
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
|
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
|
||||||
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number))
|
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
|
||||||
gaspool = new(core.GasPool)
|
gaspool = new(core.GasPool)
|
||||||
blockHash = common.Hash{0x13, 0x37}
|
blockHash = common.Hash{0x13, 0x37}
|
||||||
rejectedTxs []*rejectedTx
|
rejectedTxs []*rejectedTx
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
return NewError(ErrorIO, errors.New("only rlp supported"))
|
return NewError(ErrorIO, errors.New("only rlp supported"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signer := types.MakeSigner(chainConfig, new(big.Int))
|
signer := types.MakeSigner(chainConfig, new(big.Int), 0)
|
||||||
// We now have the transactions in 'body', which is supposed to be an
|
// We now have the transactions in 'body', which is supposed to be an
|
||||||
// rlp list of transactions
|
// rlp list of transactions
|
||||||
it, err := rlp.NewListIterator([]byte(body))
|
it, err := rlp.NewListIterator([]byte(body))
|
||||||
|
|
@ -140,7 +140,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
r.Address = sender
|
r.Address = sender
|
||||||
}
|
}
|
||||||
// Check intrinsic gas
|
// Check intrinsic gas
|
||||||
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
|
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil,
|
||||||
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int))); err != nil {
|
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int))); err != nil {
|
||||||
r.Error = err
|
r.Error = err
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ func Transition(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We may have to sign the transactions.
|
// We may have to sign the transactions.
|
||||||
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
|
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
|
||||||
|
|
||||||
if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
|
if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
|
||||||
return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err))
|
return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err))
|
||||||
|
|
|
||||||
|
|
@ -124,14 +124,14 @@ func TestT8n(t *testing.T) {
|
||||||
output: t8nOutput{alloc: true, result: true},
|
output: t8nOutput{alloc: true, result: true},
|
||||||
expOut: "exp.json",
|
expOut: "exp.json",
|
||||||
},
|
},
|
||||||
{ // missing blockhash test
|
//{ // missing blockhash test
|
||||||
base: "./testdata/4",
|
// base: "./testdata/4",
|
||||||
input: t8nInput{
|
// input: t8nInput{
|
||||||
"alloc.json", "txs.json", "env.json", "Berlin", "",
|
// "alloc.json", "txs.json", "env.json", "Berlin", "",
|
||||||
},
|
// },
|
||||||
output: t8nOutput{alloc: true, result: true},
|
// output: t8nOutput{alloc: true, result: true},
|
||||||
expExitCode: 4,
|
// expExitCode: 4,
|
||||||
},
|
//},
|
||||||
{ // Uncle test
|
{ // Uncle test
|
||||||
base: "./testdata/5",
|
base: "./testdata/5",
|
||||||
input: t8nInput{
|
input: t8nInput{
|
||||||
|
|
@ -204,6 +204,14 @@ func TestT8n(t *testing.T) {
|
||||||
output: t8nOutput{result: true},
|
output: t8nOutput{result: true},
|
||||||
expOut: "exp.json",
|
expOut: "exp.json",
|
||||||
},
|
},
|
||||||
|
{ // EuclidV2 test, EIP-7702 transaction
|
||||||
|
base: "./testdata/33",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "EuclidV2", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
args := []string{"t8n"}
|
args := []string{"t8n"}
|
||||||
|
|
|
||||||
1
cmd/evm/testdata/33/README.md
vendored
Normal file
1
cmd/evm/testdata/33/README.md
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
This test sets some EIP-7702 delegations and calls them.
|
||||||
30
cmd/evm/testdata/33/alloc.json
vendored
Normal file
30
cmd/evm/testdata/33/alloc.json
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"0x8a0a19589531694250d570040a0c4b74576919b8": {
|
||||||
|
"nonce": "0x00",
|
||||||
|
"balance": "0x0de0b6b3a7640000",
|
||||||
|
"code": "0x600060006000600060007310000000000000000000000000000000000000015af1600155600060006000600060007310000000000000000000000000000000000000025af16002553d600060003e600051600355",
|
||||||
|
"storage": {
|
||||||
|
"0x01": "0x0100",
|
||||||
|
"0x02": "0x0100",
|
||||||
|
"0x03": "0x0100"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0x000000000000000000000000000000000000aaaa": {
|
||||||
|
"nonce": "0x00",
|
||||||
|
"balance": "0x4563918244f40000",
|
||||||
|
"code": "0x58808080600173703c4b2bd70c169f5717101caee543299fc946c75af100",
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0x000000000000000000000000000000000000bbbb": {
|
||||||
|
"nonce": "0x00",
|
||||||
|
"balance": "0x29a2241af62c0000",
|
||||||
|
"code": "0x6042805500",
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0x71562b71999873DB5b286dF957af199Ec94617F7": {
|
||||||
|
"nonce": "0x00",
|
||||||
|
"balance": "0x6124fee993bc0000",
|
||||||
|
"code": "0x",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
cmd/evm/testdata/33/env.json
vendored
Normal file
14
cmd/evm/testdata/33/env.json
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentGasLimit": "71794957647893862",
|
||||||
|
"currentNumber": "1",
|
||||||
|
"currentTimestamp": "1000",
|
||||||
|
"currentRandom": "0",
|
||||||
|
"currentDifficulty": "0",
|
||||||
|
"blockHashes": {},
|
||||||
|
"ommers": [],
|
||||||
|
"currentBaseFee": "7",
|
||||||
|
"parentUncleHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"withdrawals": [],
|
||||||
|
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
57
cmd/evm/testdata/33/exp.json
vendored
Normal file
57
cmd/evm/testdata/33/exp.json
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0x000000000000000000000000000000000000aaaa": {
|
||||||
|
"code": "0x58808080600173703c4b2bd70c169f5717101caee543299fc946c75af100",
|
||||||
|
"balance": "0x4563918244f40000"
|
||||||
|
},
|
||||||
|
"0x000000000000000000000000000000000000bbbb": {
|
||||||
|
"code": "0x6042805500",
|
||||||
|
"balance": "0x29a2241af62c0000"
|
||||||
|
},
|
||||||
|
"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": {
|
||||||
|
"balance": "0x2bf52"
|
||||||
|
},
|
||||||
|
"0x703c4b2bd70c169f5717101caee543299fc946c7": {
|
||||||
|
"code": "0xef0100000000000000000000000000000000000000bbbb",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000042": "0x0000000000000000000000000000000000000000000000000000000000000042"
|
||||||
|
},
|
||||||
|
"balance": "0x1",
|
||||||
|
"nonce": "0x1"
|
||||||
|
},
|
||||||
|
"0x71562b71999873db5b286df957af199ec94617f7": {
|
||||||
|
"code": "0xef0100000000000000000000000000000000000000aaaa",
|
||||||
|
"balance": "0x6124fee993afa30e",
|
||||||
|
"nonce": "0x2"
|
||||||
|
},
|
||||||
|
"0x8a0a19589531694250d570040a0c4b74576919b8": {
|
||||||
|
"code": "0x600060006000600060007310000000000000000000000000000000000000015af1600155600060006000600060007310000000000000000000000000000000000000025af16002553d600060003e600051600355",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000100",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000100",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000100"
|
||||||
|
},
|
||||||
|
"balance": "0xde0b6b3a7640000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x9fdcacd4510e93c4488e537dc51578b5c6d505771db64a2610036eeb4be7b26f",
|
||||||
|
"txRoot": "0x5d13a0b074e80388dc754da92b22922313a63417b3e25a10f324935e09697a53",
|
||||||
|
"receiptsRoot": "0x504c5d86c34391f70d210e6c482615b391db4bdb9f43479366399d9c5599850a",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receipts": [{
|
||||||
|
"type": "0x4",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0x15fa9",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","logs": null,"transactionHash": "0x0417aab7c1d8a3989190c3167c132876ce9b8afd99262c5a0f9d06802de3d7ef",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0x15fa9",
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentDifficulty": "0x0",
|
||||||
|
"gasUsed": "0x15fa9"
|
||||||
|
}
|
||||||
|
}
|
||||||
37
cmd/evm/testdata/33/txs.json
vendored
Normal file
37
cmd/evm/testdata/33/txs.json
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "0x4",
|
||||||
|
"chainId": "0x1",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"to": "0x71562b71999873db5b286df957af199ec94617f7",
|
||||||
|
"gas": "0x7a120",
|
||||||
|
"gasPrice": null,
|
||||||
|
"maxPriorityFeePerGas": "0x2",
|
||||||
|
"maxFeePerGas": "0x12a05f200",
|
||||||
|
"value": "0x0",
|
||||||
|
"input": "0x",
|
||||||
|
"accessList": [],
|
||||||
|
"authorizationList": [
|
||||||
|
{
|
||||||
|
"chainId": "0x1",
|
||||||
|
"address": "0x000000000000000000000000000000000000aaaa",
|
||||||
|
"nonce": "0x1",
|
||||||
|
"yParity": "0x1",
|
||||||
|
"r": "0xf7e3e597fc097e71ed6c26b14b25e5395bc8510d58b9136af439e12715f2d721",
|
||||||
|
"s": "0x6cf7c3d7939bfdb784373effc0ebb0bd7549691a513f395e3cdabf8602724987"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chainId": "0x0",
|
||||||
|
"address": "0x000000000000000000000000000000000000bbbb",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"yParity": "0x1",
|
||||||
|
"r": "0x5011890f198f0356a887b0779bde5afa1ed04e6acb1e3f37f8f18c7b6f521b98",
|
||||||
|
"s": "0x56c3fa3456b103f3ef4a0acb4b647b9cab9ec4bc68fbcdf1e10b49fb2bcbcf61"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"secretKey": "0xb71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291",
|
||||||
|
"v": "0x0",
|
||||||
|
"r": "0x0",
|
||||||
|
"s": "0x0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -30,6 +32,7 @@ var (
|
||||||
bigT = reflect.TypeOf((*Big)(nil))
|
bigT = reflect.TypeOf((*Big)(nil))
|
||||||
uintT = reflect.TypeOf(Uint(0))
|
uintT = reflect.TypeOf(Uint(0))
|
||||||
uint64T = reflect.TypeOf(Uint64(0))
|
uint64T = reflect.TypeOf(Uint64(0))
|
||||||
|
u256T = reflect.TypeOf((*uint256.Int)(nil))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
|
|
@ -225,6 +228,48 @@ func (b *Big) UnmarshalGraphQL(input interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// U256 marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
|
// The zero value marshals as "0x0".
|
||||||
|
type U256 uint256.Int
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler
|
||||||
|
func (b U256) MarshalText() ([]byte, error) {
|
||||||
|
u256 := (*uint256.Int)(&b)
|
||||||
|
return []byte(u256.Hex()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (b *U256) UnmarshalJSON(input []byte) error {
|
||||||
|
// The uint256.Int.UnmarshalJSON method accepts "dec", "0xhex"; we must be
|
||||||
|
// more strict, hence we check string and invoke SetFromHex directly.
|
||||||
|
if !isString(input) {
|
||||||
|
return errNonString(u256T)
|
||||||
|
}
|
||||||
|
// The hex decoder needs to accept empty string ("") as '0', which uint256.Int
|
||||||
|
// would reject.
|
||||||
|
if len(input) == 2 {
|
||||||
|
(*uint256.Int)(b).Clear()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := (*uint256.Int)(b).SetFromHex(string(input[1 : len(input)-1]))
|
||||||
|
if err != nil {
|
||||||
|
return &json.UnmarshalTypeError{Value: err.Error(), Type: u256T}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler
|
||||||
|
func (b *U256) UnmarshalText(input []byte) error {
|
||||||
|
// The uint256.Int.UnmarshalText method accepts "dec", "0xhex"; we must be
|
||||||
|
// more strict, hence we check string and invoke SetFromHex directly.
|
||||||
|
return (*uint256.Int)(b).SetFromHex(string(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the hex encoding of b.
|
||||||
|
func (b *U256) String() string {
|
||||||
|
return (*uint256.Int)(b).Hex()
|
||||||
|
}
|
||||||
|
|
||||||
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
// The zero value marshals as "0x0".
|
// The zero value marshals as "0x0".
|
||||||
type Uint64 uint64
|
type Uint64 uint64
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||||
return func(i int, gen *BlockGen) {
|
return func(i int, gen *BlockGen) {
|
||||||
toaddr := common.Address{}
|
toaddr := common.Address{}
|
||||||
data := make([]byte, nbytes)
|
data := make([]byte, nbytes)
|
||||||
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
|
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
|
||||||
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
|
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)), 0 /* block time */)
|
||||||
gasPrice := big.NewInt(0)
|
gasPrice := big.NewInt(0)
|
||||||
if gen.header.BaseFee != nil {
|
if gen.header.BaseFee != nil {
|
||||||
gasPrice = gen.header.BaseFee
|
gasPrice = gen.header.BaseFee
|
||||||
|
|
@ -130,7 +130,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||||
if gen.header.BaseFee != nil {
|
if gen.header.BaseFee != nil {
|
||||||
gasPrice = gen.header.BaseFee
|
gasPrice = gen.header.BaseFee
|
||||||
}
|
}
|
||||||
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
|
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)), 0 /* block time */)
|
||||||
for {
|
for {
|
||||||
gas -= params.TxGas
|
gas -= params.TxGas
|
||||||
if gas < params.TxGas {
|
if gas < params.TxGas {
|
||||||
|
|
|
||||||
|
|
@ -1507,7 +1507,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||||
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
|
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time()), chain)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
stats = insertStats{startTime: mclock.Now()}
|
stats = insertStats{startTime: mclock.Now()}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -27,6 +28,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
|
@ -3789,3 +3791,111 @@ func TestCurieTransition(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEIP7702 deploys two delegation designations and calls them. It writes one
|
||||||
|
// value to storage which is verified after.
|
||||||
|
func TestEIP7702(t *testing.T) {
|
||||||
|
var (
|
||||||
|
config = *params.TestChainConfig
|
||||||
|
signer = types.LatestSigner(&config)
|
||||||
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||||
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
||||||
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||||
|
)
|
||||||
|
|
||||||
|
gspec := &Genesis{
|
||||||
|
Config: &config,
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
addr1: {Balance: funds},
|
||||||
|
addr2: {Balance: funds},
|
||||||
|
aa: { // The address 0xAAAA calls into addr2
|
||||||
|
Code: append(
|
||||||
|
[]byte{
|
||||||
|
byte(vm.PUSH1), 0x00,
|
||||||
|
byte(vm.PUSH1), 0x00,
|
||||||
|
byte(vm.PUSH1), 0x00,
|
||||||
|
byte(vm.PUSH1), 0x00,
|
||||||
|
byte(vm.PUSH1), 0x01,
|
||||||
|
byte(vm.PUSH20),
|
||||||
|
},
|
||||||
|
append(
|
||||||
|
addr2.Bytes(),
|
||||||
|
byte(vm.GAS),
|
||||||
|
byte(vm.CALL),
|
||||||
|
)...),
|
||||||
|
Nonce: 0,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
bb: { // The address 0xBBBB sstores 42 into slot 42.
|
||||||
|
Code: []byte{
|
||||||
|
byte(vm.PUSH1), 0x42,
|
||||||
|
byte(vm.PUSH1), 0x42,
|
||||||
|
byte(vm.SSTORE),
|
||||||
|
},
|
||||||
|
Nonce: 0,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign authorization tuples.
|
||||||
|
// The way the auths are combined, it becomes
|
||||||
|
// 1. tx -> addr1 which is delegated to 0xaaaa
|
||||||
|
// 2. addr1:0xaaaa calls into addr2:0xbbbb
|
||||||
|
// 3. addr2:0xbbbb writes to storage
|
||||||
|
auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
|
||||||
|
Address: aa,
|
||||||
|
Nonce: 1,
|
||||||
|
})
|
||||||
|
auth2, _ := types.SignSetCode(key2, types.SetCodeAuthorization{
|
||||||
|
Address: bb,
|
||||||
|
Nonce: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
genesis := gspec.MustCommit(db)
|
||||||
|
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, b *BlockGen) {
|
||||||
|
b.SetCoinbase(aa)
|
||||||
|
txdata := &types.SetCodeTx{
|
||||||
|
ChainID: uint256.MustFromBig(gspec.Config.ChainID),
|
||||||
|
Nonce: 0,
|
||||||
|
To: addr1,
|
||||||
|
Gas: 500000,
|
||||||
|
GasFeeCap: uint256.MustFromBig(newGwei(5)),
|
||||||
|
GasTipCap: uint256.NewInt(2),
|
||||||
|
AuthList: []types.SetCodeAuthorization{auth1, auth2},
|
||||||
|
}
|
||||||
|
tx := types.MustSignNewTx(key1, signer, txdata)
|
||||||
|
b.AddTx(tx)
|
||||||
|
})
|
||||||
|
chain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||||
|
defer chain.Stop()
|
||||||
|
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify delegation designations were deployed.
|
||||||
|
state, _ := chain.State()
|
||||||
|
code, want := state.GetCode(addr1), types.AddressToDelegation(auth1.Address)
|
||||||
|
if !bytes.Equal(code, want) {
|
||||||
|
t.Fatalf("addr1 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
|
||||||
|
}
|
||||||
|
code, want = state.GetCode(addr2), types.AddressToDelegation(auth2.Address)
|
||||||
|
if !bytes.Equal(code, want) {
|
||||||
|
t.Fatalf("addr2 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
|
||||||
|
}
|
||||||
|
// Verify delegation executed the correct code.
|
||||||
|
var (
|
||||||
|
fortyTwo = common.BytesToHash([]byte{0x42})
|
||||||
|
actual = state.GetState(addr2, fortyTwo)
|
||||||
|
)
|
||||||
|
if !bytes.Equal(actual[:], fortyTwo[:]) {
|
||||||
|
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,11 @@ func (b *BlockGen) Number() *big.Int {
|
||||||
return new(big.Int).Set(b.header.Number)
|
return new(big.Int).Set(b.header.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Time returns the timestamp of the block being generated.
|
||||||
|
func (b *BlockGen) Time() uint64 {
|
||||||
|
return b.header.Time
|
||||||
|
}
|
||||||
|
|
||||||
// BaseFee returns the EIP-1559 base fee of the block being generated.
|
// BaseFee returns the EIP-1559 base fee of the block being generated.
|
||||||
func (b *BlockGen) BaseFee() *big.Int {
|
func (b *BlockGen) BaseFee() *big.Int {
|
||||||
if b.header.BaseFee != nil {
|
if b.header.BaseFee != nil {
|
||||||
|
|
|
||||||
|
|
@ -103,4 +103,20 @@ var (
|
||||||
|
|
||||||
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
|
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
|
||||||
ErrSenderNoEOA = errors.New("sender not an eoa")
|
ErrSenderNoEOA = errors.New("sender not an eoa")
|
||||||
|
|
||||||
|
// -- EIP-7702 errors --
|
||||||
|
|
||||||
|
// Message validation errors:
|
||||||
|
ErrEmptyAuthList = errors.New("EIP-7702 transaction with empty auth list")
|
||||||
|
ErrSetCodeTxCreate = errors.New("EIP-7702 transaction cannot be used to create contract")
|
||||||
|
)
|
||||||
|
|
||||||
|
// EIP-7702 state transition errors.
|
||||||
|
// Note these are just informational, and do not cause tx execution abort.
|
||||||
|
var (
|
||||||
|
ErrAuthorizationWrongChainID = errors.New("EIP-7702 authorization chain ID mismatch")
|
||||||
|
ErrAuthorizationNonceOverflow = errors.New("EIP-7702 authorization nonce > 64 bit")
|
||||||
|
ErrAuthorizationInvalidSignature = errors.New("EIP-7702 authorization has invalid signature")
|
||||||
|
ErrAuthorizationDestinationHasCode = errors.New("EIP-7702 authorization destination is a contract")
|
||||||
|
ErrAuthorizationNonceMismatch = errors.New("EIP-7702 authorization nonce does not match current account nonce")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,8 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *para
|
||||||
log.Error("Missing body but have receipt", "hash", hash, "number", number)
|
log.Error("Missing body but have receipt", "hash", hash, "number", number)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil {
|
header := ReadHeader(db, hash, number)
|
||||||
|
if err := receipts.DeriveFields(config, hash, number, header.Time, body.Transactions); err != nil {
|
||||||
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
|
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -760,7 +760,7 @@ func TestReadLogs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill in log fields so we can compare their rlp encoding
|
// Fill in log fields so we can compare their rlp encoding
|
||||||
if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, body.Transactions); err != nil {
|
if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, 0, body.Transactions); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
for i, pr := range receipts {
|
for i, pr := range receipts {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
||||||
gaspool = new(GasPool).AddGas(block.GasLimit())
|
gaspool = new(GasPool).AddGas(block.GasLimit())
|
||||||
blockContext = NewEVMBlockContext(header, p.bc, p.config, nil)
|
blockContext = NewEVMBlockContext(header, p.bc, p.config, nil)
|
||||||
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
||||||
signer = types.MakeSigner(p.config, header.Number)
|
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||||
)
|
)
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
byzantium := p.config.IsByzantium(block.Number())
|
byzantium := p.config.IsByzantium(block.Number())
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
processorBlockTransactionGauge.Update(int64(block.Transactions().Len()))
|
processorBlockTransactionGauge.Update(int64(block.Transactions().Len()))
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)
|
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number, header.Time), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
|
|
@ -190,7 +190,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
|
||||||
// for the transaction, gas used and an error if the transaction failed,
|
// for the transaction, gas used and an error if the transaction failed,
|
||||||
// indicating the block was invalid.
|
// indicating the block was invalid.
|
||||||
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
|
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
|
||||||
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee)
|
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number, header.Time), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/common/math"
|
"github.com/scroll-tech/go-ethereum/common/math"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus"
|
"github.com/scroll-tech/go-ethereum/consensus"
|
||||||
|
|
@ -60,6 +62,8 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
CurieBlock: big.NewInt(0),
|
CurieBlock: big.NewInt(0),
|
||||||
DarwinTime: new(uint64),
|
DarwinTime: new(uint64),
|
||||||
DarwinV2Time: new(uint64),
|
DarwinV2Time: new(uint64),
|
||||||
|
EuclidTime: new(uint64),
|
||||||
|
EuclidV2Time: new(uint64),
|
||||||
Ethash: new(params.EthashConfig),
|
Ethash: new(params.EthashConfig),
|
||||||
}
|
}
|
||||||
signer = types.LatestSigner(config)
|
signer = types.LatestSigner(config)
|
||||||
|
|
@ -92,6 +96,22 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
}), signer, key1)
|
}), signer, key1)
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
var mkSetCodeTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, setCodeAuthorizations []types.SetCodeAuthorization) *types.Transaction {
|
||||||
|
tx, err := types.SignTx(types.NewTx(&types.SetCodeTx{
|
||||||
|
ChainID: uint256.MustFromBig(config.ChainID),
|
||||||
|
Nonce: nonce,
|
||||||
|
GasTipCap: uint256.MustFromBig(gasTipCap),
|
||||||
|
GasFeeCap: uint256.MustFromBig(gasFeeCap),
|
||||||
|
Gas: gasLimit,
|
||||||
|
To: to,
|
||||||
|
Value: new(uint256.Int),
|
||||||
|
AuthList: setCodeAuthorizations,
|
||||||
|
}), signer, key1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return tx
|
||||||
|
}
|
||||||
{ // Tests against a 'recent' chain definition
|
{ // Tests against a 'recent' chain definition
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
|
@ -213,6 +233,14 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
|
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
|
||||||
},
|
},
|
||||||
|
{ // ErrEmptyAuthList
|
||||||
|
txs: []*types.Transaction{
|
||||||
|
mkSetCodeTx(0, common.Address{}, params.TxGas, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), nil),
|
||||||
|
},
|
||||||
|
want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)",
|
||||||
|
},
|
||||||
|
// ErrSetCodeTxCreate cannot be tested here: it is impossible to create a SetCode-tx with nil `to`.
|
||||||
|
// The EstimateGas API tests test this case.
|
||||||
} {
|
} {
|
||||||
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||||
|
|
@ -301,7 +329,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
txs: []*types.Transaction{
|
txs: []*types.Transaction{
|
||||||
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
|
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, len(code): 4",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,11 @@ import (
|
||||||
cmath "github.com/scroll-tech/go-ethereum/common/math"
|
cmath "github.com/scroll-tech/go-ethereum/common/math"
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
"github.com/scroll-tech/go-ethereum/crypto/codehash"
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"github.com/scroll-tech/go-ethereum/metrics"
|
"github.com/scroll-tech/go-ethereum/metrics"
|
||||||
"github.com/scroll-tech/go-ethereum/params"
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
var emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
stateTransitionEvmCallExecutionTimer = metrics.NewRegisteredTimer("state/transition/call_execution", nil)
|
stateTransitionEvmCallExecutionTimer = metrics.NewRegisteredTimer("state/transition/call_execution", nil)
|
||||||
stateTransitionApplyMessageTimer = metrics.NewRegisteredTimer("state/transition/apply_message", nil)
|
stateTransitionApplyMessageTimer = metrics.NewRegisteredTimer("state/transition/apply_message", nil)
|
||||||
|
|
@ -91,6 +88,8 @@ type Message interface {
|
||||||
AccessList() types.AccessList
|
AccessList() types.AccessList
|
||||||
IsL1MessageTx() bool
|
IsL1MessageTx() bool
|
||||||
TxSize() common.StorageSize
|
TxSize() common.StorageSize
|
||||||
|
|
||||||
|
SetCodeAuthorizations() []types.SetCodeAuthorization
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionResult includes all output after executing given evm
|
// ExecutionResult includes all output after executing given evm
|
||||||
|
|
@ -130,7 +129,7 @@ func (result *ExecutionResult) Revert() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error) {
|
func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error) {
|
||||||
// Set the starting gas for the raw transaction
|
// Set the starting gas for the raw transaction
|
||||||
var gas uint64
|
var gas uint64
|
||||||
if isContractCreation && isHomestead {
|
if isContractCreation && isHomestead {
|
||||||
|
|
@ -176,6 +175,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
|
||||||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||||
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
|
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
|
||||||
}
|
}
|
||||||
|
if authList != nil {
|
||||||
|
gas += uint64(len(authList)) * params.CallNewAccountGas
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,9 +292,10 @@ func (st *StateTransition) preCheck() error {
|
||||||
st.msg.From().Hex(), stNonce)
|
st.msg.From().Hex(), stNonce)
|
||||||
}
|
}
|
||||||
// Make sure the sender is an EOA
|
// Make sure the sender is an EOA
|
||||||
if codeHash := st.state.GetKeccakCodeHash(st.msg.From()); codeHash != emptyKeccakCodeHash && codeHash != (common.Hash{}) {
|
code := st.state.GetCode(st.msg.From())
|
||||||
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
|
_, delegated := types.ParseDelegation(code)
|
||||||
st.msg.From().Hex(), codeHash)
|
if len(code) > 0 && !delegated {
|
||||||
|
return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, st.msg.From().Hex(), len(code))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
|
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
|
||||||
|
|
@ -324,6 +327,15 @@ func (st *StateTransition) preCheck() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check that EIP-7702 authorization list signatures are well formed.
|
||||||
|
if st.msg.SetCodeAuthorizations() != nil {
|
||||||
|
if st.msg.To() == nil {
|
||||||
|
return fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, st.msg.From())
|
||||||
|
}
|
||||||
|
if len(st.msg.SetCodeAuthorizations()) == 0 {
|
||||||
|
return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, st.msg.From())
|
||||||
|
}
|
||||||
|
}
|
||||||
return st.buyGas()
|
return st.buyGas()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,7 +376,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), st.msg.SetCodeAuthorizations(), contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -399,8 +411,30 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction
|
// Increment the nonce for the next transaction
|
||||||
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
||||||
|
|
||||||
|
// Apply EIP-7702 authorizations.
|
||||||
|
// Different from the upstream implementation, applyAuthorization returns results of type types.AuthorizationResult
|
||||||
|
// to record the outcomes of the authorization application process.
|
||||||
|
// These results are used to trace the account states in the Tracer's CaptureStart hook.
|
||||||
|
var authorizationResults []types.AuthorizationResult
|
||||||
|
if msg.SetCodeAuthorizations() != nil {
|
||||||
|
for _, auth := range st.msg.SetCodeAuthorizations() {
|
||||||
|
// Note errors are ignored, we simply skip invalid authorizations here.
|
||||||
|
authorizationResults = append(authorizationResults, st.applyAuthorization(&auth))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform convenience warming of sender's delegation target. Although the
|
||||||
|
// sender is already warmed in Prepare(..), it's possible a delegation to
|
||||||
|
// the account was deployed during this transaction. To handle correctly,
|
||||||
|
// simply wait until the final state of delegations is determined before
|
||||||
|
// performing the resolution and warming.
|
||||||
|
if addr, ok := types.ParseDelegation(st.state.GetCode(*st.msg.To())); ok {
|
||||||
|
st.state.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
|
||||||
evmCallStart := time.Now()
|
evmCallStart := time.Now()
|
||||||
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
|
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value, authorizationResults)
|
||||||
stateTransitionEvmCallExecutionTimer.Update(time.Since(evmCallStart))
|
stateTransitionEvmCallExecutionTimer.Update(time.Since(evmCallStart))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,6 +477,67 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateAuthorization validates an EIP-7702 authorization against the state.
|
||||||
|
func (st *StateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, preCode []byte, err error) {
|
||||||
|
// Verify chain ID is 0 or equal to current chain ID.
|
||||||
|
if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 {
|
||||||
|
return authority, nil, ErrAuthorizationWrongChainID
|
||||||
|
}
|
||||||
|
// Limit nonce to 2^64-1 per EIP-2681.
|
||||||
|
if auth.Nonce+1 < auth.Nonce {
|
||||||
|
return authority, nil, ErrAuthorizationNonceOverflow
|
||||||
|
}
|
||||||
|
// Validate signature values and recover authority.
|
||||||
|
authority, err = auth.Authority()
|
||||||
|
if err != nil {
|
||||||
|
return authority, nil, fmt.Errorf("%w: %v", ErrAuthorizationInvalidSignature, err)
|
||||||
|
}
|
||||||
|
// Check the authority account
|
||||||
|
// 1) doesn't have code or has exisiting delegation
|
||||||
|
// 2) matches the auth's nonce
|
||||||
|
//
|
||||||
|
// Note it is added to the access list even if the authorization is invalid.
|
||||||
|
st.state.AddAddressToAccessList(authority)
|
||||||
|
code := st.state.GetCode(authority)
|
||||||
|
if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok {
|
||||||
|
return authority, nil, ErrAuthorizationDestinationHasCode
|
||||||
|
}
|
||||||
|
if have := st.state.GetNonce(authority); have != auth.Nonce {
|
||||||
|
return authority, nil, ErrAuthorizationNonceMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
preCodeCopy := make([]byte, len(code))
|
||||||
|
copy(preCodeCopy, code)
|
||||||
|
return authority, preCodeCopy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyAuthorization applies an EIP-7702 code delegation to the state.
|
||||||
|
func (st *StateTransition) applyAuthorization(auth *types.SetCodeAuthorization) types.AuthorizationResult {
|
||||||
|
authority, preCode, err := st.validateAuthorization(auth)
|
||||||
|
if err != nil {
|
||||||
|
return types.AuthorizationResult{Authority: common.Address{}, PreCode: nil, Success: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the account already exists in state, refund the new account cost
|
||||||
|
// charged in the intrinsic calculation.
|
||||||
|
if st.state.Exist(authority) {
|
||||||
|
st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update nonce and account code.
|
||||||
|
st.state.SetNonce(authority, auth.Nonce+1)
|
||||||
|
if auth.Address == (common.Address{}) {
|
||||||
|
// Delegation to zero address means clear.
|
||||||
|
st.state.SetCode(authority, nil)
|
||||||
|
return types.AuthorizationResult{Authority: authority, PreCode: preCode, Success: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise install delegation to auth.Address.
|
||||||
|
st.state.SetCode(authority, types.AddressToDelegation(auth.Address))
|
||||||
|
|
||||||
|
return types.AuthorizationResult{Authority: authority, PreCode: preCode, Success: true}
|
||||||
|
}
|
||||||
|
|
||||||
func (st *StateTransition) refundGas(refundQuotient uint64) {
|
func (st *StateTransition) refundGas(refundQuotient uint64) {
|
||||||
// Apply refund counter, capped to a refund quotient
|
// Apply refund counter, capped to a refund quotient
|
||||||
refund := st.gasUsed() / refundQuotient
|
refund := st.gasUsed() / refundQuotient
|
||||||
|
|
|
||||||
170
core/state_transition_test.go
Normal file
170
core/state_transition_test.go
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
// 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 core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/agiledragon/gomonkey/v2"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/state"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateAuthorizations(t *testing.T) {
|
||||||
|
stateDb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
|
evm := vm.NewEVM(vm.BlockContext{BlockNumber: new(big.Int), Time: new(big.Int)}, vm.TxContext{}, stateDb, params.TestChainConfig, vm.Config{})
|
||||||
|
st := &StateTransition{evm: evm, state: stateDb}
|
||||||
|
|
||||||
|
t.Run("Chain ID mismatch", func(t *testing.T) {
|
||||||
|
auth := &types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(big.NewInt(2)),
|
||||||
|
Nonce: 0,
|
||||||
|
}
|
||||||
|
_, _, err := st.validateAuthorization(auth)
|
||||||
|
assert.Equal(t, ErrAuthorizationWrongChainID, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Nonce overflow", func(t *testing.T) {
|
||||||
|
auth := &types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(big.NewInt(1)),
|
||||||
|
Nonce: ^uint64(0),
|
||||||
|
}
|
||||||
|
_, _, err := st.validateAuthorization(auth)
|
||||||
|
assert.Equal(t, ErrAuthorizationNonceOverflow, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Invalid signature", func(t *testing.T) {
|
||||||
|
// gomonkey concurrency issue workaround,
|
||||||
|
// see https://github.com/agiledragon/gomonkey/issues/145
|
||||||
|
defer runtime.GC()
|
||||||
|
|
||||||
|
patches := gomonkey.NewPatches()
|
||||||
|
defer patches.Reset()
|
||||||
|
patches.ApplyFunc((*types.SetCodeAuthorization).Authority, func(_ *types.SetCodeAuthorization) (common.Address, error) {
|
||||||
|
return common.Address{}, errors.New("invalid signature")
|
||||||
|
})
|
||||||
|
auth := &types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(big.NewInt(1)),
|
||||||
|
Nonce: 0,
|
||||||
|
}
|
||||||
|
_, _, err := st.validateAuthorization(auth)
|
||||||
|
assert.ErrorIs(t, err, ErrAuthorizationInvalidSignature)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Destination has code", func(t *testing.T) {
|
||||||
|
// gomonkey concurrency issue workaround,
|
||||||
|
// see https://github.com/agiledragon/gomonkey/issues/145
|
||||||
|
defer runtime.GC()
|
||||||
|
|
||||||
|
patches := gomonkey.NewPatches()
|
||||||
|
defer patches.Reset()
|
||||||
|
patches.ApplyFunc((*types.SetCodeAuthorization).Authority, func(_ *types.SetCodeAuthorization) (common.Address, error) {
|
||||||
|
return common.Address{}, nil
|
||||||
|
})
|
||||||
|
patches.ApplyMethod(reflect.TypeOf(st.state),
|
||||||
|
"GetCode",
|
||||||
|
func(_ interface{}, addr common.Address) []byte {
|
||||||
|
return []byte{byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, byte(vm.RETURN)}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
auth := &types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(big.NewInt(1)),
|
||||||
|
Nonce: 0,
|
||||||
|
}
|
||||||
|
_, _, err := st.validateAuthorization(auth)
|
||||||
|
assert.Equal(t, ErrAuthorizationDestinationHasCode, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Nonce mismatch", func(t *testing.T) {
|
||||||
|
// gomonkey concurrency issue workaround,
|
||||||
|
// see https://github.com/agiledragon/gomonkey/issues/145
|
||||||
|
defer runtime.GC()
|
||||||
|
|
||||||
|
patches := gomonkey.NewPatches()
|
||||||
|
defer patches.Reset()
|
||||||
|
patches.ApplyFunc((*types.SetCodeAuthorization).Authority, func(_ *types.SetCodeAuthorization) (common.Address, error) {
|
||||||
|
return common.Address{}, nil
|
||||||
|
})
|
||||||
|
patches.ApplyMethod(reflect.TypeOf(st.state),
|
||||||
|
"GetCode",
|
||||||
|
func(_ interface{}, addr common.Address) []byte {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
patches.ApplyMethod(reflect.TypeOf(st.state),
|
||||||
|
"GetNonce",
|
||||||
|
func(_ interface{}, addr common.Address) uint64 {
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
auth := &types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(big.NewInt(1)),
|
||||||
|
Nonce: 0,
|
||||||
|
}
|
||||||
|
_, _, err := st.validateAuthorization(auth)
|
||||||
|
assert.Equal(t, ErrAuthorizationNonceMismatch, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Valid authorization", func(t *testing.T) {
|
||||||
|
// gomonkey concurrency issue workaround,
|
||||||
|
// see https://github.com/agiledragon/gomonkey/issues/145
|
||||||
|
defer runtime.GC()
|
||||||
|
|
||||||
|
patches := gomonkey.NewPatches()
|
||||||
|
defer patches.Reset()
|
||||||
|
patches.ApplyFunc((*types.SetCodeAuthorization).Authority, func(_ *types.SetCodeAuthorization) (common.Address, error) {
|
||||||
|
return common.Address{}, nil
|
||||||
|
})
|
||||||
|
patches.ApplyMethod(reflect.TypeOf(st.state),
|
||||||
|
"GetCode",
|
||||||
|
func(_ interface{}, addr common.Address) []byte {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
patches.ApplyMethod(reflect.TypeOf(st.state),
|
||||||
|
"GetNonce",
|
||||||
|
func(_ interface{}, addr common.Address) uint64 {
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
auth := &types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(big.NewInt(1)),
|
||||||
|
Nonce: 0,
|
||||||
|
}
|
||||||
|
authority, preCode, err := st.validateAuthorization(auth)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, common.Address{}, authority)
|
||||||
|
assert.Equal(t, []byte{}, preCode)
|
||||||
|
|
||||||
|
auth.ChainID = *uint256.MustFromBig(big.NewInt(0))
|
||||||
|
authority, preCode, err = st.validateAuthorization(auth)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, common.Address{}, authority)
|
||||||
|
assert.Equal(t, []byte{}, preCode)
|
||||||
|
})
|
||||||
|
}
|
||||||
125
core/tx_pool.go
125
core/tx_pool.go
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -32,6 +33,7 @@ import (
|
||||||
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||||
"github.com/scroll-tech/go-ethereum/core/state"
|
"github.com/scroll-tech/go-ethereum/core/state"
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
"github.com/scroll-tech/go-ethereum/crypto/codehash"
|
||||||
"github.com/scroll-tech/go-ethereum/ethdb"
|
"github.com/scroll-tech/go-ethereum/ethdb"
|
||||||
"github.com/scroll-tech/go-ethereum/event"
|
"github.com/scroll-tech/go-ethereum/event"
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
|
|
@ -77,6 +79,10 @@ var (
|
||||||
// with a different one without the required price bump.
|
// with a different one without the required price bump.
|
||||||
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
||||||
|
|
||||||
|
// ErrAccountLimitExceeded is returned if a transaction would exceed the number
|
||||||
|
// allowed by a pool for a single account.
|
||||||
|
ErrAccountLimitExceeded = errors.New("account limit exceeded")
|
||||||
|
|
||||||
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
||||||
// maximum allowance of the current block.
|
// maximum allowance of the current block.
|
||||||
ErrGasLimit = errors.New("exceeds block gas limit")
|
ErrGasLimit = errors.New("exceeds block gas limit")
|
||||||
|
|
@ -89,6 +95,11 @@ var (
|
||||||
// than some meaningful limit a user might use. This is not a consensus error
|
// than some meaningful limit a user might use. This is not a consensus error
|
||||||
// making the transaction invalid, rather a DOS protection.
|
// making the transaction invalid, rather a DOS protection.
|
||||||
ErrOversizedData = errors.New("oversized data")
|
ErrOversizedData = errors.New("oversized data")
|
||||||
|
|
||||||
|
// ErrAuthorityReserved is returned if a transaction has an authorization
|
||||||
|
// signed by an address which already has in-flight transactions known to the
|
||||||
|
// pool.
|
||||||
|
ErrAuthorityReserved = errors.New("authority already reserved")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -253,6 +264,20 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
||||||
// The pool separates processable transactions (which can be applied to the
|
// The pool separates processable transactions (which can be applied to the
|
||||||
// current state) and future transactions. Transactions move between those
|
// current state) and future transactions. Transactions move between those
|
||||||
// two states over time as they are received and processed.
|
// two states over time as they are received and processed.
|
||||||
|
//
|
||||||
|
// In addition to tracking transactions, the pool also tracks a set of pending SetCode
|
||||||
|
// authorizations (EIP7702). This helps minimize number of transactions that can be
|
||||||
|
// trivially churned in the pool. As a standard rule, any account with a deployed
|
||||||
|
// delegation or an in-flight authorization to deploy a delegation will only be allowed a
|
||||||
|
// single transaction slot instead of the standard number. This is due to the possibility
|
||||||
|
// of the account being sweeped by an unrelated account.
|
||||||
|
//
|
||||||
|
// Because SetCode transactions can have many authorizations included, we avoid explicitly
|
||||||
|
// checking their validity to save the state lookup. So long as the encompassing
|
||||||
|
// transaction is valid, the authorization will be accepted and tracked by the pool. In
|
||||||
|
// case the pool is tracking a pending / queued transaction from a specific account, it
|
||||||
|
// will reject new transactions with delegations from that account with standard in-flight
|
||||||
|
// transactions.
|
||||||
type TxPool struct {
|
type TxPool struct {
|
||||||
config TxPoolConfig
|
config TxPoolConfig
|
||||||
chainconfig *params.ChainConfig
|
chainconfig *params.ChainConfig
|
||||||
|
|
@ -267,6 +292,7 @@ type TxPool struct {
|
||||||
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
||||||
eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.
|
eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.
|
||||||
shanghai bool // Fork indicator whether we are in the Shanghai stage.
|
shanghai bool // Fork indicator whether we are in the Shanghai stage.
|
||||||
|
eip7702 bool // Fork indicator whether we are using EIP-7702 type transactions.
|
||||||
|
|
||||||
currentState *state.StateDB // Current state in the blockchain head
|
currentState *state.StateDB // Current state in the blockchain head
|
||||||
currentHead *big.Int // Current blockchain head
|
currentHead *big.Int // Current blockchain head
|
||||||
|
|
@ -728,6 +754,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType {
|
if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType {
|
||||||
return ErrTxTypeNotSupported
|
return ErrTxTypeNotSupported
|
||||||
}
|
}
|
||||||
|
// Reject set code transactions until EIP-7702 activates.
|
||||||
|
if !pool.eip7702 && tx.Type() == types.SetCodeTxType {
|
||||||
|
return ErrTxTypeNotSupported
|
||||||
|
}
|
||||||
// Reject transactions over defined size to prevent DOS attacks
|
// Reject transactions over defined size to prevent DOS attacks
|
||||||
if uint64(tx.Size()) > txMaxSize {
|
if uint64(tx.Size()) > txMaxSize {
|
||||||
return ErrOversizedData
|
return ErrOversizedData
|
||||||
|
|
@ -779,6 +809,25 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
|
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
|
||||||
return ErrInsufficientFunds
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
|
list := pool.pending[from]
|
||||||
|
if list == nil || !list.Overlaps(tx) {
|
||||||
|
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
|
||||||
|
// overflow the number of permitted transactions from a single account
|
||||||
|
// (i.e. max cancellable via out-of-bound transaction).
|
||||||
|
if used, left := usedAndLeftSlots(pool, from); left <= 0 {
|
||||||
|
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
|
||||||
|
}
|
||||||
|
// Verify no authorizations will invalidate existing transactions known to
|
||||||
|
// the pool.
|
||||||
|
if conflicts := knownConflicts(pool, tx.SetCodeAuthorities()); len(conflicts) > 0 {
|
||||||
|
return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tx.Type() == types.SetCodeTxType {
|
||||||
|
if len(tx.SetCodeAuthorizations()) == 0 {
|
||||||
|
return fmt.Errorf("set code tx must have at least one authorization tuple")
|
||||||
|
}
|
||||||
|
}
|
||||||
// 2. If FeeVault is enabled, perform an additional check for L1 data fees.
|
// 2. If FeeVault is enabled, perform an additional check for L1 data fees.
|
||||||
if pool.chainconfig.Scroll.FeeVaultEnabled() {
|
if pool.chainconfig.Scroll.FeeVaultEnabled() {
|
||||||
// Get L1 data fee in current state
|
// Get L1 data fee in current state
|
||||||
|
|
@ -793,7 +842,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ensure the transaction has more gas than the basic tx fee.
|
// Ensure the transaction has more gas than the basic tx fee.
|
||||||
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1514,6 +1563,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||||
pool.eip2718 = pool.chainconfig.IsCurie(next)
|
pool.eip2718 = pool.chainconfig.IsCurie(next)
|
||||||
pool.eip1559 = pool.chainconfig.IsCurie(next)
|
pool.eip1559 = pool.chainconfig.IsCurie(next)
|
||||||
pool.shanghai = pool.chainconfig.IsShanghai(next)
|
pool.shanghai = pool.chainconfig.IsShanghai(next)
|
||||||
|
pool.eip7702 = pool.chainconfig.IsEuclidV2(newHead.Time)
|
||||||
|
|
||||||
// Update current head
|
// Update current head
|
||||||
pool.currentHead = next
|
pool.currentHead = next
|
||||||
|
|
@ -1966,6 +2016,8 @@ type txLookup struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
locals map[common.Hash]*types.Transaction
|
locals map[common.Hash]*types.Transaction
|
||||||
remotes map[common.Hash]*types.Transaction
|
remotes map[common.Hash]*types.Transaction
|
||||||
|
|
||||||
|
auths map[common.Address][]common.Hash // All accounts with a pooled authorization
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxLookup returns a new txLookup structure.
|
// newTxLookup returns a new txLookup structure.
|
||||||
|
|
@ -1973,6 +2025,7 @@ func newTxLookup() *txLookup {
|
||||||
return &txLookup{
|
return &txLookup{
|
||||||
locals: make(map[common.Hash]*types.Transaction),
|
locals: make(map[common.Hash]*types.Transaction),
|
||||||
remotes: make(map[common.Hash]*types.Transaction),
|
remotes: make(map[common.Hash]*types.Transaction),
|
||||||
|
auths: make(map[common.Address][]common.Hash),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2071,6 +2124,7 @@ func (t *txLookup) Add(tx *types.Transaction, local bool) {
|
||||||
} else {
|
} else {
|
||||||
t.remotes[tx.Hash()] = tx
|
t.remotes[tx.Hash()] = tx
|
||||||
}
|
}
|
||||||
|
t.addAuthorities(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes a transaction from the lookup.
|
// Remove removes a transaction from the lookup.
|
||||||
|
|
@ -2078,6 +2132,7 @@ func (t *txLookup) Remove(hash common.Hash) {
|
||||||
t.lock.Lock()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
t.removeAuthorities(hash)
|
||||||
tx, ok := t.locals[hash]
|
tx, ok := t.locals[hash]
|
||||||
if !ok {
|
if !ok {
|
||||||
tx, ok = t.remotes[hash]
|
tx, ok = t.remotes[hash]
|
||||||
|
|
@ -2122,7 +2177,75 @@ func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addAuthorities tracks the supplied tx in relation to each authority it
|
||||||
|
// specifies.
|
||||||
|
func (t *txLookup) addAuthorities(tx *types.Transaction) {
|
||||||
|
for _, addr := range tx.SetCodeAuthorities() {
|
||||||
|
list, ok := t.auths[addr]
|
||||||
|
if !ok {
|
||||||
|
list = []common.Hash{}
|
||||||
|
}
|
||||||
|
if slices.Contains(list, tx.Hash()) {
|
||||||
|
// Don't add duplicates.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, tx.Hash())
|
||||||
|
t.auths[addr] = list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeAuthorities stops tracking the supplied tx in relation to its
|
||||||
|
// authorities.
|
||||||
|
func (t *txLookup) removeAuthorities(hash common.Hash) {
|
||||||
|
for addr := range t.auths {
|
||||||
|
list := t.auths[addr]
|
||||||
|
// Remove tx from tracker.
|
||||||
|
if i := slices.Index(list, hash); i >= 0 {
|
||||||
|
list = append(list[:i], list[i+1:]...)
|
||||||
|
} else {
|
||||||
|
log.Error("Authority with untracked tx", "addr", addr, "hash", hash)
|
||||||
|
}
|
||||||
|
if len(list) == 0 {
|
||||||
|
// If list is newly empty, delete it entirely.
|
||||||
|
delete(t.auths, addr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.auths[addr] = list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// numSlots calculates the number of slots needed for a single transaction.
|
// numSlots calculates the number of slots needed for a single transaction.
|
||||||
func numSlots(tx *types.Transaction) int {
|
func numSlots(tx *types.Transaction) int {
|
||||||
return int((tx.Size() + txSlotSize - 1) / txSlotSize)
|
return int((tx.Size() + txSlotSize - 1) / txSlotSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usedAndLeftSlots returns the number of slots used and left for the given address.
|
||||||
|
func usedAndLeftSlots(pool *TxPool, addr common.Address) (int, int) {
|
||||||
|
var have int
|
||||||
|
if list := pool.pending[addr]; list != nil {
|
||||||
|
have += list.Len()
|
||||||
|
}
|
||||||
|
if list := pool.queue[addr]; list != nil {
|
||||||
|
have += list.Len()
|
||||||
|
}
|
||||||
|
if pool.currentState.GetKeccakCodeHash(addr) != codehash.EmptyKeccakCodeHash || len(pool.all.auths[addr]) != 0 {
|
||||||
|
// Allow at most one in-flight tx for delegated accounts or those with
|
||||||
|
// a pending authorization.
|
||||||
|
return have, max(0, 1-have)
|
||||||
|
}
|
||||||
|
return have, math.MaxInt
|
||||||
|
}
|
||||||
|
|
||||||
|
// knownConflicts returns a list of addresses that conflict with the given authorities.
|
||||||
|
func knownConflicts(pool *TxPool, auths []common.Address) []common.Address {
|
||||||
|
var conflicts []common.Address
|
||||||
|
// Authorities cannot conflict with any pending or queued transactions.
|
||||||
|
for _, addr := range auths {
|
||||||
|
if list := pool.pending[addr]; list != nil {
|
||||||
|
conflicts = append(conflicts, addr)
|
||||||
|
} else if list := pool.queue[addr]; list != nil {
|
||||||
|
conflicts = append(conflicts, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conflicts
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ import (
|
||||||
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||||
"github.com/scroll-tech/go-ethereum/core/state"
|
"github.com/scroll-tech/go-ethereum/core/state"
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
"github.com/scroll-tech/go-ethereum/crypto"
|
"github.com/scroll-tech/go-ethereum/crypto"
|
||||||
"github.com/scroll-tech/go-ethereum/ethdb"
|
"github.com/scroll-tech/go-ethereum/ethdb"
|
||||||
"github.com/scroll-tech/go-ethereum/event"
|
"github.com/scroll-tech/go-ethereum/event"
|
||||||
|
|
@ -135,6 +137,39 @@ func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int,
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unsignedAuth struct {
|
||||||
|
nonce uint64
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCodeTx(nonce uint64, key *ecdsa.PrivateKey, unsigned []unsignedAuth) *types.Transaction {
|
||||||
|
return pricedSetCodeTx(nonce, 250000, uint256.NewInt(1000), uint256.NewInt(1), key, unsigned)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pricedSetCodeTx(nonce uint64, gaslimit uint64, gasFee, tip *uint256.Int, key *ecdsa.PrivateKey, unsigned []unsignedAuth) *types.Transaction {
|
||||||
|
var authList []types.SetCodeAuthorization
|
||||||
|
for _, u := range unsigned {
|
||||||
|
auth, _ := types.SignSetCode(u.key, types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(params.TestChainConfig.ChainID),
|
||||||
|
Address: common.Address{0x42},
|
||||||
|
Nonce: u.nonce,
|
||||||
|
})
|
||||||
|
authList = append(authList, auth)
|
||||||
|
}
|
||||||
|
return types.MustSignNewTx(key, types.LatestSignerForChainID(params.TestChainConfig.ChainID), &types.SetCodeTx{
|
||||||
|
ChainID: uint256.MustFromBig(params.TestChainConfig.ChainID),
|
||||||
|
Nonce: nonce,
|
||||||
|
GasTipCap: tip,
|
||||||
|
GasFeeCap: gasFee,
|
||||||
|
Gas: gaslimit,
|
||||||
|
To: common.Address{},
|
||||||
|
Value: uint256.NewInt(100),
|
||||||
|
Data: nil,
|
||||||
|
AccessList: nil,
|
||||||
|
AuthList: authList,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||||
return setupTxPoolWithConfig(params.TestChainConfig)
|
return setupTxPoolWithConfig(params.TestChainConfig)
|
||||||
}
|
}
|
||||||
|
|
@ -2631,6 +2666,215 @@ func TestTransactionSlotCount(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSetCodeTransactions tests a few scenarios regarding the EIP-7702
|
||||||
|
// SetCodeTx.
|
||||||
|
func TestSetCodeTransactions(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create the test accounts
|
||||||
|
var (
|
||||||
|
keyA, _ = crypto.GenerateKey()
|
||||||
|
keyB, _ = crypto.GenerateKey()
|
||||||
|
keyC, _ = crypto.GenerateKey()
|
||||||
|
addrA = crypto.PubkeyToAddress(keyA.PublicKey)
|
||||||
|
addrB = crypto.PubkeyToAddress(keyB.PublicKey)
|
||||||
|
addrC = crypto.PubkeyToAddress(keyC.PublicKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
pending int
|
||||||
|
queued int
|
||||||
|
run func(string, *TxPool, *state.StateDB)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// Check that only one in-flight transaction is allowed for accounts
|
||||||
|
// with delegation set. Also verify the accepted transaction can be
|
||||||
|
// replaced by fee.
|
||||||
|
name: "only-one-in-flight",
|
||||||
|
pending: 1,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
aa := common.Address{0xaa, 0xaa}
|
||||||
|
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...))
|
||||||
|
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)})
|
||||||
|
// Send transactions. First is accepted, second is rejected.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add remote transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrAccountLimitExceeded) {
|
||||||
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrAccountLimitExceeded, err)
|
||||||
|
}
|
||||||
|
// Also check gapped transaction.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrAccountLimitExceeded) {
|
||||||
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrAccountLimitExceeded, err)
|
||||||
|
}
|
||||||
|
// Replace by fee.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyA)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to replace with remote transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allow-setcode-tx-with-pending-authority-tx",
|
||||||
|
pending: 2,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
// Send two transactions where the first has no conflicting delegations and
|
||||||
|
// the second should be allowed despite conflicting with the authorities in 1).
|
||||||
|
if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add conflicting delegation: %v", name, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allow-one-tx-from-pooled-delegation",
|
||||||
|
pending: 2,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
// Verify C cannot originate another transaction when it has a pooled delegation.
|
||||||
|
if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyC)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with pending delegation: %v", name, err)
|
||||||
|
}
|
||||||
|
// Also check gapped transaction is rejected.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, ErrAccountLimitExceeded) {
|
||||||
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrAccountLimitExceeded, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "replace-by-fee-setcode-tx",
|
||||||
|
pending: 1,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
// 4. Fee bump the setcode tx send.
|
||||||
|
if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(2000), uint256.NewInt(2), keyB, []unsignedAuth{{0, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allow-tx-from-replaced-authority",
|
||||||
|
pending: 2,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
// Fee bump with a different auth list. Make sure that unlocks the authorities.
|
||||||
|
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyB}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(3000), uint256.NewInt(300), keyA, []unsignedAuth{{0, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
// Now send a regular tx from B.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyB)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to replace with remote transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allow-tx-from-replaced-self-sponsor-authority",
|
||||||
|
pending: 2,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyA}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(30), uint256.NewInt(30), keyA, []unsignedAuth{{0, keyB}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
// Now send a regular tx from keyA.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to replace with remote transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
// Make sure we can still send from keyB.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyB)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to replace with remote transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "track-multiple-conflicting-delegations",
|
||||||
|
pending: 3,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
// Send two setcode txs both with C as an authority.
|
||||||
|
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(30), uint256.NewInt(30), keyB, []unsignedAuth{{0, keyC}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
// Replace the tx from A with a non-setcode tx.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to replace with remote transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
// Make sure we can only pool one tx from keyC since it is still a
|
||||||
|
// pending authority.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to added single pooled for account with pending delegation: %v", name, err)
|
||||||
|
}
|
||||||
|
if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), ErrAccountLimitExceeded; !errors.Is(err, want) {
|
||||||
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reject-delegation-from-pending-account",
|
||||||
|
pending: 1,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
// Attempt to submit a delegation from an account with a pending tx.
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), ErrAuthorityReserved; !errors.Is(err, want) {
|
||||||
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nonce-gapped-invalid-auth-does-not-block-pending-tx",
|
||||||
|
pending: 1,
|
||||||
|
queued: 1,
|
||||||
|
run: func(name string, pool *TxPool, statedb *state.StateDB) {
|
||||||
|
if err := pool.addRemoteSync(setCodeTx(1, keyC, []unsignedAuth{{0, keyA}})); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add nonce-gapped setcode transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil {
|
||||||
|
t.Fatalf("%s: failed to add pending transaction: %v", name, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
// Create the pool to test the status retrievals with
|
||||||
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
|
blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
|
||||||
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||||
|
defer pool.Stop()
|
||||||
|
|
||||||
|
testAddBalance(pool, addrA, big.NewInt(params.Ether))
|
||||||
|
testAddBalance(pool, addrB, big.NewInt(params.Ether))
|
||||||
|
testAddBalance(pool, addrC, big.NewInt(params.Ether))
|
||||||
|
|
||||||
|
aa := common.Address{0xaa, 0xaa}
|
||||||
|
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...))
|
||||||
|
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)})
|
||||||
|
|
||||||
|
tt.run(tt.name, pool, statedb)
|
||||||
|
pending, queued := pool.Stats()
|
||||||
|
if pending != tt.pending {
|
||||||
|
t.Fatalf("%s: pending transactions mismatched: have %d, want %d", tt.name, pending, tt.pending)
|
||||||
|
}
|
||||||
|
if queued != tt.queued {
|
||||||
|
t.Fatalf("%s: queued transactions mismatched: have %d, want %d", tt.name, queued, tt.queued)
|
||||||
|
}
|
||||||
|
if err := validateTxPoolInternals(pool); err != nil {
|
||||||
|
t.Fatalf("%s: pool internal state corrupted: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Benchmarks the speed of validating the contents of the pending queue of the
|
// Benchmarks the speed of validating the contents of the pending queue of the
|
||||||
// transaction pool.
|
// transaction pool.
|
||||||
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
|
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,9 @@ type BlobTx struct {
|
||||||
Sidecar *BlobTxSidecar `rlp:"-"`
|
Sidecar *BlobTxSidecar `rlp:"-"`
|
||||||
|
|
||||||
// Signature values
|
// Signature values
|
||||||
V *uint256.Int `json:"v" gencodec:"required"`
|
V *uint256.Int `json:"v"`
|
||||||
R *uint256.Int `json:"r" gencodec:"required"`
|
R *uint256.Int `json:"r"`
|
||||||
S *uint256.Int `json:"s" gencodec:"required"`
|
S *uint256.Int `json:"s"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlobTxSidecar contains the blobs of a blob transaction.
|
// BlobTxSidecar contains the blobs of a blob transaction.
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,9 @@ type DynamicFeeTx struct {
|
||||||
AccessList AccessList
|
AccessList AccessList
|
||||||
|
|
||||||
// Signature values
|
// Signature values
|
||||||
V *big.Int `json:"v" gencodec:"required"`
|
V *big.Int `json:"v"`
|
||||||
R *big.Int `json:"r" gencodec:"required"`
|
R *big.Int `json:"r"`
|
||||||
S *big.Int `json:"s" gencodec:"required"`
|
S *big.Int `json:"s"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||||
|
|
|
||||||
76
core/types/gen_authorization.go
Normal file
76
core/types/gen_authorization.go
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*authorizationMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (s SetCodeAuthorization) MarshalJSON() ([]byte, error) {
|
||||||
|
type SetCodeAuthorization struct {
|
||||||
|
ChainID hexutil.U256 `json:"chainId" gencodec:"required"`
|
||||||
|
Address common.Address `json:"address" gencodec:"required"`
|
||||||
|
Nonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||||
|
V hexutil.Uint64 `json:"yParity" gencodec:"required"`
|
||||||
|
R hexutil.U256 `json:"r" gencodec:"required"`
|
||||||
|
S hexutil.U256 `json:"s" gencodec:"required"`
|
||||||
|
}
|
||||||
|
var enc SetCodeAuthorization
|
||||||
|
enc.ChainID = hexutil.U256(s.ChainID)
|
||||||
|
enc.Address = s.Address
|
||||||
|
enc.Nonce = hexutil.Uint64(s.Nonce)
|
||||||
|
enc.V = hexutil.Uint64(s.V)
|
||||||
|
enc.R = hexutil.U256(s.R)
|
||||||
|
enc.S = hexutil.U256(s.S)
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (s *SetCodeAuthorization) UnmarshalJSON(input []byte) error {
|
||||||
|
type SetCodeAuthorization struct {
|
||||||
|
ChainID *hexutil.U256 `json:"chainId" gencodec:"required"`
|
||||||
|
Address *common.Address `json:"address" gencodec:"required"`
|
||||||
|
Nonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||||
|
V *hexutil.Uint64 `json:"yParity" gencodec:"required"`
|
||||||
|
R *hexutil.U256 `json:"r" gencodec:"required"`
|
||||||
|
S *hexutil.U256 `json:"s" gencodec:"required"`
|
||||||
|
}
|
||||||
|
var dec SetCodeAuthorization
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.ChainID == nil {
|
||||||
|
return errors.New("missing required field 'chainId' for SetCodeAuthorization")
|
||||||
|
}
|
||||||
|
s.ChainID = uint256.Int(*dec.ChainID)
|
||||||
|
if dec.Address == nil {
|
||||||
|
return errors.New("missing required field 'address' for SetCodeAuthorization")
|
||||||
|
}
|
||||||
|
s.Address = *dec.Address
|
||||||
|
if dec.Nonce == nil {
|
||||||
|
return errors.New("missing required field 'nonce' for SetCodeAuthorization")
|
||||||
|
}
|
||||||
|
s.Nonce = uint64(*dec.Nonce)
|
||||||
|
if dec.V == nil {
|
||||||
|
return errors.New("missing required field 'yParity' for SetCodeAuthorization")
|
||||||
|
}
|
||||||
|
s.V = uint8(*dec.V)
|
||||||
|
if dec.R == nil {
|
||||||
|
return errors.New("missing required field 'r' for SetCodeAuthorization")
|
||||||
|
}
|
||||||
|
s.R = uint256.Int(*dec.R)
|
||||||
|
if dec.S == nil {
|
||||||
|
return errors.New("missing required field 's' for SetCodeAuthorization")
|
||||||
|
}
|
||||||
|
s.S = uint256.Int(*dec.S)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -138,6 +138,7 @@ type TransactionData struct {
|
||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
IsCreate bool `json:"isCreate"`
|
IsCreate bool `json:"isCreate"`
|
||||||
AccessList AccessList `json:"accessList"`
|
AccessList AccessList `json:"accessList"`
|
||||||
|
AuthorizationList []SetCodeAuthorization `json:"authorizationList"`
|
||||||
V *hexutil.Big `json:"v"`
|
V *hexutil.Big `json:"v"`
|
||||||
R *hexutil.Big `json:"r"`
|
R *hexutil.Big `json:"r"`
|
||||||
S *hexutil.Big `json:"s"`
|
S *hexutil.Big `json:"s"`
|
||||||
|
|
@ -145,8 +146,8 @@ type TransactionData struct {
|
||||||
|
|
||||||
// NewTransactionData returns a transaction that will serialize to the trace
|
// NewTransactionData returns a transaction that will serialize to the trace
|
||||||
// representation, with the given location metadata set (if available).
|
// representation, with the given location metadata set (if available).
|
||||||
func NewTransactionData(tx *Transaction, blockNumber uint64, config *params.ChainConfig) *TransactionData {
|
func NewTransactionData(tx *Transaction, blockNumber, blockTime uint64, config *params.ChainConfig) *TransactionData {
|
||||||
signer := MakeSigner(config, big.NewInt(0).SetUint64(blockNumber))
|
signer := MakeSigner(config, big.NewInt(0).SetUint64(blockNumber), blockTime)
|
||||||
from, _ := Sender(signer, tx)
|
from, _ := Sender(signer, tx)
|
||||||
v, r, s := tx.RawSignatureValues()
|
v, r, s := tx.RawSignatureValues()
|
||||||
|
|
||||||
|
|
@ -170,6 +171,7 @@ func NewTransactionData(tx *Transaction, blockNumber uint64, config *params.Chai
|
||||||
Data: hexutil.Encode(tx.Data()),
|
Data: hexutil.Encode(tx.Data()),
|
||||||
IsCreate: tx.To() == nil,
|
IsCreate: tx.To() == nil,
|
||||||
AccessList: tx.AccessList(),
|
AccessList: tx.AccessList(),
|
||||||
|
AuthorizationList: tx.SetCodeAuthorizations(),
|
||||||
V: (*hexutil.Big)(v),
|
V: (*hexutil.Big)(v),
|
||||||
R: (*hexutil.Big)(r),
|
R: (*hexutil.Big)(r),
|
||||||
S: (*hexutil.Big)(s),
|
S: (*hexutil.Big)(s),
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
|
||||||
return errEmptyTypedReceipt
|
return errEmptyTypedReceipt
|
||||||
}
|
}
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case DynamicFeeTxType, AccessListTxType, BlobTxType, L1MessageTxType:
|
case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType, L1MessageTxType:
|
||||||
var data receiptRLP
|
var data receiptRLP
|
||||||
err := rlp.DecodeBytes(b[1:], &data)
|
err := rlp.DecodeBytes(b[1:], &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -419,20 +419,13 @@ func (rs Receipts) Len() int { return len(rs) }
|
||||||
func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
||||||
r := rs[i]
|
r := rs[i]
|
||||||
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
||||||
|
if r.Type == LegacyTxType {
|
||||||
|
rlp.Encode(w, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteByte(r.Type)
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case LegacyTxType:
|
case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType, L1MessageTxType:
|
||||||
rlp.Encode(w, data)
|
|
||||||
case AccessListTxType:
|
|
||||||
w.WriteByte(AccessListTxType)
|
|
||||||
rlp.Encode(w, data)
|
|
||||||
case DynamicFeeTxType:
|
|
||||||
w.WriteByte(DynamicFeeTxType)
|
|
||||||
rlp.Encode(w, data)
|
|
||||||
case BlobTxType:
|
|
||||||
w.WriteByte(BlobTxType)
|
|
||||||
rlp.Encode(w, data)
|
|
||||||
case L1MessageTxType:
|
|
||||||
w.WriteByte(L1MessageTxType)
|
|
||||||
rlp.Encode(w, data)
|
rlp.Encode(w, data)
|
||||||
default:
|
default:
|
||||||
// For unsupported types, write nothing. Since this is for
|
// For unsupported types, write nothing. Since this is for
|
||||||
|
|
@ -443,8 +436,8 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
||||||
|
|
||||||
// DeriveFields fills the receipts with their computed fields based on consensus
|
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||||
// data and contextual infos like containing block and transactions.
|
// data and contextual infos like containing block and transactions.
|
||||||
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
|
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number, time uint64, txs Transactions) error {
|
||||||
signer := MakeSigner(config, new(big.Int).SetUint64(number))
|
signer := MakeSigner(config, new(big.Int).SetUint64(number), time)
|
||||||
|
|
||||||
logIndex := uint(0)
|
logIndex := uint(0)
|
||||||
if len(txs) != len(rs) {
|
if len(txs) != len(rs) {
|
||||||
|
|
|
||||||
|
|
@ -297,13 +297,14 @@ func TestDeriveFields(t *testing.T) {
|
||||||
// Clear all the computed fields and re-derive them
|
// Clear all the computed fields and re-derive them
|
||||||
number := big.NewInt(1)
|
number := big.NewInt(1)
|
||||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||||
|
time := uint64(0)
|
||||||
|
|
||||||
clearComputedFieldsOnReceipts(t, receipts)
|
clearComputedFieldsOnReceipts(t, receipts)
|
||||||
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil {
|
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), time, txs); err != nil {
|
||||||
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
|
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
|
||||||
}
|
}
|
||||||
// Iterate over all the computed fields and check that they're correct
|
// Iterate over all the computed fields and check that they're correct
|
||||||
signer := MakeSigner(params.TestChainConfig, number)
|
signer := MakeSigner(params.TestChainConfig, number, time)
|
||||||
|
|
||||||
logIndex := uint(0)
|
logIndex := uint(0)
|
||||||
for i := range receipts {
|
for i := range receipts {
|
||||||
|
|
|
||||||
222
core/types/setcode_tx.go
Normal file
222
core/types/setcode_tx.go
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
// Copyright 2024 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"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||||
|
"github.com/scroll-tech/go-ethereum/crypto"
|
||||||
|
"github.com/scroll-tech/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DelegationPrefix is used by code to denote the account is delegating to
|
||||||
|
// another account.
|
||||||
|
var DelegationPrefix = []byte{0xef, 0x01, 0x00}
|
||||||
|
|
||||||
|
// ParseDelegation tries to parse the address from a delegation slice.
|
||||||
|
func ParseDelegation(b []byte) (common.Address, bool) {
|
||||||
|
if len(b) != 23 || !bytes.HasPrefix(b, DelegationPrefix) {
|
||||||
|
return common.Address{}, false
|
||||||
|
}
|
||||||
|
return common.BytesToAddress(b[len(DelegationPrefix):]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressToDelegation adds the delegation prefix to the specified address.
|
||||||
|
func AddressToDelegation(addr common.Address) []byte {
|
||||||
|
return append(DelegationPrefix, addr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizationResult is the result of SetCode transaction's delegations.
|
||||||
|
type AuthorizationResult struct {
|
||||||
|
Authority common.Address
|
||||||
|
PreCode []byte
|
||||||
|
Success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCodeTx implements the EIP-7702 transaction type which temporarily installs
|
||||||
|
// the code at the signer's address.
|
||||||
|
type SetCodeTx struct {
|
||||||
|
ChainID *uint256.Int
|
||||||
|
Nonce uint64
|
||||||
|
GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
|
||||||
|
GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
|
||||||
|
Gas uint64
|
||||||
|
To common.Address
|
||||||
|
Value *uint256.Int
|
||||||
|
Data []byte
|
||||||
|
AccessList AccessList
|
||||||
|
AuthList []SetCodeAuthorization
|
||||||
|
|
||||||
|
// Signature values
|
||||||
|
V *uint256.Int `json:"v"`
|
||||||
|
R *uint256.Int `json:"r"`
|
||||||
|
S *uint256.Int `json:"s"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type SetCodeAuthorization -field-override authorizationMarshaling -out gen_authorization.go
|
||||||
|
|
||||||
|
// SetCodeAuthorization is an authorization from an account to deploy code at its address.
|
||||||
|
type SetCodeAuthorization struct {
|
||||||
|
ChainID uint256.Int `json:"chainId" gencodec:"required"`
|
||||||
|
Address common.Address `json:"address" gencodec:"required"`
|
||||||
|
Nonce uint64 `json:"nonce" gencodec:"required"`
|
||||||
|
V uint8 `json:"yParity" gencodec:"required"`
|
||||||
|
R uint256.Int `json:"r" gencodec:"required"`
|
||||||
|
S uint256.Int `json:"s" gencodec:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// field type overrides for gencodec
|
||||||
|
type authorizationMarshaling struct {
|
||||||
|
ChainID hexutil.U256
|
||||||
|
Nonce hexutil.Uint64
|
||||||
|
V hexutil.Uint64
|
||||||
|
R hexutil.U256
|
||||||
|
S hexutil.U256
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignSetCode creates a signed the SetCode authorization.
|
||||||
|
func SignSetCode(prv *ecdsa.PrivateKey, auth SetCodeAuthorization) (SetCodeAuthorization, error) {
|
||||||
|
sighash := auth.sigHash()
|
||||||
|
sig, err := crypto.Sign(sighash[:], prv)
|
||||||
|
if err != nil {
|
||||||
|
return SetCodeAuthorization{}, err
|
||||||
|
}
|
||||||
|
r, s, _ := decodeSignature(sig)
|
||||||
|
return SetCodeAuthorization{
|
||||||
|
ChainID: auth.ChainID,
|
||||||
|
Address: auth.Address,
|
||||||
|
Nonce: auth.Nonce,
|
||||||
|
V: sig[64],
|
||||||
|
R: *uint256.MustFromBig(r),
|
||||||
|
S: *uint256.MustFromBig(s),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *SetCodeAuthorization) sigHash() common.Hash {
|
||||||
|
return prefixedRlpHash(0x05, []any{
|
||||||
|
a.ChainID,
|
||||||
|
a.Address,
|
||||||
|
a.Nonce,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authority recovers the the authorizing account of an authorization.
|
||||||
|
func (a *SetCodeAuthorization) Authority() (common.Address, error) {
|
||||||
|
sighash := a.sigHash()
|
||||||
|
if !crypto.ValidateSignatureValues(a.V, a.R.ToBig(), a.S.ToBig(), true) {
|
||||||
|
return common.Address{}, ErrInvalidSig
|
||||||
|
}
|
||||||
|
// encode the signature in uncompressed format
|
||||||
|
var sig [crypto.SignatureLength]byte
|
||||||
|
a.R.WriteToSlice(sig[:32])
|
||||||
|
a.S.WriteToSlice(sig[32:64])
|
||||||
|
sig[64] = a.V
|
||||||
|
// recover the public key from the signature
|
||||||
|
pub, err := crypto.Ecrecover(sighash[:], sig[:])
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
if len(pub) == 0 || pub[0] != 4 {
|
||||||
|
return common.Address{}, errors.New("invalid public key")
|
||||||
|
}
|
||||||
|
var addr common.Address
|
||||||
|
copy(addr[:], crypto.Keccak256(pub[1:])[12:])
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||||
|
func (tx *SetCodeTx) copy() TxData {
|
||||||
|
cpy := &SetCodeTx{
|
||||||
|
Nonce: tx.Nonce,
|
||||||
|
To: tx.To,
|
||||||
|
Data: common.CopyBytes(tx.Data),
|
||||||
|
Gas: tx.Gas,
|
||||||
|
// These are copied below.
|
||||||
|
AccessList: make(AccessList, len(tx.AccessList)),
|
||||||
|
AuthList: make([]SetCodeAuthorization, len(tx.AuthList)),
|
||||||
|
Value: new(uint256.Int),
|
||||||
|
ChainID: new(uint256.Int),
|
||||||
|
GasTipCap: new(uint256.Int),
|
||||||
|
GasFeeCap: new(uint256.Int),
|
||||||
|
V: new(uint256.Int),
|
||||||
|
R: new(uint256.Int),
|
||||||
|
S: new(uint256.Int),
|
||||||
|
}
|
||||||
|
copy(cpy.AccessList, tx.AccessList)
|
||||||
|
copy(cpy.AuthList, tx.AuthList)
|
||||||
|
if tx.Value != nil {
|
||||||
|
cpy.Value.Set(tx.Value)
|
||||||
|
}
|
||||||
|
if tx.ChainID != nil {
|
||||||
|
cpy.ChainID.Set(tx.ChainID)
|
||||||
|
}
|
||||||
|
if tx.GasTipCap != nil {
|
||||||
|
cpy.GasTipCap.Set(tx.GasTipCap)
|
||||||
|
}
|
||||||
|
if tx.GasFeeCap != nil {
|
||||||
|
cpy.GasFeeCap.Set(tx.GasFeeCap)
|
||||||
|
}
|
||||||
|
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 *SetCodeTx) txType() byte { return SetCodeTxType }
|
||||||
|
func (tx *SetCodeTx) chainID() *big.Int { return tx.ChainID.ToBig() }
|
||||||
|
func (tx *SetCodeTx) accessList() AccessList { return tx.AccessList }
|
||||||
|
func (tx *SetCodeTx) data() []byte { return tx.Data }
|
||||||
|
func (tx *SetCodeTx) gas() uint64 { return tx.Gas }
|
||||||
|
func (tx *SetCodeTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
|
||||||
|
func (tx *SetCodeTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
|
||||||
|
func (tx *SetCodeTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
|
||||||
|
func (tx *SetCodeTx) value() *big.Int { return tx.Value.ToBig() }
|
||||||
|
func (tx *SetCodeTx) nonce() uint64 { return tx.Nonce }
|
||||||
|
func (tx *SetCodeTx) to() *common.Address { tmp := tx.To; return &tmp }
|
||||||
|
|
||||||
|
func (tx *SetCodeTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||||
|
return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *SetCodeTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||||
|
tx.ChainID = uint256.MustFromBig(chainID)
|
||||||
|
tx.V.SetFromBig(v)
|
||||||
|
tx.R.SetFromBig(r)
|
||||||
|
tx.S.SetFromBig(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *SetCodeTx) encode(b *bytes.Buffer) error {
|
||||||
|
return rlp.Encode(b, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *SetCodeTx) decode(input []byte) error {
|
||||||
|
return rlp.DecodeBytes(input, tx)
|
||||||
|
}
|
||||||
70
core/types/setcode_tx_test.go
Normal file
70
core/types/setcode_tx_test.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright 2024 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 (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestParseDelegation tests a few possible delegation designator values and
|
||||||
|
// ensures they are parsed correctly.
|
||||||
|
func TestParseDelegation(t *testing.T) {
|
||||||
|
addr := common.Address{0x42}
|
||||||
|
for _, tt := range []struct {
|
||||||
|
val []byte
|
||||||
|
want *common.Address
|
||||||
|
}{
|
||||||
|
{ // simple correct delegation
|
||||||
|
val: append(DelegationPrefix, addr.Bytes()...),
|
||||||
|
want: &addr,
|
||||||
|
},
|
||||||
|
{ // wrong address size
|
||||||
|
val: append(DelegationPrefix, addr.Bytes()[0:19]...),
|
||||||
|
},
|
||||||
|
{ // short address
|
||||||
|
val: append(DelegationPrefix, 0x42),
|
||||||
|
},
|
||||||
|
{ // long address
|
||||||
|
val: append(append(DelegationPrefix, addr.Bytes()...), 0x42),
|
||||||
|
},
|
||||||
|
{ // wrong prefix size
|
||||||
|
val: append(DelegationPrefix[:2], addr.Bytes()...),
|
||||||
|
},
|
||||||
|
{ // wrong prefix
|
||||||
|
val: append([]byte{0xef, 0x01, 0x01}, addr.Bytes()...),
|
||||||
|
},
|
||||||
|
{ // wrong prefix
|
||||||
|
val: append([]byte{0xef, 0x00, 0x00}, addr.Bytes()...),
|
||||||
|
},
|
||||||
|
{ // no prefix
|
||||||
|
val: addr.Bytes(),
|
||||||
|
},
|
||||||
|
{ // no address
|
||||||
|
val: DelegationPrefix,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
got, ok := ParseDelegation(tt.val)
|
||||||
|
if ok && tt.want == nil {
|
||||||
|
t.Fatalf("expected fail, got %s", got.Hex())
|
||||||
|
}
|
||||||
|
if !ok && tt.want != nil {
|
||||||
|
t.Fatalf("failed to parse, want %s", tt.want.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,6 +52,7 @@ const (
|
||||||
AccessListTxType = 0x01
|
AccessListTxType = 0x01
|
||||||
DynamicFeeTxType = 0x02
|
DynamicFeeTxType = 0x02
|
||||||
BlobTxType = 0x03
|
BlobTxType = 0x03
|
||||||
|
SetCodeTxType = 0x04
|
||||||
|
|
||||||
L1MessageTxType = 0x7E
|
L1MessageTxType = 0x7E
|
||||||
)
|
)
|
||||||
|
|
@ -202,6 +203,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||||
inner = new(DynamicFeeTx)
|
inner = new(DynamicFeeTx)
|
||||||
case BlobTxType:
|
case BlobTxType:
|
||||||
inner = new(BlobTx)
|
inner = new(BlobTx)
|
||||||
|
case SetCodeTxType:
|
||||||
|
inner = new(SetCodeTx)
|
||||||
case L1MessageTxType:
|
case L1MessageTxType:
|
||||||
inner = new(L1MessageTx)
|
inner = new(L1MessageTx)
|
||||||
default:
|
default:
|
||||||
|
|
@ -465,6 +468,30 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i
|
||||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other)
|
return tx.EffectiveGasTipValue(baseFee).Cmp(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCodeAuthorizations returns the authorizations list of the transaction.
|
||||||
|
func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization {
|
||||||
|
setcodetx, ok := tx.inner.(*SetCodeTx)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return setcodetx.AuthList
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCodeAuthorities returns a list of each authorization's corresponding authority.
|
||||||
|
func (tx *Transaction) SetCodeAuthorities() []common.Address {
|
||||||
|
setcodetx, ok := tx.inner.(*SetCodeTx)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
auths := make([]common.Address, 0, len(setcodetx.AuthList))
|
||||||
|
for _, auth := range setcodetx.AuthList {
|
||||||
|
if addr, err := auth.Authority(); err == nil {
|
||||||
|
auths = append(auths, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return auths
|
||||||
|
}
|
||||||
|
|
||||||
// Hash returns the transaction hash.
|
// Hash returns the transaction hash.
|
||||||
func (tx *Transaction) Hash() common.Hash {
|
func (tx *Transaction) Hash() common.Hash {
|
||||||
if hash := tx.hash.Load(); hash != nil {
|
if hash := tx.hash.Load(); hash != nil {
|
||||||
|
|
@ -753,9 +780,10 @@ type Message struct {
|
||||||
isFake bool
|
isFake bool
|
||||||
isL1MessageTx bool
|
isL1MessageTx bool
|
||||||
txSize common.StorageSize
|
txSize common.StorageSize
|
||||||
|
setCodeAuthorizations []SetCodeAuthorization
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool) Message {
|
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool, setCodeAuthorizations []SetCodeAuthorization) Message {
|
||||||
return Message{
|
return Message{
|
||||||
from: from,
|
from: from,
|
||||||
to: to,
|
to: to,
|
||||||
|
|
@ -769,6 +797,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
||||||
accessList: accessList,
|
accessList: accessList,
|
||||||
isFake: isFake,
|
isFake: isFake,
|
||||||
isL1MessageTx: false,
|
isL1MessageTx: false,
|
||||||
|
setCodeAuthorizations: setCodeAuthorizations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -787,6 +816,7 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
|
||||||
isFake: false,
|
isFake: false,
|
||||||
isL1MessageTx: tx.IsL1MessageTx(),
|
isL1MessageTx: tx.IsL1MessageTx(),
|
||||||
txSize: tx.Size(),
|
txSize: tx.Size(),
|
||||||
|
setCodeAuthorizations: tx.SetCodeAuthorizations(),
|
||||||
}
|
}
|
||||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
||||||
if baseFee != nil {
|
if baseFee != nil {
|
||||||
|
|
@ -810,6 +840,7 @@ func (m Message) AccessList() AccessList { return m.accessList }
|
||||||
func (m Message) IsFake() bool { return m.isFake }
|
func (m Message) IsFake() bool { return m.isFake }
|
||||||
func (m Message) IsL1MessageTx() bool { return m.isL1MessageTx }
|
func (m Message) IsL1MessageTx() bool { return m.isL1MessageTx }
|
||||||
func (m Message) TxSize() common.StorageSize { return m.txSize }
|
func (m Message) TxSize() common.StorageSize { return m.txSize }
|
||||||
|
func (m Message) SetCodeAuthorizations() []SetCodeAuthorization { return m.setCodeAuthorizations }
|
||||||
|
|
||||||
// copyAddressPtr copies an address.
|
// copyAddressPtr copies an address.
|
||||||
func copyAddressPtr(a *common.Address) *common.Address {
|
func copyAddressPtr(a *common.Address) *common.Address {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ type txJSON struct {
|
||||||
Input *hexutil.Bytes `json:"input"`
|
Input *hexutil.Bytes `json:"input"`
|
||||||
AccessList *AccessList `json:"accessList,omitempty"`
|
AccessList *AccessList `json:"accessList,omitempty"`
|
||||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||||
|
AuthorizationList []SetCodeAuthorization `json:"authorizationList,omitempty"`
|
||||||
V *hexutil.Big `json:"v"`
|
V *hexutil.Big `json:"v"`
|
||||||
R *hexutil.Big `json:"r"`
|
R *hexutil.Big `json:"r"`
|
||||||
S *hexutil.Big `json:"s"`
|
S *hexutil.Big `json:"s"`
|
||||||
|
|
@ -166,6 +167,23 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||||
enc.Commitments = itx.Sidecar.Commitments
|
enc.Commitments = itx.Sidecar.Commitments
|
||||||
enc.Proofs = itx.Sidecar.Proofs
|
enc.Proofs = itx.Sidecar.Proofs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *SetCodeTx:
|
||||||
|
enc.ChainID = (*hexutil.Big)(itx.ChainID.ToBig())
|
||||||
|
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
|
||||||
|
enc.To = tx.To()
|
||||||
|
enc.Gas = (*hexutil.Uint64)(&itx.Gas)
|
||||||
|
enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap.ToBig())
|
||||||
|
enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap.ToBig())
|
||||||
|
enc.Value = (*hexutil.Big)(itx.Value.ToBig())
|
||||||
|
enc.Input = (*hexutil.Bytes)(&itx.Data)
|
||||||
|
enc.AccessList = &itx.AccessList
|
||||||
|
enc.AuthorizationList = itx.AuthList
|
||||||
|
enc.V = (*hexutil.Big)(itx.V.ToBig())
|
||||||
|
enc.R = (*hexutil.Big)(itx.R.ToBig())
|
||||||
|
enc.S = (*hexutil.Big)(itx.S.ToBig())
|
||||||
|
yparity := itx.V.Uint64()
|
||||||
|
enc.YParity = (*hexutil.Uint64)(&yparity)
|
||||||
}
|
}
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
@ -346,8 +364,10 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||||
case BlobTxType:
|
case BlobTxType:
|
||||||
var itx BlobTx
|
var itx BlobTx
|
||||||
inner = &itx
|
inner = &itx
|
||||||
if dec.ChainID == nil {
|
var overflow bool
|
||||||
return errors.New("missing required field 'chainId' in transaction")
|
itx.ChainID, overflow = uint256.FromBig(dec.ChainID.ToInt())
|
||||||
|
if overflow {
|
||||||
|
return errors.New("'chainId' value overflows uint256")
|
||||||
}
|
}
|
||||||
itx.ChainID = uint256.MustFromBig((*big.Int)(dec.ChainID))
|
itx.ChainID = uint256.MustFromBig((*big.Int)(dec.ChainID))
|
||||||
if dec.Nonce == nil {
|
if dec.Nonce == nil {
|
||||||
|
|
@ -391,7 +411,84 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||||
itx.BlobHashes = dec.BlobVersionedHashes
|
itx.BlobHashes = dec.BlobVersionedHashes
|
||||||
|
|
||||||
// signature R
|
// signature R
|
||||||
|
if dec.R == nil {
|
||||||
|
return errors.New("missing required field 'r' in transaction")
|
||||||
|
}
|
||||||
|
itx.R, overflow = uint256.FromBig((*big.Int)(dec.R))
|
||||||
|
if overflow {
|
||||||
|
return errors.New("'r' value overflows uint256")
|
||||||
|
}
|
||||||
|
// signature S
|
||||||
|
if dec.S == nil {
|
||||||
|
return errors.New("missing required field 's' in transaction")
|
||||||
|
}
|
||||||
|
itx.S, overflow = uint256.FromBig((*big.Int)(dec.S))
|
||||||
|
if overflow {
|
||||||
|
return errors.New("'s' value overflows uint256")
|
||||||
|
}
|
||||||
|
// signature V
|
||||||
|
vbig, err := dec.yParityValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
itx.V, overflow = uint256.FromBig(vbig)
|
||||||
|
if overflow {
|
||||||
|
return errors.New("'v' value overflows uint256")
|
||||||
|
}
|
||||||
|
if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {
|
||||||
|
if err := sanityCheckSignature(vbig, itx.R.ToBig(), itx.S.ToBig(), false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SetCodeTxType:
|
||||||
|
var itx SetCodeTx
|
||||||
|
inner = &itx
|
||||||
|
if dec.ChainID == nil {
|
||||||
|
return errors.New("missing required field 'chainId' in transaction")
|
||||||
|
}
|
||||||
var overflow bool
|
var overflow bool
|
||||||
|
itx.ChainID, overflow = uint256.FromBig(dec.ChainID.ToInt())
|
||||||
|
if overflow {
|
||||||
|
return errors.New("'chainId' value overflows uint256")
|
||||||
|
}
|
||||||
|
if dec.Nonce == nil {
|
||||||
|
return errors.New("missing required field 'nonce' in transaction")
|
||||||
|
}
|
||||||
|
itx.Nonce = uint64(*dec.Nonce)
|
||||||
|
if dec.To == nil {
|
||||||
|
return errors.New("missing required field 'to' in transaction")
|
||||||
|
}
|
||||||
|
itx.To = *dec.To
|
||||||
|
if dec.Gas == nil {
|
||||||
|
return errors.New("missing required field 'gas' for txdata")
|
||||||
|
}
|
||||||
|
itx.Gas = uint64(*dec.Gas)
|
||||||
|
if dec.MaxPriorityFeePerGas == nil {
|
||||||
|
return errors.New("missing required field 'maxPriorityFeePerGas' for txdata")
|
||||||
|
}
|
||||||
|
itx.GasTipCap = uint256.MustFromBig((*big.Int)(dec.MaxPriorityFeePerGas))
|
||||||
|
if dec.MaxFeePerGas == nil {
|
||||||
|
return errors.New("missing required field 'maxFeePerGas' for txdata")
|
||||||
|
}
|
||||||
|
itx.GasFeeCap = uint256.MustFromBig((*big.Int)(dec.MaxFeePerGas))
|
||||||
|
if dec.Value == nil {
|
||||||
|
return errors.New("missing required field 'value' in transaction")
|
||||||
|
}
|
||||||
|
itx.Value = uint256.MustFromBig((*big.Int)(dec.Value))
|
||||||
|
if dec.Input == nil {
|
||||||
|
return errors.New("missing required field 'input' in transaction")
|
||||||
|
}
|
||||||
|
itx.Data = *dec.Input
|
||||||
|
if dec.AccessList != nil {
|
||||||
|
itx.AccessList = *dec.AccessList
|
||||||
|
}
|
||||||
|
if dec.AuthorizationList == nil {
|
||||||
|
return errors.New("missing required field 'authorizationList' in transaction")
|
||||||
|
}
|
||||||
|
itx.AuthList = dec.AuthorizationList
|
||||||
|
|
||||||
|
// signature R
|
||||||
if dec.R == nil {
|
if dec.R == nil {
|
||||||
return errors.New("missing required field 'r' in transaction")
|
return errors.New("missing required field 'r' in transaction")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,11 @@ type sigCache struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeSigner returns a Signer based on the given chain config and block number.
|
// MakeSigner returns a Signer based on the given chain config and block number.
|
||||||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
|
||||||
var signer Signer
|
var signer Signer
|
||||||
switch {
|
switch {
|
||||||
|
case config.IsEuclidV2(blockTime):
|
||||||
|
signer = NewEuclidV2Signer(config.ChainID)
|
||||||
case config.IsCurie(blockNumber):
|
case config.IsCurie(blockNumber):
|
||||||
signer = NewLondonSignerWithEIP4844(config.ChainID)
|
signer = NewLondonSignerWithEIP4844(config.ChainID)
|
||||||
case config.IsLondon(blockNumber):
|
case config.IsLondon(blockNumber):
|
||||||
|
|
@ -65,6 +67,9 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||||
// have the current block number available, use MakeSigner instead.
|
// have the current block number available, use MakeSigner instead.
|
||||||
func LatestSigner(config *params.ChainConfig) Signer {
|
func LatestSigner(config *params.ChainConfig) Signer {
|
||||||
if config.ChainID != nil {
|
if config.ChainID != nil {
|
||||||
|
if config.EuclidV2Time != nil {
|
||||||
|
return NewEuclidV2Signer(config.ChainID)
|
||||||
|
}
|
||||||
if config.CurieBlock != nil {
|
if config.CurieBlock != nil {
|
||||||
return NewLondonSignerWithEIP4844(config.ChainID)
|
return NewLondonSignerWithEIP4844(config.ChainID)
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +97,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer {
|
||||||
if chainID == nil {
|
if chainID == nil {
|
||||||
return HomesteadSigner{}
|
return HomesteadSigner{}
|
||||||
}
|
}
|
||||||
return NewLondonSignerWithEIP4844(chainID)
|
return NewEuclidV2Signer(chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTx signs the transaction using the given signer and private key.
|
// SignTx signs the transaction using the given signer and private key.
|
||||||
|
|
@ -175,6 +180,77 @@ type Signer interface {
|
||||||
Equal(Signer) bool
|
Equal(Signer) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type euclidV2Signer struct{ londonSignerWithEIP4844 }
|
||||||
|
|
||||||
|
// NewEuclidV2Signer returns a signer that accepts
|
||||||
|
// - EIP-7702 set code transactions
|
||||||
|
// - EIP-4844 blob transactions
|
||||||
|
// - EIP-1559 dynamic fee transactions
|
||||||
|
// - EIP-2930 access list transactions,
|
||||||
|
// - EIP-155 replay protected transactions, and
|
||||||
|
// - legacy Homestead transactions.
|
||||||
|
func NewEuclidV2Signer(chainId *big.Int) Signer {
|
||||||
|
signer, _ := NewLondonSignerWithEIP4844(chainId).(londonSignerWithEIP4844)
|
||||||
|
return euclidV2Signer{signer}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s euclidV2Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||||
|
if tx.Type() != SetCodeTxType {
|
||||||
|
return s.londonSignerWithEIP4844.Sender(tx)
|
||||||
|
}
|
||||||
|
V, R, S := tx.RawSignatureValues()
|
||||||
|
|
||||||
|
// Set code txs are defined to use 0 and 1 as their recovery
|
||||||
|
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
||||||
|
V = new(big.Int).Add(V, big.NewInt(27))
|
||||||
|
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||||
|
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
|
||||||
|
}
|
||||||
|
return recoverPlain(s.Hash(tx), R, S, V, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s euclidV2Signer) Equal(s2 Signer) bool {
|
||||||
|
x, ok := s2.(euclidV2Signer)
|
||||||
|
return ok && x.chainId.Cmp(s.chainId) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s euclidV2Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||||
|
txdata, ok := tx.inner.(*SetCodeTx)
|
||||||
|
if !ok {
|
||||||
|
return s.londonSignerWithEIP4844.SignatureValues(tx, sig)
|
||||||
|
}
|
||||||
|
// Check that chain ID of tx matches the signer. We also accept ID zero here,
|
||||||
|
// because it indicates that the chain ID was not specified in the tx.
|
||||||
|
if txdata.ChainID != nil && txdata.ChainID.CmpBig(s.chainId) != 0 {
|
||||||
|
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
|
||||||
|
}
|
||||||
|
R, S, _ = decodeSignature(sig)
|
||||||
|
V = big.NewInt(int64(sig[64]))
|
||||||
|
return R, S, V, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash to be signed by the sender.
|
||||||
|
// It does not uniquely identify the transaction.
|
||||||
|
func (s euclidV2Signer) Hash(tx *Transaction) common.Hash {
|
||||||
|
if tx.Type() != SetCodeTxType {
|
||||||
|
return s.londonSignerWithEIP4844.Hash(tx)
|
||||||
|
}
|
||||||
|
return prefixedRlpHash(
|
||||||
|
tx.Type(),
|
||||||
|
[]interface{}{
|
||||||
|
s.chainId,
|
||||||
|
tx.Nonce(),
|
||||||
|
tx.GasTipCap(),
|
||||||
|
tx.GasFeeCap(),
|
||||||
|
tx.Gas(),
|
||||||
|
tx.To(),
|
||||||
|
tx.Value(),
|
||||||
|
tx.Data(),
|
||||||
|
tx.AccessList(),
|
||||||
|
tx.SetCodeAuthorizations(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type londonSignerWithEIP4844 struct{ londonSigner }
|
type londonSignerWithEIP4844 struct{ londonSigner }
|
||||||
|
|
||||||
// NewLondonSignerWithEIP4844 returns a signer that accepts
|
// NewLondonSignerWithEIP4844 returns a signer that accepts
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,14 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
|
for _, auth := range authorizationResults {
|
||||||
|
if auth.Success {
|
||||||
|
if _, ok := a.excl[auth.Authority]; !ok {
|
||||||
|
a.list.addAddress(auth.Authority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ var activators = map[int]func(*JumpTable){
|
||||||
1884: enable1884,
|
1884: enable1884,
|
||||||
1344: enable1344,
|
1344: enable1344,
|
||||||
1153: enable1153,
|
1153: enable1153,
|
||||||
|
7702: enable7702,
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableEIP enables the given EIP on the config.
|
// EnableEIP enables the given EIP on the config.
|
||||||
|
|
@ -271,3 +272,11 @@ func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
||||||
scope.Memory.Copy(dst.Uint64(), src.Uint64(), length.Uint64())
|
scope.Memory.Copy(dst.Uint64(), src.Uint64(), length.Uint64())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enable7702 the EIP-7702 changes to support delegation designators.
|
||||||
|
func enable7702(jt *JumpTable) {
|
||||||
|
jt[CALL].dynamicGas = gasCallEIP7702
|
||||||
|
jt[CALLCODE].dynamicGas = gasCallCodeEIP7702
|
||||||
|
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
|
||||||
|
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,3 +67,8 @@ type ErrInvalidOpCode struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
|
func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
|
||||||
|
|
||||||
|
// NewErrInvalidOpCode is only used in tests
|
||||||
|
func NewErrInvalidOpCode(opcode OpCode) *ErrInvalidOpCode {
|
||||||
|
return &ErrInvalidOpCode{opcode: opcode}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/crypto"
|
"github.com/scroll-tech/go-ethereum/crypto"
|
||||||
"github.com/scroll-tech/go-ethereum/crypto/codehash"
|
"github.com/scroll-tech/go-ethereum/crypto/codehash"
|
||||||
"github.com/scroll-tech/go-ethereum/params"
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
|
|
@ -175,7 +176,7 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
|
||||||
// parameters. It also handles any necessary value transfer required and takes
|
// parameters. It also handles any necessary value transfer required and takes
|
||||||
// the necessary steps to create accounts and reverses the state in case of an
|
// the necessary steps to create accounts and reverses the state in case of an
|
||||||
// execution error or failed value transfer.
|
// execution error or failed value transfer.
|
||||||
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) (ret []byte, leftOverGas uint64, err error) {
|
||||||
if evm.Config.NoRecursion && evm.depth > 0 {
|
if evm.Config.NoRecursion && evm.depth > 0 {
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +196,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// Calling a non existing account, don't do anything, but ping the tracer
|
// Calling a non existing account, don't do anything, but ping the tracer
|
||||||
if evm.Config.Debug {
|
if evm.Config.Debug {
|
||||||
if evm.depth == 0 {
|
if evm.depth == 0 {
|
||||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value, authorizationResults)
|
||||||
evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
|
evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
|
||||||
} else {
|
} else {
|
||||||
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||||
|
|
@ -211,7 +212,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// Capture the tracer start/end events in debug mode
|
// Capture the tracer start/end events in debug mode
|
||||||
if evm.Config.Debug {
|
if evm.Config.Debug {
|
||||||
if evm.depth == 0 {
|
if evm.depth == 0 {
|
||||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value, authorizationResults)
|
||||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||||
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
|
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
|
||||||
}(gas, time.Now())
|
}(gas, time.Now())
|
||||||
|
|
@ -229,7 +230,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
// The contract is a scoped environment for this execution context only.
|
// The contract is a scoped environment for this execution context only.
|
||||||
code := evm.StateDB.GetCode(addr)
|
code := evm.resolveCode(addr)
|
||||||
if len(code) == 0 {
|
if len(code) == 0 {
|
||||||
ret, err = nil, nil // gas is unchanged
|
ret, err = nil, nil // gas is unchanged
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -237,7 +238,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// If the account has no code, we can abort here
|
// If the account has no code, we can abort here
|
||||||
// The depth-check is already done, and precompiles handled above
|
// The depth-check is already done, and precompiles handled above
|
||||||
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
||||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), code)
|
contract.SetCallCode(&addrCopy, evm.resolveKeccakCodeHash(addrCopy), code)
|
||||||
ret, err = evm.interpreter.Run(contract, input, false)
|
ret, err = evm.interpreter.Run(contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +298,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
// The contract is a scoped environment for this execution context only.
|
// The contract is a scoped environment for this execution context only.
|
||||||
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
|
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
|
||||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
contract.SetCallCode(&addrCopy, evm.resolveKeccakCodeHash(addrCopy), evm.resolveCode(addrCopy))
|
||||||
ret, err = evm.interpreter.Run(contract, input, false)
|
ret, err = evm.interpreter.Run(contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
|
|
@ -340,7 +341,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
addrCopy := addr
|
addrCopy := addr
|
||||||
// Initialise a new contract and make initialise the delegate values
|
// Initialise a new contract and make initialise the delegate values
|
||||||
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
|
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
|
||||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
contract.SetCallCode(&addrCopy, evm.resolveKeccakCodeHash(addrCopy), evm.resolveCode(addrCopy))
|
||||||
ret, err = evm.interpreter.Run(contract, input, false)
|
ret, err = evm.interpreter.Run(contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +397,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
// The contract is a scoped environment for this execution context only.
|
// The contract is a scoped environment for this execution context only.
|
||||||
contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
|
contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
|
||||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetKeccakCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
contract.SetCallCode(&addrCopy, evm.resolveKeccakCodeHash(addrCopy), evm.resolveCode(addrCopy))
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
// When an error was returned by the EVM or when setting the creation code
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||||
// when we're in Homestead this also counts for code storage gas errors.
|
// when we're in Homestead this also counts for code storage gas errors.
|
||||||
|
|
@ -469,7 +470,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
|
|
||||||
if evm.Config.Debug {
|
if evm.Config.Debug {
|
||||||
if evm.depth == 0 {
|
if evm.depth == 0 {
|
||||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
|
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value, nil)
|
||||||
} else {
|
} else {
|
||||||
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
|
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
|
||||||
}
|
}
|
||||||
|
|
@ -538,6 +539,35 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *
|
||||||
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
|
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveCode returns the code associated with the provided account. After
|
||||||
|
// EuclidV2, it can also resolve code pointed to by a delegation designator.
|
||||||
|
func (evm *EVM) resolveCode(addr common.Address) []byte {
|
||||||
|
code := evm.StateDB.GetCode(addr)
|
||||||
|
if !evm.chainRules.IsEuclidV2 {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
if target, ok := types.ParseDelegation(code); ok {
|
||||||
|
// Note we only follow one level of delegation.
|
||||||
|
return evm.StateDB.GetCode(target)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveKeccakCodeHash returns the code hash associated with the provided address.
|
||||||
|
// After EuclidV2, it can also resolve code hash of the account pointed to by a
|
||||||
|
// delegation designator. Although this is not accessible in the EVM it is used
|
||||||
|
// internally to associate jumpdest analysis to code.
|
||||||
|
func (evm *EVM) resolveKeccakCodeHash(addr common.Address) common.Hash {
|
||||||
|
if evm.chainRules.IsEuclidV2 {
|
||||||
|
code := evm.StateDB.GetCode(addr)
|
||||||
|
if target, ok := types.ParseDelegation(code); ok {
|
||||||
|
// Note we only follow one level of delegation.
|
||||||
|
return evm.StateDB.GetKeccakCodeHash(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return evm.StateDB.GetKeccakCodeHash(addr)
|
||||||
|
}
|
||||||
|
|
||||||
// ChainConfig returns the environment's chain configuration
|
// ChainConfig returns the environment's chain configuration
|
||||||
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ func TestEIP2200(t *testing.T) {
|
||||||
}
|
}
|
||||||
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
|
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
|
||||||
|
|
||||||
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
|
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int), nil)
|
||||||
if err != tt.failure {
|
if err != tt.failure {
|
||||||
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
|
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +151,7 @@ func TestCreateGas(t *testing.T) {
|
||||||
|
|
||||||
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
|
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
|
||||||
var startGas = uint64(testGas)
|
var startGas = uint64(testGas)
|
||||||
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
|
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -676,7 +676,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||||
bigVal = value.ToBig()
|
bigVal = value.ToBig()
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal)
|
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||||
if cfg.JumpTable[STOP] == nil {
|
if cfg.JumpTable[STOP] == nil {
|
||||||
var jt JumpTable
|
var jt JumpTable
|
||||||
switch {
|
switch {
|
||||||
|
case evm.chainRules.IsEuclidV2:
|
||||||
|
jt = euclidV2InstructionSet
|
||||||
case evm.chainRules.IsDarwin:
|
case evm.chainRules.IsDarwin:
|
||||||
jt = darwinInstructionSet
|
jt = darwinInstructionSet
|
||||||
case evm.chainRules.IsCurie:
|
case evm.chainRules.IsCurie:
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,20 @@ var (
|
||||||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||||
curieInstructionSet = newCurieInstructionSet()
|
curieInstructionSet = newCurieInstructionSet()
|
||||||
darwinInstructionSet = newDarwinInstructionSet()
|
darwinInstructionSet = newDarwinInstructionSet()
|
||||||
|
euclidV2InstructionSet = newEuclidV2InstructionSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
type JumpTable [256]*operation
|
type JumpTable [256]*operation
|
||||||
|
|
||||||
|
// newEuclidV2InstructionSet returns the frontier, homestead, byzantium,
|
||||||
|
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin and euclidV2 instructions.
|
||||||
|
func newEuclidV2InstructionSet() JumpTable {
|
||||||
|
instructionSet := newDarwinInstructionSet()
|
||||||
|
enable7702(&instructionSet) // EIP-7702 Setcode transaction type
|
||||||
|
return instructionSet
|
||||||
|
}
|
||||||
|
|
||||||
// newDarwinInstructionSet returns the frontier, homestead, byzantium,
|
// newDarwinInstructionSet returns the frontier, homestead, byzantium,
|
||||||
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, and darwin instructions.
|
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, and darwin instructions.
|
||||||
func newDarwinInstructionSet() JumpTable {
|
func newDarwinInstructionSet() JumpTable {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ func (s *StructLog) ErrorString() string {
|
||||||
// Note that reference types are actual VM data structures; make copies
|
// Note that reference types are actual VM data structures; make copies
|
||||||
// if you need to retain them beyond the current call.
|
// if you need to retain them beyond the current call.
|
||||||
type EVMLogger interface {
|
type EVMLogger interface {
|
||||||
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
|
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult)
|
||||||
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
||||||
CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
||||||
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
||||||
|
|
@ -191,7 +191,7 @@ func (l *StructLogger) Reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, isCreate bool, input []byte, gas uint64, value *big.Int) {
|
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, isCreate bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
l.env = env
|
l.env = env
|
||||||
|
|
||||||
if isCreate {
|
if isCreate {
|
||||||
|
|
@ -204,6 +204,13 @@ func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Add
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
traceCodeWithAddress(l, to)
|
traceCodeWithAddress(l, to)
|
||||||
|
// only update the statesAffected when it's not a contract creation
|
||||||
|
// because contract creation is not allowed in set code transactions
|
||||||
|
for _, auth := range authorizationResults {
|
||||||
|
if auth.Success {
|
||||||
|
l.statesAffected[auth.Authority] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l.statesAffected[from] = struct{}{}
|
l.statesAffected[from] = struct{}{}
|
||||||
|
|
@ -430,7 +437,7 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
t.env = env
|
t.env = env
|
||||||
if !create {
|
if !create {
|
||||||
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
|
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/common/math"
|
"github.com/scroll-tech/go-ethereum/common/math"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONLogger struct {
|
type JSONLogger struct {
|
||||||
|
|
@ -42,7 +43,7 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
l.env = env
|
l.env = env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func TestStoreCapture(t *testing.T) {
|
||||||
scope.Stack.push(uint256.NewInt(1))
|
scope.Stack.push(uint256.NewInt(1))
|
||||||
scope.Stack.push(new(uint256.Int))
|
scope.Stack.push(new(uint256.Int))
|
||||||
var index common.Hash
|
var index common.Hash
|
||||||
logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil)
|
logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil, nil)
|
||||||
logger.CaptureState(0, SSTORE, 0, 0, scope, nil, 0, nil)
|
logger.CaptureState(0, SSTORE, 0, 0, scope, nil, 0, nil)
|
||||||
if len(logger.storage[contract.Address()]) == 0 {
|
if len(logger.storage[contract.Address()]) == 0 {
|
||||||
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(),
|
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(),
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/common/math"
|
"github.com/scroll-tech/go-ethereum/common/math"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/params"
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -215,3 +216,70 @@ var (
|
||||||
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
|
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
|
||||||
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall)
|
||||||
|
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall)
|
||||||
|
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall)
|
||||||
|
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode)
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||||
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
var (
|
||||||
|
total uint64 // total dynamic gas used
|
||||||
|
addr = common.Address(stack.Back(1).Bytes20())
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check slot presence in the access list
|
||||||
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
|
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||||
|
// the cost to charge for cold access, if any, is Cold - Warm
|
||||||
|
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||||
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
|
// gas for call
|
||||||
|
if !contract.UseGas(coldCost) {
|
||||||
|
return 0, ErrOutOfGas
|
||||||
|
}
|
||||||
|
total += coldCost
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if code is a delegation and if so, charge for resolution.
|
||||||
|
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||||
|
var cost uint64
|
||||||
|
if evm.StateDB.AddressInAccessList(target) {
|
||||||
|
cost = params.WarmStorageReadCostEIP2929
|
||||||
|
} else {
|
||||||
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
|
cost = params.ColdAccountAccessCostEIP2929
|
||||||
|
}
|
||||||
|
if !contract.UseGas(cost) {
|
||||||
|
return 0, ErrOutOfGas
|
||||||
|
}
|
||||||
|
total += cost
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now call the old calculator, which takes into account
|
||||||
|
// - create new account
|
||||||
|
// - transfer value
|
||||||
|
// - memory expansion
|
||||||
|
// - 63/64ths rule
|
||||||
|
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return old, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily add the gas charge back to the contract and return value. By
|
||||||
|
// adding it to the return, it will be charged outside of this function, as
|
||||||
|
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||||
|
// tracers.
|
||||||
|
contract.Gas += total
|
||||||
|
|
||||||
|
var overflow bool
|
||||||
|
if total, overflow = math.SafeAdd(old, total); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||||
input,
|
input,
|
||||||
cfg.GasLimit,
|
cfg.GasLimit,
|
||||||
cfg.Value,
|
cfg.Value,
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
return ret, cfg.State, err
|
return ret, cfg.State, err
|
||||||
}
|
}
|
||||||
|
|
@ -200,6 +201,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
||||||
input,
|
input,
|
||||||
cfg.GasLimit,
|
cfg.GasLimit,
|
||||||
cfg.Value,
|
cfg.Value,
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
return ret, leftOverGas, err
|
return ret, leftOverGas, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/agiledragon/gomonkey/v2"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus"
|
"github.com/scroll-tech/go-ethereum/consensus"
|
||||||
|
|
@ -354,7 +357,7 @@ type stepCounter struct {
|
||||||
steps int
|
steps int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepCounter) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (s *stepCounter) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepCounter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
func (s *stepCounter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||||
|
|
@ -409,12 +412,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
||||||
//cfg.State.CreateAccount(cfg.Origin)
|
//cfg.State.CreateAccount(cfg.Origin)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
cfg.State.SetCode(destination, code)
|
cfg.State.SetCode(destination, code)
|
||||||
vmenv.Call(sender, destination, nil, gas, cfg.Value)
|
vmenv.Call(sender, destination, nil, gas, cfg.Value, nil)
|
||||||
|
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
vmenv.Call(sender, destination, nil, gas, cfg.Value)
|
vmenv.Call(sender, destination, nil, gas, cfg.Value, nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -964,3 +967,87 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) {
|
||||||
benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b)
|
benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b)
|
||||||
benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b)
|
benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDelegatedAccountAccessCost tests that calling an account with an EIP-7702
|
||||||
|
// delegation designator incurs the correct amount of gas based on the tracer.
|
||||||
|
func TestDelegatedAccountAccessCost(t *testing.T) {
|
||||||
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
|
statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")))
|
||||||
|
statedb.SetCode(common.HexToAddress("0xaa"), []byte{
|
||||||
|
byte(vm.PUSH1), 0x00,
|
||||||
|
byte(vm.PUSH1), 0x00,
|
||||||
|
byte(vm.RETURN),
|
||||||
|
})
|
||||||
|
|
||||||
|
patches := gomonkey.ApplyMethod(reflect.TypeOf(statedb), "GetPoseidonCodeHash", func(_ *state.StateDB, addr common.Address) common.Hash {
|
||||||
|
return common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
|
||||||
|
})
|
||||||
|
defer patches.Reset()
|
||||||
|
|
||||||
|
for i, tc := range []struct {
|
||||||
|
code []byte
|
||||||
|
step int
|
||||||
|
want uint64
|
||||||
|
}{
|
||||||
|
{ // CALL(0xff)
|
||||||
|
code: []byte{
|
||||||
|
byte(vm.PUSH1), 0x0,
|
||||||
|
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP),
|
||||||
|
},
|
||||||
|
step: 7,
|
||||||
|
want: 5455,
|
||||||
|
},
|
||||||
|
{ // CALLCODE(0xff)
|
||||||
|
code: []byte{
|
||||||
|
byte(vm.PUSH1), 0x0,
|
||||||
|
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP),
|
||||||
|
},
|
||||||
|
step: 7,
|
||||||
|
want: 5455,
|
||||||
|
},
|
||||||
|
{ // DELEGATECALL(0xff)
|
||||||
|
code: []byte{
|
||||||
|
byte(vm.PUSH1), 0x0,
|
||||||
|
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP),
|
||||||
|
},
|
||||||
|
step: 6,
|
||||||
|
want: 5455,
|
||||||
|
},
|
||||||
|
{ // STATICCALL(0xff)
|
||||||
|
code: []byte{
|
||||||
|
byte(vm.PUSH1), 0x0,
|
||||||
|
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP),
|
||||||
|
},
|
||||||
|
step: 6,
|
||||||
|
want: 5455,
|
||||||
|
},
|
||||||
|
{ // SELFDESTRUCT(0xff): should not be affected by resolution
|
||||||
|
code: []byte{
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT),
|
||||||
|
},
|
||||||
|
step: 1,
|
||||||
|
want: 3,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
tracer := vm.NewStructLogger(nil)
|
||||||
|
Execute(tc.code, nil, &Config{
|
||||||
|
ChainConfig: params.TestChainConfig,
|
||||||
|
State: statedb,
|
||||||
|
EVMConfig: vm.Config{
|
||||||
|
Debug: true,
|
||||||
|
Tracer: tracer,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
have := tracer.StructLogs()[tc.step].GasCost
|
||||||
|
if want := tc.want; have != want {
|
||||||
|
for ii, op := range tracer.StructLogs() {
|
||||||
|
t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost)
|
||||||
|
}
|
||||||
|
t.Fatalf("tescase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -920,7 +920,7 @@ func (api *ScrollAPI) GetSkippedTransaction(ctx context.Context, hash common.Has
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var rpcTx RPCTransaction
|
var rpcTx RPCTransaction
|
||||||
rpcTx.RPCTransaction = *ethapi.NewRPCTransaction(stx.Tx, common.Hash{}, 0, 0, nil, api.eth.blockchain.Config())
|
rpcTx.RPCTransaction = *ethapi.NewRPCTransaction(stx.Tx, common.Hash{}, 0, 0, 0, nil, api.eth.blockchain.Config())
|
||||||
rpcTx.SkipReason = stx.Reason
|
rpcTx.SkipReason = stx.Reason
|
||||||
rpcTx.SkipBlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(stx.BlockNumber))
|
rpcTx.SkipBlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(stx.BlockNumber))
|
||||||
rpcTx.SkipBlockHash = stx.BlockHash
|
rpcTx.SkipBlockHash = stx.BlockHash
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(bc.Config(), header.Number)
|
signer = types.MakeSigner(bc.Config(), header.Number, header.Time)
|
||||||
txHeap = types.NewTransactionsByPriceAndNonce(signer, pending, nil)
|
txHeap = types.NewTransactionsByPriceAndNonce(signer, pending, nil)
|
||||||
transactions []*types.Transaction
|
transactions []*types.Transaction
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Bloc
|
||||||
block.SetCoinbase(common.Address{seed})
|
block.SetCoinbase(common.Address{seed})
|
||||||
// Add one tx to every secondblock
|
// Add one tx to every secondblock
|
||||||
if !empty && i%2 == 0 {
|
if !empty && i%2 == 0 {
|
||||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool)
|
||||||
}
|
}
|
||||||
// Include transactions to the miner to make blocks more interesting.
|
// Include transactions to the miner to make blocks more interesting.
|
||||||
if parent == tc.genesis && i%22 == 0 {
|
if parent == tc.genesis && i%22 == 0 {
|
||||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common
|
||||||
|
|
||||||
// If the block number is multiple of 3, send a bonus transaction to the miner
|
// If the block number is multiple of 3, send a bonus transaction to the miner
|
||||||
if parent == genesis && i%3 == 0 {
|
if parent == genesis && i%3 == 0 {
|
||||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
results []*big.Int
|
results []*big.Int
|
||||||
)
|
)
|
||||||
for sent < oracle.checkBlocks && number > 0 {
|
for sent < oracle.checkBlocks && number > 0 {
|
||||||
go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
|
go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit)
|
||||||
sent++
|
sent++
|
||||||
exp++
|
exp++
|
||||||
number--
|
number--
|
||||||
|
|
@ -255,7 +255,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
// meaningful returned, try to query more blocks. But the maximum
|
// meaningful returned, try to query more blocks. But the maximum
|
||||||
// is 2*checkBlocks.
|
// is 2*checkBlocks.
|
||||||
if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 {
|
if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 {
|
||||||
go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
|
go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit)
|
||||||
sent++
|
sent++
|
||||||
exp++
|
exp++
|
||||||
number--
|
number--
|
||||||
|
|
@ -311,7 +311,7 @@ func (s *txSorter) Less(i, j int) bool {
|
||||||
// and sends it to the result channel. If the block is empty or all transactions
|
// and sends it to the result channel. If the block is empty or all transactions
|
||||||
// are sent by the miner itself(it doesn't make any sense to include this kind of
|
// are sent by the miner itself(it doesn't make any sense to include this kind of
|
||||||
// transaction prices for sampling), nil gasprice is returned.
|
// transaction prices for sampling), nil gasprice is returned.
|
||||||
func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {
|
func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {
|
||||||
block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
|
block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
|
||||||
if block == nil {
|
if block == nil {
|
||||||
select {
|
select {
|
||||||
|
|
@ -320,6 +320,8 @@ func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, b
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
signer := types.MakeSigner(oracle.backend.ChainConfig(), block.Number(), block.Time())
|
||||||
|
|
||||||
// Sort the transaction by effective tip in ascending sort.
|
// Sort the transaction by effective tip in ascending sort.
|
||||||
txs := make([]*types.Transaction, len(block.Transactions()))
|
txs := make([]*types.Transaction, len(block.Transactions()))
|
||||||
copy(txs, block.Transactions())
|
copy(txs, block.Transactions())
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
|
||||||
return nil, vm.BlockContext{}, statedb, nil
|
return nil, vm.BlockContext{}, statedb, nil
|
||||||
}
|
}
|
||||||
// Recompute transactions up to the target index.
|
// Recompute transactions up to the target index.
|
||||||
signer := types.MakeSigner(eth.blockchain.Config(), block.Number())
|
signer := types.MakeSigner(eth.blockchain.Config(), block.Number(), block.Time())
|
||||||
for idx, tx := range block.Transactions() {
|
for idx, tx := range block.Transactions() {
|
||||||
// Assemble the transaction call message and return if the requested offset
|
// Assemble the transaction call message and return if the requested offset
|
||||||
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
|
||||||
|
|
||||||
// Fetch and execute the next block trace tasks
|
// Fetch and execute the next block trace tasks
|
||||||
for task := range tasks {
|
for task := range tasks {
|
||||||
signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
|
signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), task.block.Time())
|
||||||
blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), api.backend.ChainConfig(), nil)
|
blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), api.backend.ChainConfig(), nil)
|
||||||
// Trace all the transactions contained within
|
// Trace all the transactions contained within
|
||||||
for i, tx := range task.block.Transactions() {
|
for i, tx := range task.block.Transactions() {
|
||||||
|
|
@ -534,7 +534,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
roots []common.Hash
|
roots []common.Hash
|
||||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
||||||
chainConfig = api.backend.ChainConfig()
|
chainConfig = api.backend.ChainConfig()
|
||||||
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), api.backend.ChainConfig(), nil)
|
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), api.backend.ChainConfig(), nil)
|
||||||
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
|
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
|
||||||
|
|
@ -602,7 +602,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
}
|
}
|
||||||
// Execute all the transaction contained within the block concurrently
|
// Execute all the transaction contained within the block concurrently
|
||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
||||||
txs = block.Transactions()
|
txs = block.Transactions()
|
||||||
results = make([]*txTraceResult, len(txs))
|
results = make([]*txTraceResult, len(txs))
|
||||||
|
|
||||||
|
|
@ -726,7 +726,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
// Execute transaction, either tracing all or just the requested one
|
// Execute transaction, either tracing all or just the requested one
|
||||||
var (
|
var (
|
||||||
dumps []string
|
dumps []string
|
||||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
||||||
chainConfig = api.backend.ChainConfig()
|
chainConfig = api.backend.ChainConfig()
|
||||||
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), api.backend.ChainConfig(), nil)
|
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), api.backend.ChainConfig(), nil)
|
||||||
canon = true
|
canon = true
|
||||||
|
|
@ -906,7 +906,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer := types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
||||||
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig(), signer, statedb, block.Number())
|
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig(), signer, statedb, block.Number())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
|
||||||
return nil, vm.BlockContext{}, statedb, nil
|
return nil, vm.BlockContext{}, statedb, nil
|
||||||
}
|
}
|
||||||
// Recompute transactions up to the target index.
|
// Recompute transactions up to the target index.
|
||||||
signer := types.MakeSigner(b.chainConfig, block.Number())
|
signer := types.MakeSigner(b.chainConfig, block.Number(), block.Time())
|
||||||
for idx, tx := range block.Transactions() {
|
for idx, tx := range block.Transactions() {
|
||||||
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
}
|
}
|
||||||
// Configure a blockchain with the given prestate
|
// Configure a blockchain with the given prestate
|
||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
|
signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
|
||||||
origin, _ = signer.Sender(tx)
|
origin, _ = signer.Sender(tx)
|
||||||
txContext = vm.TxContext{
|
txContext = vm.TxContext{
|
||||||
Origin: origin,
|
Origin: origin,
|
||||||
|
|
@ -278,7 +278,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
|
if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
|
||||||
b.Fatalf("failed to parse testcase input: %v", err)
|
b.Fatalf("failed to parse testcase input: %v", err)
|
||||||
}
|
}
|
||||||
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
|
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
|
||||||
msg, err := tx.AsMessage(signer, nil)
|
msg, err := tx.AsMessage(signer, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||||
"github.com/scroll-tech/go-ethereum/core"
|
"github.com/scroll-tech/go-ethereum/core"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
"github.com/scroll-tech/go-ethereum/crypto"
|
"github.com/scroll-tech/go-ethereum/crypto"
|
||||||
tracers2 "github.com/scroll-tech/go-ethereum/eth/tracers"
|
tracers2 "github.com/scroll-tech/go-ethereum/eth/tracers"
|
||||||
|
|
@ -681,7 +682,7 @@ func wrapError(context string, err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||||
func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
jst.env = env
|
jst.env = env
|
||||||
jst.ctx["type"] = "CALL"
|
jst.ctx["type"] = "CALL"
|
||||||
if create {
|
if create {
|
||||||
|
|
@ -705,7 +706,7 @@ func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
|
||||||
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
|
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
|
||||||
isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
|
isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
|
||||||
isShanghai := env.ChainConfig().IsShanghai(env.Context.BlockNumber)
|
isShanghai := env.ChainConfig().IsShanghai(env.Context.BlockNumber)
|
||||||
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul, isShanghai)
|
intrinsicGas, err := core.IntrinsicGas(input, nil, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul, isShanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
|
||||||
)
|
)
|
||||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||||
|
|
||||||
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
|
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value, nil)
|
||||||
ret, err := env.Interpreter().Run(contract, []byte{}, false)
|
ret, err := env.Interpreter().Run(contract, []byte{}, false)
|
||||||
tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err)
|
tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -152,7 +152,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||||
scope := &vm.ScopeContext{
|
scope := &vm.ScopeContext{
|
||||||
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
||||||
}
|
}
|
||||||
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0))
|
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0), nil)
|
||||||
tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
|
tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
|
||||||
timeout := errors.New("stahp")
|
timeout := errors.New("stahp")
|
||||||
tracer.Stop(timeout)
|
tracer.Stop(timeout)
|
||||||
|
|
@ -173,7 +173,7 @@ func TestNoStepExec(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||||
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
|
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0), nil)
|
||||||
tracer.CaptureEnd(nil, 0, 1, nil)
|
tracer.CaptureEnd(nil, 0, 1, nil)
|
||||||
ret, err := tracer.GetResult()
|
ret, err := tracer.GetResult()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
@ -79,7 +80,7 @@ func (t *fourByteTracer) store(id []byte, size int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
t.env = env
|
t.env = env
|
||||||
|
|
||||||
// Update list of precompiles based on current block
|
// Update list of precompiles based on current block
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
@ -64,7 +65,7 @@ func newCallTracer(ctx *tracers.Context) tracers.Tracer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
t.env = env
|
t.env = env
|
||||||
t.callstack[0] = callFrame{
|
t.callstack[0] = callFrame{
|
||||||
Type: "CALL",
|
Type: "CALL",
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
@ -40,7 +41,7 @@ func newNoopTracer(ctx *tracers.Context) tracers.Tracer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
"github.com/scroll-tech/go-ethereum/crypto"
|
"github.com/scroll-tech/go-ethereum/crypto"
|
||||||
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
||||||
|
|
@ -71,7 +72,7 @@ func newPrestateTracer(ctx *tracers.Context) tracers.Tracer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
t.env = env
|
t.env = env
|
||||||
t.create = create
|
t.create = create
|
||||||
t.to = to
|
t.to = to
|
||||||
|
|
@ -80,6 +81,10 @@ func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
|
||||||
t.lookupAccount(to)
|
t.lookupAccount(to)
|
||||||
t.lookupAccount(env.Context.Coinbase)
|
t.lookupAccount(env.Context.Coinbase)
|
||||||
|
|
||||||
|
for _, auth := range authorizationResults {
|
||||||
|
t.lookupAccount(auth.Authority)
|
||||||
|
}
|
||||||
|
|
||||||
// The recipient balance includes the value transferred.
|
// The recipient balance includes the value transferred.
|
||||||
toBal := new(big.Int).Sub(t.pre[to].Balance, value)
|
toBal := new(big.Int).Sub(t.pre[to].Balance, value)
|
||||||
t.pre[to].Balance = toBal
|
t.pre[to].Balance = toBal
|
||||||
|
|
@ -92,6 +97,14 @@ func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
|
||||||
fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
|
fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
|
||||||
t.pre[from].Balance = fromBal
|
t.pre[from].Balance = fromBal
|
||||||
t.pre[from].Nonce--
|
t.pre[from].Nonce--
|
||||||
|
|
||||||
|
for i := len(authorizationResults) - 1; i >= 0; i-- {
|
||||||
|
auth := authorizationResults[i]
|
||||||
|
if auth.Success {
|
||||||
|
t.pre[auth.Authority].Nonce--
|
||||||
|
t.pre[auth.Authority].Code = auth.PreCode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||||
|
|
|
||||||
|
|
@ -751,5 +751,8 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||||
if msg.BlobHashes != nil {
|
if msg.BlobHashes != nil {
|
||||||
arg["blobVersionedHashes"] = msg.BlobHashes
|
arg["blobVersionedHashes"] = msg.BlobHashes
|
||||||
}
|
}
|
||||||
|
if msg.AuthList != nil {
|
||||||
|
arg["authorizationList"] = msg.AuthList
|
||||||
|
}
|
||||||
return arg
|
return arg
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,9 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||||
if msg.BlobHashes != nil {
|
if msg.BlobHashes != nil {
|
||||||
arg["blobVersionedHashes"] = msg.BlobHashes
|
arg["blobVersionedHashes"] = msg.BlobHashes
|
||||||
}
|
}
|
||||||
|
if msg.AuthList != nil {
|
||||||
|
arg["authorizationList"] = msg.AuthList
|
||||||
|
}
|
||||||
return arg
|
return arg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ func (t *Transaction) GasPrice(ctx context.Context) (hexutil.Big, error) {
|
||||||
switch tx.Type() {
|
switch tx.Type() {
|
||||||
case types.AccessListTxType:
|
case types.AccessListTxType:
|
||||||
return hexutil.Big(*tx.GasPrice()), nil
|
return hexutil.Big(*tx.GasPrice()), nil
|
||||||
case types.DynamicFeeTxType:
|
case types.DynamicFeeTxType, types.SetCodeTxType:
|
||||||
if t.block != nil {
|
if t.block != nil {
|
||||||
if baseFee, _ := t.block.BaseFeePerGas(ctx); baseFee != nil {
|
if baseFee, _ := t.block.BaseFeePerGas(ctx); baseFee != nil {
|
||||||
// price = min(tip, gasFeeCap - baseFee) + baseFee
|
// price = min(tip, gasFeeCap - baseFee) + baseFee
|
||||||
|
|
@ -263,7 +263,7 @@ func (t *Transaction) MaxFeePerGas(ctx context.Context) (*hexutil.Big, error) {
|
||||||
switch tx.Type() {
|
switch tx.Type() {
|
||||||
case types.AccessListTxType:
|
case types.AccessListTxType:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case types.DynamicFeeTxType:
|
case types.DynamicFeeTxType, types.SetCodeTxType:
|
||||||
return (*hexutil.Big)(tx.GasFeeCap()), nil
|
return (*hexutil.Big)(tx.GasFeeCap()), nil
|
||||||
default:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -278,7 +278,7 @@ func (t *Transaction) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, e
|
||||||
switch tx.Type() {
|
switch tx.Type() {
|
||||||
case types.AccessListTxType:
|
case types.AccessListTxType:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case types.DynamicFeeTxType:
|
case types.DynamicFeeTxType, types.SetCodeTxType:
|
||||||
return (*hexutil.Big)(tx.GasTipCap()), nil
|
return (*hexutil.Big)(tx.GasTipCap()), nil
|
||||||
default:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,9 @@ type CallMsg struct {
|
||||||
// For BlobTxType
|
// For BlobTxType
|
||||||
BlobGasFeeCap *big.Int
|
BlobGasFeeCap *big.Int
|
||||||
BlobHashes []common.Hash
|
BlobHashes []common.Hash
|
||||||
|
|
||||||
|
// For SetCodeTxType
|
||||||
|
AuthList []types.SetCodeAuthorization
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ContractCaller provides contract calls, essentially transactions that are executed by
|
// A ContractCaller provides contract calls, essentially transactions that are executed by
|
||||||
|
|
|
||||||
|
|
@ -888,7 +888,7 @@ func (s *PublicBlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHas
|
||||||
for i, receipt := range receipts {
|
for i, receipt := range receipts {
|
||||||
blockNumber := block.NumberU64()
|
blockNumber := block.NumberU64()
|
||||||
bigblock := new(big.Int).SetUint64(blockNumber)
|
bigblock := new(big.Int).SetUint64(blockNumber)
|
||||||
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
|
signer := types.MakeSigner(s.b.ChainConfig(), bigblock, block.Time())
|
||||||
res, err := marshalReceipt(ctx, s.b, receipt, bigblock, block.Hash(), blockNumber, signer, txs[i], i)
|
res, err := marshalReceipt(ctx, s.b, receipt, bigblock, block.Hash(), blockNumber, signer, txs[i], i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to marshal receipt %d: %w", i, err)
|
return nil, fmt.Errorf("failed to marshal receipt %d: %w", i, err)
|
||||||
|
|
@ -991,7 +991,7 @@ func EstimateL1MsgFee(ctx context.Context, b Backend, args TransactionArgs, bloc
|
||||||
evm.Cancel()
|
evm.Cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
signer := types.MakeSigner(config, header.Number)
|
signer := types.MakeSigner(config, header.Number, header.Time)
|
||||||
return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, evm.StateDB, header.Number)
|
return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, evm.StateDB, header.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1350,9 +1350,11 @@ type RPCTransaction struct {
|
||||||
Type hexutil.Uint64 `json:"type"`
|
Type hexutil.Uint64 `json:"type"`
|
||||||
Accesses *types.AccessList `json:"accessList,omitempty"`
|
Accesses *types.AccessList `json:"accessList,omitempty"`
|
||||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||||
|
AuthorizationList []types.SetCodeAuthorization `json:"authorizationList,omitempty"`
|
||||||
V *hexutil.Big `json:"v"`
|
V *hexutil.Big `json:"v"`
|
||||||
R *hexutil.Big `json:"r"`
|
R *hexutil.Big `json:"r"`
|
||||||
S *hexutil.Big `json:"s"`
|
S *hexutil.Big `json:"s"`
|
||||||
|
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
|
||||||
|
|
||||||
// L1 message transaction fields:
|
// L1 message transaction fields:
|
||||||
Sender *common.Address `json:"sender,omitempty"`
|
Sender *common.Address `json:"sender,omitempty"`
|
||||||
|
|
@ -1361,8 +1363,8 @@ type RPCTransaction struct {
|
||||||
|
|
||||||
// NewRPCTransaction returns a transaction that will serialize to the RPC
|
// NewRPCTransaction returns a transaction that will serialize to the RPC
|
||||||
// representation, with the given location metadata set (if available).
|
// representation, with the given location metadata set (if available).
|
||||||
func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int, config *params.ChainConfig) *RPCTransaction {
|
func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber, blockTime uint64, index uint64, baseFee *big.Int, config *params.ChainConfig) *RPCTransaction {
|
||||||
signer := types.MakeSigner(config, big.NewInt(0).SetUint64(blockNumber))
|
signer := types.MakeSigner(config, big.NewInt(0).SetUint64(blockNumber), blockTime)
|
||||||
from, _ := types.Sender(signer, tx)
|
from, _ := types.Sender(signer, tx)
|
||||||
v, r, s := tx.RawSignatureValues()
|
v, r, s := tx.RawSignatureValues()
|
||||||
result := &RPCTransaction{
|
result := &RPCTransaction{
|
||||||
|
|
@ -1387,12 +1389,16 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
||||||
switch tx.Type() {
|
switch tx.Type() {
|
||||||
case types.AccessListTxType:
|
case types.AccessListTxType:
|
||||||
al := tx.AccessList()
|
al := tx.AccessList()
|
||||||
|
yparity := hexutil.Uint64(v.Sign())
|
||||||
result.Accesses = &al
|
result.Accesses = &al
|
||||||
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||||
|
result.YParity = &yparity
|
||||||
case types.DynamicFeeTxType:
|
case types.DynamicFeeTxType:
|
||||||
al := tx.AccessList()
|
al := tx.AccessList()
|
||||||
|
yparity := hexutil.Uint64(v.Sign())
|
||||||
result.Accesses = &al
|
result.Accesses = &al
|
||||||
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||||
|
result.YParity = &yparity
|
||||||
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
|
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
|
||||||
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
|
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
|
||||||
// if the transaction has been mined, compute the effective gas price
|
// if the transaction has been mined, compute the effective gas price
|
||||||
|
|
@ -1403,6 +1409,23 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
||||||
} else {
|
} else {
|
||||||
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
|
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
|
||||||
}
|
}
|
||||||
|
case types.SetCodeTxType:
|
||||||
|
al := tx.AccessList()
|
||||||
|
yparity := hexutil.Uint64(v.Sign())
|
||||||
|
result.Accesses = &al
|
||||||
|
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||||
|
result.YParity = &yparity
|
||||||
|
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
|
||||||
|
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
|
||||||
|
// if the transaction has been mined, compute the effective gas price
|
||||||
|
if baseFee != nil && blockHash != (common.Hash{}) {
|
||||||
|
// price = min(tip, gasFeeCap - baseFee) + baseFee
|
||||||
|
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
|
||||||
|
result.GasPrice = (*hexutil.Big)(price)
|
||||||
|
} else {
|
||||||
|
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
|
||||||
|
}
|
||||||
|
result.AuthorizationList = tx.SetCodeAuthorizations()
|
||||||
case types.L1MessageTxType:
|
case types.L1MessageTxType:
|
||||||
msg := tx.AsL1MessageTx()
|
msg := tx.AsL1MessageTx()
|
||||||
result.Sender = &msg.Sender
|
result.Sender = &msg.Sender
|
||||||
|
|
@ -1415,11 +1438,13 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
||||||
func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig, l1BaseFee *big.Int) *RPCTransaction {
|
func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig, l1BaseFee *big.Int) *RPCTransaction {
|
||||||
var baseFee *big.Int
|
var baseFee *big.Int
|
||||||
blockNumber := uint64(0)
|
blockNumber := uint64(0)
|
||||||
|
blockTime := uint64(0)
|
||||||
if current != nil {
|
if current != nil {
|
||||||
baseFee = misc.CalcBaseFee(config, current, l1BaseFee)
|
baseFee = misc.CalcBaseFee(config, current, l1BaseFee)
|
||||||
blockNumber = current.Number.Uint64()
|
blockNumber = current.Number.Uint64()
|
||||||
|
blockTime = current.Time
|
||||||
}
|
}
|
||||||
return NewRPCTransaction(tx, common.Hash{}, blockNumber, 0, baseFee, config)
|
return NewRPCTransaction(tx, common.Hash{}, blockNumber, blockTime, 0, baseFee, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
|
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
|
||||||
|
|
@ -1428,7 +1453,7 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *param
|
||||||
if index >= uint64(len(txs)) {
|
if index >= uint64(len(txs)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return NewRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee(), config)
|
return NewRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time(), index, b.BaseFee(), config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
|
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
|
||||||
|
|
@ -1539,7 +1564,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, err
|
return nil, 0, nil, err
|
||||||
}
|
}
|
||||||
signer := types.MakeSigner(b.ChainConfig(), header.Number)
|
signer := types.MakeSigner(b.ChainConfig(), header.Number, header.Time)
|
||||||
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig(), signer, statedb, header.Number)
|
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig(), signer, statedb, header.Number)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
||||||
|
|
@ -1651,7 +1676,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, has
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee, s.b.ChainConfig()), nil
|
return NewRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil
|
||||||
}
|
}
|
||||||
// No finalized transaction, try to retrieve it from the pool
|
// No finalized transaction, try to retrieve it from the pool
|
||||||
if tx := s.b.GetPoolTransaction(hash); tx != nil {
|
if tx := s.b.GetPoolTransaction(hash); tx != nil {
|
||||||
|
|
@ -1695,6 +1720,10 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
header, err := s.b.HeaderByHash(ctx, blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
receipts, err := s.b.GetReceipts(ctx, blockHash)
|
receipts, err := s.b.GetReceipts(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1705,9 +1734,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
||||||
receipt := receipts[index]
|
receipt := receipts[index]
|
||||||
|
|
||||||
// Derive the sender.
|
// Derive the sender.
|
||||||
bigblock := new(big.Int).SetUint64(blockNumber)
|
signer := types.MakeSigner(s.b.ChainConfig(), header.Number, header.Time)
|
||||||
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
|
return marshalReceipt(ctx, s.b, receipt, header.Number, blockHash, blockNumber, signer, tx, int(index))
|
||||||
return marshalReceipt(ctx, s.b, receipt, bigblock, blockHash, blockNumber, signer, tx, int(index))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshalReceipt marshals a transaction receipt into a JSON object.
|
// marshalReceipt marshals a transaction receipt into a JSON object.
|
||||||
|
|
@ -1790,7 +1818,7 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
// Print a log with full tx details for manual investigations and interventions
|
// Print a log with full tx details for manual investigations and interventions
|
||||||
signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
|
signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number(), b.CurrentBlock().Time())
|
||||||
from, err := types.Sender(signer, tx)
|
from, err := types.Sender(signer, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
|
|
|
||||||
415
internal/ethapi/api_test.go
Normal file
415
internal/ethapi/api_test.go
Normal file
|
|
@ -0,0 +1,415 @@
|
||||||
|
// Copyright 2023 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 ethapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum"
|
||||||
|
"github.com/scroll-tech/go-ethereum/accounts"
|
||||||
|
"github.com/scroll-tech/go-ethereum/accounts/keystore"
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||||
|
"github.com/scroll-tech/go-ethereum/consensus"
|
||||||
|
"github.com/scroll-tech/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/bloombits"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/state"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
|
"github.com/scroll-tech/go-ethereum/crypto"
|
||||||
|
"github.com/scroll-tech/go-ethereum/ethdb"
|
||||||
|
"github.com/scroll-tech/go-ethereum/event"
|
||||||
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
|
"github.com/scroll-tech/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestAccountManager(t *testing.T) (*accounts.Manager, accounts.Account) {
|
||||||
|
var (
|
||||||
|
dir = t.TempDir()
|
||||||
|
am = accounts.NewManager(nil)
|
||||||
|
b = keystore.NewKeyStore(dir, 2, 1)
|
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
)
|
||||||
|
acc, err := b.ImportECDSA(testKey, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create test account: %v", err)
|
||||||
|
}
|
||||||
|
if err := b.Unlock(acc, ""); err != nil {
|
||||||
|
t.Fatalf("failed to unlock account: %v\n", err)
|
||||||
|
}
|
||||||
|
am.AddBackend(b)
|
||||||
|
return am, acc
|
||||||
|
}
|
||||||
|
|
||||||
|
type account struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
addr common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAccounts(n int) (accounts []account) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
accounts = append(accounts, account{key: key, addr: addr})
|
||||||
|
}
|
||||||
|
slices.SortFunc(accounts, func(a, b account) int { return bytes.Compare(a.addr[:], b.addr[:]) })
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
type testBackend struct {
|
||||||
|
db ethdb.Database
|
||||||
|
chain *core.BlockChain
|
||||||
|
pending *types.Block
|
||||||
|
accman *accounts.Manager
|
||||||
|
acc accounts.Account
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.Engine, generator func(i int, b *core.BlockGen)) *testBackend {
|
||||||
|
var (
|
||||||
|
cacheConfig = &core.CacheConfig{
|
||||||
|
TrieCleanLimit: 256,
|
||||||
|
TrieDirtyLimit: 256,
|
||||||
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
|
SnapshotLimit: 0,
|
||||||
|
TrieDirtyDisabled: true, // Archive mode
|
||||||
|
}
|
||||||
|
)
|
||||||
|
accman, acc := newTestAccountManager(t)
|
||||||
|
gspec.Alloc[acc.Address] = core.GenesisAccount{Balance: big.NewInt(params.Ether)}
|
||||||
|
// Generate blocks for testing
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
genesis := gspec.MustCommit(db)
|
||||||
|
blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, n, generator)
|
||||||
|
txlookupLimit := uint64(0)
|
||||||
|
chain, err := core.NewBlockChain(db, cacheConfig, gspec.Config, engine, vm.Config{}, nil, &txlookupLimit)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
backend := &testBackend{db: db, chain: chain, accman: accman, acc: acc}
|
||||||
|
return backend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) setPendingBlock(block *types.Block) {
|
||||||
|
b.pending = block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
|
||||||
|
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
|
return big.NewInt(0), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
|
||||||
|
return nil, nil, nil, nil, nil
|
||||||
|
}
|
||||||
|
func (b testBackend) BlobBaseFee(ctx context.Context) *big.Int { return new(big.Int) }
|
||||||
|
func (b testBackend) ChainDb() ethdb.Database { return b.db }
|
||||||
|
func (b testBackend) AccountManager() *accounts.Manager { return b.accman }
|
||||||
|
func (b testBackend) ExtRPCEnabled() bool { return false }
|
||||||
|
func (b testBackend) RPCGasCap() uint64 { return 10000000 }
|
||||||
|
func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second }
|
||||||
|
func (b testBackend) RPCTxFeeCap() float64 { return 0 }
|
||||||
|
func (b testBackend) UnprotectedAllowed() bool { return false }
|
||||||
|
func (b testBackend) SetHead(number uint64) {}
|
||||||
|
func (b testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
||||||
|
if number == rpc.LatestBlockNumber {
|
||||||
|
return b.chain.CurrentHeader(), nil
|
||||||
|
}
|
||||||
|
if number == rpc.PendingBlockNumber && b.pending != nil {
|
||||||
|
return b.pending.Header(), nil
|
||||||
|
}
|
||||||
|
return b.chain.GetHeaderByNumber(uint64(number)), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
|
return b.chain.GetHeaderByHash(hash), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.HeaderByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
if blockHash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
return b.HeaderByHash(ctx, blockHash)
|
||||||
|
}
|
||||||
|
panic("unknown type rpc.BlockNumberOrHash")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b testBackend) CurrentHeader() *types.Header { return b.chain.CurrentHeader() }
|
||||||
|
func (b testBackend) CurrentBlock() *types.Block { return b.chain.CurrentBlock() }
|
||||||
|
func (b testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
|
||||||
|
if number == rpc.LatestBlockNumber {
|
||||||
|
head := b.chain.CurrentBlock()
|
||||||
|
return b.chain.GetBlock(head.Hash(), head.Number().Uint64()), nil
|
||||||
|
}
|
||||||
|
if number == rpc.PendingBlockNumber {
|
||||||
|
return b.pending, nil
|
||||||
|
}
|
||||||
|
return b.chain.GetBlockByNumber(uint64(number)), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||||
|
return b.chain.GetBlockByHash(hash), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.BlockByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
if blockHash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
return b.BlockByHash(ctx, blockHash)
|
||||||
|
}
|
||||||
|
panic("unknown type rpc.BlockNumberOrHash")
|
||||||
|
}
|
||||||
|
func (b testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
|
||||||
|
return b.chain.GetBlock(hash, uint64(number.Int64())).Body(), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||||
|
if number == rpc.PendingBlockNumber {
|
||||||
|
panic("pending state not implemented")
|
||||||
|
}
|
||||||
|
header, err := b.HeaderByNumber(ctx, number)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if header == nil {
|
||||||
|
return nil, nil, errors.New("header not found")
|
||||||
|
}
|
||||||
|
stateDb, err := b.chain.StateAt(header.Root)
|
||||||
|
return stateDb, header, err
|
||||||
|
}
|
||||||
|
func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
panic("only implemented for number")
|
||||||
|
}
|
||||||
|
func (b testBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) { panic("implement me") }
|
||||||
|
func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
|
header, err := b.HeaderByHash(ctx, hash)
|
||||||
|
if header == nil || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), b.chain.Config())
|
||||||
|
return receipts, nil
|
||||||
|
}
|
||||||
|
func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
|
||||||
|
if b.pending != nil && hash == b.pending.Hash() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return big.NewInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b testBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
|
||||||
|
vmError := func() error { return nil }
|
||||||
|
if vmConfig == nil {
|
||||||
|
vmConfig = b.chain.GetVMConfig()
|
||||||
|
}
|
||||||
|
txContext := core.NewEVMTxContext(msg)
|
||||||
|
context := core.NewEVMBlockContext(header, b.chain, b.ChainConfig(), nil)
|
||||||
|
return vm.NewEVM(context, txContext, state, b.chain.Config(), *vmConfig), vmError, nil
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) RemoveTx(txHash common.Hash) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||||
|
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
|
||||||
|
return tx, blockHash, blockNumber, index, nil
|
||||||
|
}
|
||||||
|
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
|
||||||
|
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") }
|
||||||
|
func (b testBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
func (b testBackend) Stats() (pending int, queued int) { panic("implement me") }
|
||||||
|
func (b testBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeNewTxsEvent(events chan<- core.NewTxsEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() }
|
||||||
|
func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() }
|
||||||
|
func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) BloomStatus() (uint64, uint64) { panic("implement me") }
|
||||||
|
func (b testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEstimateGas(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// Initialize test accounts
|
||||||
|
var (
|
||||||
|
accounts = newAccounts(3)
|
||||||
|
genesis = &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: core.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether), Code: append(types.DelegationPrefix, accounts[2].addr.Bytes()...)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genBlocks = 10
|
||||||
|
signer = types.HomesteadSigner{}
|
||||||
|
)
|
||||||
|
|
||||||
|
api := NewPublicBlockChainAPI(newTestBackend(t, genBlocks, genesis, ethash.NewFaker(), func(i int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1]
|
||||||
|
// value: 1000 wei
|
||||||
|
// fee: 0 wei
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}))
|
||||||
|
|
||||||
|
setCodeAuthorization, _ := types.SignSetCode(accounts[0].key, types.SetCodeAuthorization{
|
||||||
|
Address: accounts[0].addr,
|
||||||
|
Nonce: uint64(genBlocks + 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
var testSuite = []struct {
|
||||||
|
blockNumber rpc.BlockNumber
|
||||||
|
call TransactionArgs
|
||||||
|
want uint64
|
||||||
|
expectErr error
|
||||||
|
}{
|
||||||
|
// Should be able to send to an EIP-7702 delegated account.
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1)),
|
||||||
|
},
|
||||||
|
want: 21000,
|
||||||
|
},
|
||||||
|
// Should be able to send as EIP-7702 delegated account.
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[1].addr,
|
||||||
|
To: &accounts[0].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1)),
|
||||||
|
},
|
||||||
|
want: 21000,
|
||||||
|
},
|
||||||
|
// Should be able to estimate SetCodeTx.
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(0)),
|
||||||
|
AuthorizationList: []types.SetCodeAuthorization{setCodeAuthorization},
|
||||||
|
},
|
||||||
|
want: 46000,
|
||||||
|
},
|
||||||
|
// Should retrieve the code of 0xef0001 || accounts[0].addr and return an invalid opcode error.
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[0].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(0)),
|
||||||
|
AuthorizationList: []types.SetCodeAuthorization{setCodeAuthorization},
|
||||||
|
},
|
||||||
|
expectErr: vm.NewErrInvalidOpCode(0xef),
|
||||||
|
},
|
||||||
|
// SetCodeTx with empty authorization list should fail.
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &common.Address{},
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(0)),
|
||||||
|
AuthorizationList: []types.SetCodeAuthorization{},
|
||||||
|
},
|
||||||
|
expectErr: core.ErrEmptyAuthList,
|
||||||
|
},
|
||||||
|
// SetCodeTx with nil `to` should fail.
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: nil,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(0)),
|
||||||
|
AuthorizationList: []types.SetCodeAuthorization{setCodeAuthorization},
|
||||||
|
},
|
||||||
|
expectErr: core.ErrSetCodeTxCreate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range testSuite {
|
||||||
|
result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber})
|
||||||
|
if tc.expectErr != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !errors.Is(err, tc.expectErr) {
|
||||||
|
if !reflect.DeepEqual(err, tc.expectErr) {
|
||||||
|
t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: want no error, have %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if float64(result) > float64(tc.want) {
|
||||||
|
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, uint64(result), tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||||
"github.com/scroll-tech/go-ethereum/common/math"
|
"github.com/scroll-tech/go-ethereum/common/math"
|
||||||
|
|
@ -52,6 +54,9 @@ type TransactionArgs struct {
|
||||||
// Introduced by AccessListTxType transaction.
|
// Introduced by AccessListTxType transaction.
|
||||||
AccessList *types.AccessList `json:"accessList,omitempty"`
|
AccessList *types.AccessList `json:"accessList,omitempty"`
|
||||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||||
|
|
||||||
|
// For SetCodeTxType
|
||||||
|
AuthorizationList []types.SetCodeAuthorization `json:"authorizationList"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// from retrieves the transaction sender address.
|
// from retrieves the transaction sender address.
|
||||||
|
|
@ -247,7 +252,7 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (t
|
||||||
if args.AccessList != nil {
|
if args.AccessList != nil {
|
||||||
accessList = *args.AccessList
|
accessList = *args.AccessList
|
||||||
}
|
}
|
||||||
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true)
|
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true, args.AuthorizationList)
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,6 +261,27 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (t
|
||||||
func (args *TransactionArgs) toTransaction() *types.Transaction {
|
func (args *TransactionArgs) toTransaction() *types.Transaction {
|
||||||
var data types.TxData
|
var data types.TxData
|
||||||
switch {
|
switch {
|
||||||
|
case args.AuthorizationList != nil:
|
||||||
|
al := types.AccessList{}
|
||||||
|
if args.AccessList != nil {
|
||||||
|
al = *args.AccessList
|
||||||
|
}
|
||||||
|
setCodeAuthorizations := []types.SetCodeAuthorization{}
|
||||||
|
if args.AuthorizationList != nil {
|
||||||
|
setCodeAuthorizations = args.AuthorizationList
|
||||||
|
}
|
||||||
|
data = &types.SetCodeTx{
|
||||||
|
To: *args.To,
|
||||||
|
ChainID: uint256.MustFromBig(args.ChainID.ToInt()),
|
||||||
|
Nonce: uint64(*args.Nonce),
|
||||||
|
Gas: uint64(*args.Gas),
|
||||||
|
GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)),
|
||||||
|
GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)),
|
||||||
|
Value: uint256.MustFromBig((*big.Int)(args.Value)),
|
||||||
|
Data: args.data(),
|
||||||
|
AccessList: al,
|
||||||
|
AuthList: setCodeAuthorizations,
|
||||||
|
}
|
||||||
case args.MaxFeePerGas != nil:
|
case args.MaxFeePerGas != nil:
|
||||||
al := types.AccessList{}
|
al := types.AccessList{}
|
||||||
if args.AccessList != nil {
|
if args.AccessList != nil {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Bloc
|
||||||
block.SetCoinbase(common.Address{seed})
|
block.SetCoinbase(common.Address{seed})
|
||||||
// Add one tx to every secondblock
|
// Add one tx to every secondblock
|
||||||
if !empty && i%2 == 0 {
|
if !empty && i%2 == 0 {
|
||||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool)
|
||||||
}
|
}
|
||||||
// Include transactions to the miner to make blocks more interesting.
|
// Include transactions to the miner to make blocks more interesting.
|
||||||
if parent == tc.genesis && i%22 == 0 {
|
if parent == tc.genesis && i%22 == 0 {
|
||||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common
|
||||||
|
|
||||||
// If the block number is multiple of 3, send a bonus transaction to the miner
|
// If the block number is multiple of 3, send a bonus transaction to the miner
|
||||||
if parent == genesis && i%3 == 0 {
|
if parent == genesis && i%3 == 0 {
|
||||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
from := statedb.GetOrNewStateObject(bankAddr)
|
from := statedb.GetOrNewStateObject(bankAddr)
|
||||||
from.SetBalance(math.MaxBig256)
|
from.SetBalance(math.MaxBig256)
|
||||||
|
|
||||||
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)}
|
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, nil)}
|
||||||
|
|
||||||
context := core.NewEVMBlockContext(header, bc, config, nil)
|
context := core.NewEVMBlockContext(header, bc, config, nil)
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
|
|
@ -144,7 +144,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
|
|
||||||
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
|
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
signer := types.MakeSigner(config, header.Number)
|
signer := types.MakeSigner(config, header.Number, header.Time)
|
||||||
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, statedb, header.Number)
|
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, statedb, header.Number)
|
||||||
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
|
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
|
||||||
res = append(res, result.Return()...)
|
res = append(res, result.Return()...)
|
||||||
|
|
@ -153,12 +153,12 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
header := lc.GetHeaderByHash(bhash)
|
header := lc.GetHeaderByHash(bhash)
|
||||||
state := light.NewState(ctx, header, lc.Odr())
|
state := light.NewState(ctx, header, lc.Odr())
|
||||||
state.SetBalance(bankAddr, math.MaxBig256)
|
state.SetBalance(bankAddr, math.MaxBig256)
|
||||||
msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)}
|
msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, nil)}
|
||||||
context := core.NewEVMBlockContext(header, lc, config, nil)
|
context := core.NewEVMBlockContext(header, lc, config, nil)
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true})
|
vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
signer := types.MakeSigner(config, header.Number)
|
signer := types.MakeSigner(config, header.Number, header.Time)
|
||||||
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, state, header.Number)
|
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, state, header.Number)
|
||||||
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
|
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
|
||||||
if state.Error() == nil {
|
if state.Error() == nil {
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
|
||||||
return nil, vm.BlockContext{}, statedb, nil
|
return nil, vm.BlockContext{}, statedb, nil
|
||||||
}
|
}
|
||||||
// Recompute transactions up to the target index.
|
// Recompute transactions up to the target index.
|
||||||
signer := types.MakeSigner(leth.blockchain.Config(), block.Number())
|
signer := types.MakeSigner(leth.blockchain.Config(), block.Number(), block.Time())
|
||||||
for idx, tx := range block.Transactions() {
|
for idx, tx := range block.Transactions() {
|
||||||
// Assemble the transaction call message and return if the requested offset
|
// Assemble the transaction call message and return if the requested offset
|
||||||
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
||||||
|
|
|
||||||
|
|
@ -195,12 +195,12 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
|
||||||
|
|
||||||
// Perform read-only call.
|
// Perform read-only call.
|
||||||
st.SetBalance(testBankAddress, math.MaxBig256)
|
st.SetBalance(testBankAddress, math.MaxBig256)
|
||||||
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)}
|
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, nil)}
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
context := core.NewEVMBlockContext(header, chain, config, nil)
|
context := core.NewEVMBlockContext(header, chain, config, nil)
|
||||||
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
|
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
signer := types.MakeSigner(config, header.Number)
|
signer := types.MakeSigner(config, header.Number, header.Time)
|
||||||
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, st, header.Number)
|
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, st, header.Number)
|
||||||
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
|
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
|
||||||
res = append(res, result.Return()...)
|
res = append(res, result.Return()...)
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
|
||||||
genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
|
genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
|
||||||
config := rawdb.ReadChainConfig(odr.Database(), genesis)
|
config := rawdb.ReadChainConfig(odr.Database(), genesis)
|
||||||
|
|
||||||
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Transactions()); err != nil {
|
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.Transactions()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rawdb.WriteReceipts(odr.Database(), hash, number, receipts)
|
rawdb.WriteReceipts(odr.Database(), hash, number, receipts)
|
||||||
|
|
|
||||||
|
|
@ -420,7 +420,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should supply enough intrinsic gas
|
// Should supply enough intrinsic gas
|
||||||
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -648,7 +648,7 @@ func (w *worker) processTxPool() (bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := types.MakeSigner(w.chainConfig, w.current.header.Number)
|
signer := types.MakeSigner(w.chainConfig, w.current.header.Number, w.current.header.Time)
|
||||||
if w.prioritizedTx != nil && w.current.header.Number.Uint64() > w.prioritizedTx.blockNumber {
|
if w.prioritizedTx != nil && w.current.header.Number.Uint64() > w.prioritizedTx.blockNumber {
|
||||||
w.prioritizedTx = nil
|
w.prioritizedTx = nil
|
||||||
}
|
}
|
||||||
|
|
@ -688,7 +688,7 @@ func (w *worker) processTxPool() (bool, error) {
|
||||||
// processTxnSlice
|
// processTxnSlice
|
||||||
func (w *worker) processTxnSlice(txns types.Transactions) (bool, error) {
|
func (w *worker) processTxnSlice(txns types.Transactions) (bool, error) {
|
||||||
txsMap := make(map[common.Address]types.Transactions)
|
txsMap := make(map[common.Address]types.Transactions)
|
||||||
signer := types.MakeSigner(w.chainConfig, w.current.header.Number)
|
signer := types.MakeSigner(w.chainConfig, w.current.header.Number, w.current.header.Time)
|
||||||
for _, tx := range txns {
|
for _, tx := range txns {
|
||||||
acc, _ := types.Sender(signer, tx)
|
acc, _ := types.Sender(signer, tx)
|
||||||
txsMap[acc] = append(txsMap[acc], tx)
|
txsMap[acc] = append(txsMap[acc], tx)
|
||||||
|
|
|
||||||
|
|
@ -499,6 +499,8 @@ var (
|
||||||
CurieBlock: big.NewInt(0),
|
CurieBlock: big.NewInt(0),
|
||||||
DarwinTime: new(uint64),
|
DarwinTime: new(uint64),
|
||||||
DarwinV2Time: new(uint64),
|
DarwinV2Time: new(uint64),
|
||||||
|
EuclidTime: new(uint64),
|
||||||
|
EuclidV2Time: new(uint64),
|
||||||
TerminalTotalDifficulty: nil,
|
TerminalTotalDifficulty: nil,
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
Clique: nil,
|
Clique: nil,
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ const (
|
||||||
TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul)
|
TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul)
|
||||||
TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list
|
TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list
|
||||||
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
|
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
|
||||||
|
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
|
||||||
|
|
||||||
// These have been changed during the course of the chain
|
// These have been changed during the course of the chain
|
||||||
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
|
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 8 // Minor version component of the current release
|
VersionMinor = 8 // Minor version component of the current release
|
||||||
VersionPatch = 9 // Patch version component of the current release
|
VersionPatch = 10 // Patch version component of the current release
|
||||||
VersionMeta = "mainnet" // Version metadata to append to the version string
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ func TestAsyncChecker(t *testing.T) {
|
||||||
// Create a database pre-initialize with a genesis block
|
// Create a database pre-initialize with a genesis block
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
chainConfig := params.TestChainConfig.Clone()
|
chainConfig := params.TestChainConfig.Clone()
|
||||||
|
chainConfig.EuclidTime = nil
|
||||||
|
chainConfig.EuclidV2Time = nil
|
||||||
chainConfig.Scroll.UseZktrie = true
|
chainConfig.Scroll.UseZktrie = true
|
||||||
(&core.Genesis{
|
(&core.Genesis{
|
||||||
Config: chainConfig,
|
Config: chainConfig,
|
||||||
|
|
@ -38,7 +40,7 @@ func TestAsyncChecker(t *testing.T) {
|
||||||
|
|
||||||
bs, _ := core.GenerateChain(chainConfig, chain.Genesis(), ethash.NewFaker(), db, 100, func(i int, block *core.BlockGen) {
|
bs, _ := core.GenerateChain(chainConfig, chain.Genesis(), ethash.NewFaker(), db, 100, func(i int, block *core.BlockGen) {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
signer := types.MakeSigner(chainConfig, block.Number())
|
signer := types.MakeSigner(chainConfig, block.Number(), block.Time())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), testAddr, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), testAddr, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ func (l *Logger) logSha256(inputLen uint64) {
|
||||||
l.sha256Usage += numBlocks * blockRows
|
l.sha256Usage += numBlocks * blockRows
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (l *Logger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
l.currentEnv = env
|
l.currentEnv = env
|
||||||
l.isCreate = create
|
l.isCreate = create
|
||||||
if !l.isCreate {
|
if !l.isCreate {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||||
_ "github.com/scroll-tech/go-ethereum/eth/tracers/native"
|
_ "github.com/scroll-tech/go-ethereum/eth/tracers/native"
|
||||||
)
|
)
|
||||||
|
|
@ -20,9 +21,9 @@ func NewMuxTracer(tracers ...vm.EVMLogger) *MuxTracer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart runs CaptureStart for each tracer in the MuxTracer
|
// CaptureStart runs CaptureStart for each tracer in the MuxTracer
|
||||||
func (t *MuxTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *MuxTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int, authorizationResults []types.AuthorizationResult) {
|
||||||
for _, tracer := range t.tracers {
|
for _, tracer := range t.tracers {
|
||||||
tracer.CaptureStart(env, from, to, create, input, gas, value)
|
tracer.CaptureStart(env, from, to, create, input, gas, value, authorizationResults)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ func CreateTraceEnvHelper(chainConfig *params.ChainConfig, logConfig *vm.LogConf
|
||||||
commitAfterApply: commitAfterApply,
|
commitAfterApply: commitAfterApply,
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
signer: types.MakeSigner(chainConfig, block.Number()),
|
signer: types.MakeSigner(chainConfig, block.Number(), block.Time()),
|
||||||
state: statedb,
|
state: statedb,
|
||||||
blockCtx: blockCtx,
|
blockCtx: blockCtx,
|
||||||
StorageTrace: &types.StorageTrace{
|
StorageTrace: &types.StorageTrace{
|
||||||
|
|
@ -533,7 +533,7 @@ func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, erro
|
||||||
|
|
||||||
txs := make([]*types.TransactionData, block.Transactions().Len())
|
txs := make([]*types.TransactionData, block.Transactions().Len())
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
txs[i] = types.NewTransactionData(tx, block.NumberU64(), env.chainConfig)
|
txs[i] = types.NewTransactionData(tx, block.NumberU64(), block.Time(), env.chainConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
intrinsicStorageProofs := map[common.Address][]common.Hash{
|
intrinsicStorageProofs := map[common.Address][]common.Hash{
|
||||||
|
|
|
||||||
75
tests/gen_stauthorization.go
Normal file
75
tests/gen_stauthorization.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/common/math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*stAuthorizationMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (s stAuthorization) MarshalJSON() ([]byte, error) {
|
||||||
|
type stAuthorization struct {
|
||||||
|
ChainID *math.HexOrDecimal256 `json:"chainId" gencodec:"required"`
|
||||||
|
Address common.Address `json:"address" gencodec:"required"`
|
||||||
|
Nonce math.HexOrDecimal64 `json:"nonce" gencodec:"required"`
|
||||||
|
V math.HexOrDecimal64 `json:"v" gencodec:"required"`
|
||||||
|
R *math.HexOrDecimal256 `json:"r" gencodec:"required"`
|
||||||
|
S *math.HexOrDecimal256 `json:"s" gencodec:"required"`
|
||||||
|
}
|
||||||
|
var enc stAuthorization
|
||||||
|
enc.ChainID = (*math.HexOrDecimal256)(s.ChainID)
|
||||||
|
enc.Address = s.Address
|
||||||
|
enc.Nonce = math.HexOrDecimal64(s.Nonce)
|
||||||
|
enc.V = math.HexOrDecimal64(s.V)
|
||||||
|
enc.R = (*math.HexOrDecimal256)(s.R)
|
||||||
|
enc.S = (*math.HexOrDecimal256)(s.S)
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (s *stAuthorization) UnmarshalJSON(input []byte) error {
|
||||||
|
type stAuthorization struct {
|
||||||
|
ChainID *math.HexOrDecimal256 `json:"chainId" gencodec:"required"`
|
||||||
|
Address *common.Address `json:"address" gencodec:"required"`
|
||||||
|
Nonce *math.HexOrDecimal64 `json:"nonce" gencodec:"required"`
|
||||||
|
V *math.HexOrDecimal64 `json:"v" gencodec:"required"`
|
||||||
|
R *math.HexOrDecimal256 `json:"r" gencodec:"required"`
|
||||||
|
S *math.HexOrDecimal256 `json:"s" gencodec:"required"`
|
||||||
|
}
|
||||||
|
var dec stAuthorization
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.ChainID == nil {
|
||||||
|
return errors.New("missing required field 'chainId' for stAuthorization")
|
||||||
|
}
|
||||||
|
s.ChainID = (*big.Int)(dec.ChainID)
|
||||||
|
if dec.Address == nil {
|
||||||
|
return errors.New("missing required field 'address' for stAuthorization")
|
||||||
|
}
|
||||||
|
s.Address = *dec.Address
|
||||||
|
if dec.Nonce == nil {
|
||||||
|
return errors.New("missing required field 'nonce' for stAuthorization")
|
||||||
|
}
|
||||||
|
s.Nonce = uint64(*dec.Nonce)
|
||||||
|
if dec.V == nil {
|
||||||
|
return errors.New("missing required field 'v' for stAuthorization")
|
||||||
|
}
|
||||||
|
s.V = uint8(*dec.V)
|
||||||
|
if dec.R == nil {
|
||||||
|
return errors.New("missing required field 'r' for stAuthorization")
|
||||||
|
}
|
||||||
|
s.R = (*big.Int)(dec.R)
|
||||||
|
if dec.S == nil {
|
||||||
|
return errors.New("missing required field 's' for stAuthorization")
|
||||||
|
}
|
||||||
|
s.S = (*big.Int)(dec.S)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) {
|
||||||
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
||||||
Value []string `json:"value"`
|
Value []string `json:"value"`
|
||||||
PrivateKey hexutil.Bytes `json:"secretKey"`
|
PrivateKey hexutil.Bytes `json:"secretKey"`
|
||||||
|
AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"`
|
||||||
}
|
}
|
||||||
var enc stTransaction
|
var enc stTransaction
|
||||||
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
|
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
|
||||||
|
|
@ -43,6 +44,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
enc.Value = s.Value
|
enc.Value = s.Value
|
||||||
enc.PrivateKey = s.PrivateKey
|
enc.PrivateKey = s.PrivateKey
|
||||||
|
enc.AuthorizationList = s.AuthorizationList
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,6 +61,7 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
|
||||||
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
||||||
Value []string `json:"value"`
|
Value []string `json:"value"`
|
||||||
PrivateKey *hexutil.Bytes `json:"secretKey"`
|
PrivateKey *hexutil.Bytes `json:"secretKey"`
|
||||||
|
AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"`
|
||||||
}
|
}
|
||||||
var dec stTransaction
|
var dec stTransaction
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -97,5 +100,8 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
|
||||||
if dec.PrivateKey != nil {
|
if dec.PrivateKey != nil {
|
||||||
s.PrivateKey = *dec.PrivateKey
|
s.PrivateKey = *dec.PrivateKey
|
||||||
}
|
}
|
||||||
|
if dec.AuthorizationList != nil {
|
||||||
|
s.AuthorizationList = dec.AuthorizationList
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
BerlinBlock: big.NewInt(0),
|
BerlinBlock: big.NewInt(0),
|
||||||
LondonBlock: big.NewInt(0),
|
LondonBlock: big.NewInt(0),
|
||||||
|
CurieBlock: big.NewInt(0), // EIP-1559 is enabled in Curie fork
|
||||||
},
|
},
|
||||||
"ArrowGlacier": {
|
"ArrowGlacier": {
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
|
|
@ -232,6 +233,29 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
BernoulliBlock: big.NewInt(0),
|
BernoulliBlock: big.NewInt(0),
|
||||||
CurieBlock: big.NewInt(0),
|
CurieBlock: big.NewInt(0),
|
||||||
},
|
},
|
||||||
|
"EuclidV2": {
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
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),
|
||||||
|
ArchimedesBlock: big.NewInt(0),
|
||||||
|
ShanghaiBlock: big.NewInt(0),
|
||||||
|
BernoulliBlock: big.NewInt(0),
|
||||||
|
CurieBlock: big.NewInt(0),
|
||||||
|
DarwinTime: new(uint64),
|
||||||
|
DarwinV2Time: new(uint64),
|
||||||
|
EuclidTime: new(uint64),
|
||||||
|
EuclidV2Time: new(uint64),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the set of defined fork names
|
// Returns the set of defined fork names
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||||
"github.com/scroll-tech/go-ethereum/common/math"
|
"github.com/scroll-tech/go-ethereum/common/math"
|
||||||
|
|
@ -111,6 +113,7 @@ type stTransaction struct {
|
||||||
GasLimit []uint64 `json:"gasLimit"`
|
GasLimit []uint64 `json:"gasLimit"`
|
||||||
Value []string `json:"value"`
|
Value []string `json:"value"`
|
||||||
PrivateKey []byte `json:"secretKey"`
|
PrivateKey []byte `json:"secretKey"`
|
||||||
|
AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type stTransactionMarshaling struct {
|
type stTransactionMarshaling struct {
|
||||||
|
|
@ -122,6 +125,27 @@ type stTransactionMarshaling struct {
|
||||||
PrivateKey hexutil.Bytes
|
PrivateKey hexutil.Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type stAuthorization -field-override stAuthorizationMarshaling -out gen_stauthorization.go
|
||||||
|
|
||||||
|
// Authorization is an authorization from an account to deploy code at it's address.
|
||||||
|
type stAuthorization struct {
|
||||||
|
ChainID *big.Int `json:"chainId" gencodec:"required"`
|
||||||
|
Address common.Address `json:"address" gencodec:"required"`
|
||||||
|
Nonce uint64 `json:"nonce" gencodec:"required"`
|
||||||
|
V uint8 `json:"v" gencodec:"required"`
|
||||||
|
R *big.Int `json:"r" gencodec:"required"`
|
||||||
|
S *big.Int `json:"s" gencodec:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// field type overrides for gencodec
|
||||||
|
type stAuthorizationMarshaling struct {
|
||||||
|
ChainID *math.HexOrDecimal256
|
||||||
|
Nonce math.HexOrDecimal64
|
||||||
|
V math.HexOrDecimal64
|
||||||
|
R *math.HexOrDecimal256
|
||||||
|
S *math.HexOrDecimal256
|
||||||
|
}
|
||||||
|
|
||||||
// GetChainConfig takes a fork definition and returns a chain config.
|
// GetChainConfig takes a fork definition and returns a chain config.
|
||||||
// The fork definition can be
|
// The fork definition can be
|
||||||
// - a plain forkname, e.g. `Byzantium`,
|
// - a plain forkname, e.g. `Byzantium`,
|
||||||
|
|
@ -355,8 +379,23 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (core.Messa
|
||||||
return nil, fmt.Errorf("no gas price provided")
|
return nil, fmt.Errorf("no gas price provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var authList []types.SetCodeAuthorization
|
||||||
|
if tx.AuthorizationList != nil {
|
||||||
|
authList = make([]types.SetCodeAuthorization, len(tx.AuthorizationList))
|
||||||
|
for i, auth := range tx.AuthorizationList {
|
||||||
|
authList[i] = types.SetCodeAuthorization{
|
||||||
|
ChainID: *uint256.MustFromBig(auth.ChainID),
|
||||||
|
Address: auth.Address,
|
||||||
|
Nonce: auth.Nonce,
|
||||||
|
V: auth.V,
|
||||||
|
R: *uint256.MustFromBig(auth.R),
|
||||||
|
S: *uint256.MustFromBig(auth.S),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, gasPrice,
|
msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, gasPrice,
|
||||||
tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, false)
|
tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, false, authList)
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Intrinsic gas
|
// Intrinsic gas
|
||||||
requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false)
|
requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, isHomestead, isIstanbul, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue