mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
all: impl eip-7702
Co-authored-by: lightclient <lightclient@protonmail.com> Co-authored-by: Mario Vega <marioevz@gmail.com>
This commit is contained in:
parent
9d87ac7c12
commit
9db178c799
29 changed files with 1078 additions and 48 deletions
|
|
@ -133,7 +133,7 @@ func Transaction(ctx *cli.Context) error {
|
|||
r.Address = sender
|
||||
}
|
||||
// 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.AuthList(), tx.To() == nil,
|
||||
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int), 0)); err != nil {
|
||||
r.Error = err
|
||||
results = append(results, r)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func runStateTest(fname string, cfg vm.Config, dump bool) error {
|
|||
}
|
||||
var testsByName map[string]tests.StateTest
|
||||
if err := json.Unmarshal(src, &testsByName); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to read test file: %w", err)
|
||||
}
|
||||
|
||||
// Iterate over all the tests, run them and aggregate the results
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
data := make([]byte, nbytes)
|
||||
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
|
||||
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false)
|
||||
signer := gen.Signer()
|
||||
gasPrice := big.NewInt(0)
|
||||
if gen.header.BaseFee != nil {
|
||||
|
|
|
|||
|
|
@ -112,4 +112,7 @@ var (
|
|||
|
||||
// ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
|
||||
ErrBlobTxCreate = errors.New("blob transaction of type create")
|
||||
|
||||
// ErrEmptyAuthList is returned if a set code transaction has an empty auth list.
|
||||
ErrEmptyAuthList = errors.New("set code transaction with empty auth list")
|
||||
)
|
||||
|
|
|
|||
131
core/setcode_test.go
Normal file
131
core/setcode_test.go
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
func TestEIP7702(t *testing.T) {
|
||||
var (
|
||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
||||
engine = beacon.NewFaker()
|
||||
|
||||
// A sender who makes transactions, has some funds
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||
config = *params.AllEthashProtocolChanges
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {Balance: funds},
|
||||
addr2: {Balance: funds},
|
||||
// The address 0xAAAA sstores 1 into slot 2.
|
||||
aa: {
|
||||
Code: []byte{
|
||||
byte(vm.PC), // [0]
|
||||
byte(vm.DUP1), // [0,0]
|
||||
byte(vm.DUP1), // [0,0,0]
|
||||
byte(vm.DUP1), // [0,0,0,0]
|
||||
byte(vm.PUSH1), 0x01, // [0,0,0,0,1] (value)
|
||||
byte(vm.PUSH20), addr2[0], addr2[1], addr2[2], addr2[3], addr2[4], addr2[5], addr2[6], addr2[7], addr2[8], addr2[9], addr2[10], addr2[11], addr2[12], addr2[13], addr2[14], addr2[15], addr2[16], addr2[17], addr2[18], addr2[19],
|
||||
byte(vm.GAS),
|
||||
byte(vm.CALL),
|
||||
byte(vm.STOP),
|
||||
},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
// The address 0xBBBB sstores 42 into slot 42.
|
||||
bb: {
|
||||
Code: []byte{
|
||||
byte(vm.PUSH1), 0x42,
|
||||
byte(vm.DUP1),
|
||||
byte(vm.SSTORE),
|
||||
byte(vm.STOP),
|
||||
},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
gspec.Config.BerlinBlock = common.Big0
|
||||
gspec.Config.LondonBlock = common.Big0
|
||||
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||
gspec.Config.TerminalTotalDifficultyPassed = true
|
||||
gspec.Config.ShanghaiTime = u64(0)
|
||||
gspec.Config.CancunTime = u64(0)
|
||||
gspec.Config.PragueTime = u64(0)
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
|
||||
auth1, _ := types.SignAuth(&types.Authorization{
|
||||
ChainID: new(big.Int).Set(gspec.Config.ChainID),
|
||||
Address: aa,
|
||||
Nonce: 1,
|
||||
}, key1)
|
||||
|
||||
auth2, _ := types.SignAuth(&types.Authorization{
|
||||
ChainID: new(big.Int),
|
||||
Address: bb,
|
||||
Nonce: 0,
|
||||
}, key2)
|
||||
|
||||
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 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.Authorization{auth1, auth2},
|
||||
}
|
||||
tx := types.NewTx(txdata)
|
||||
tx, err := types.SignTx(tx, signer, key1)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
b.AddTx(tx)
|
||||
})
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
defer chain.Stop()
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
var (
|
||||
state, _ = chain.State()
|
||||
fortyTwo = common.BytesToHash([]byte{0x42})
|
||||
actual = state.GetState(addr2, fortyTwo)
|
||||
)
|
||||
if code, want := state.GetCode(addr1), types.AddressToDelegation(auth1.Address); !bytes.Equal(code, want) {
|
||||
t.Fatalf("addr1 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
|
||||
}
|
||||
if code, want := state.GetCode(addr2), types.AddressToDelegation(auth2.Address); !bytes.Equal(code, want) {
|
||||
t.Fatalf("addr2 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
|
||||
}
|
||||
if actual.Cmp(fortyTwo) != 0 {
|
||||
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
|
||||
}
|
||||
}
|
||||
|
|
@ -384,6 +384,22 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
|||
return common.Hash{}
|
||||
}
|
||||
|
||||
func (s *StateDB) ResolveCode(addr common.Address) []byte {
|
||||
stateObject := s.resolveStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.Code()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StateDB) ResolveCodeHash(addr common.Address) common.Hash {
|
||||
stateObject := s.resolveStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return common.BytesToHash(stateObject.CodeHash())
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
// GetState retrieves the value associated with the specific key.
|
||||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
|
|
@ -639,6 +655,18 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
|||
return obj
|
||||
}
|
||||
|
||||
func (s *StateDB) resolveStateObject(addr common.Address) *stateObject {
|
||||
obj := s.getStateObject(addr)
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
addr, ok := types.ParseDelegation(obj.Code())
|
||||
if !ok {
|
||||
return obj
|
||||
}
|
||||
return s.getStateObject(addr)
|
||||
}
|
||||
|
||||
func (s *StateDB) setStateObject(object *stateObject) {
|
||||
s.stateObjects[object.Address()] = object
|
||||
}
|
||||
|
|
@ -1381,6 +1409,10 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
|
|||
al.AddAddress(sender)
|
||||
if dst != nil {
|
||||
al.AddAddress(*dst)
|
||||
// TODO: is this right?
|
||||
if addr, ok := types.ParseDelegation(s.GetCode(*dst)); ok {
|
||||
al.AddAddress(addr)
|
||||
}
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
|
|
|
|||
|
|
@ -431,12 +431,12 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||
|
||||
var (
|
||||
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, true, true, true, true)
|
||||
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true)
|
||||
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
|
||||
// will not contain that copied data.
|
||||
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
|
||||
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
|
||||
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, true, true, true, true)
|
||||
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true)
|
||||
)
|
||||
|
||||
func TestProcessVerkle(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
|
@ -27,6 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -68,7 +70,7 @@ func (result *ExecutionResult) Revert() []byte {
|
|||
}
|
||||
|
||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, authList types.AuthorizationList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas uint64
|
||||
if isContractCreation && isHomestead {
|
||||
|
|
@ -114,6 +116,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
|
|||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
|
||||
}
|
||||
if authList != nil {
|
||||
gas += uint64(len(authList)) * params.CallNewAccountGas
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +146,7 @@ type Message struct {
|
|||
AccessList types.AccessList
|
||||
BlobGasFeeCap *big.Int
|
||||
BlobHashes []common.Hash
|
||||
AuthList types.AuthorizationList
|
||||
|
||||
// When SkipAccountChecks is true, the message nonce is not checked against the
|
||||
// account nonce in state. It also disables checking that the sender is an EOA.
|
||||
|
|
@ -160,6 +166,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
|||
Value: tx.Value(),
|
||||
Data: tx.Data(),
|
||||
AccessList: tx.AccessList(),
|
||||
AuthList: tx.AuthList(),
|
||||
SkipAccountChecks: false,
|
||||
BlobHashes: tx.BlobHashes(),
|
||||
BlobGasFeeCap: tx.BlobGasFeeCap(),
|
||||
|
|
@ -294,10 +301,10 @@ func (st *StateTransition) preCheck() error {
|
|||
msg.From.Hex(), stNonce)
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
codeHash := st.state.GetCodeHash(msg.From)
|
||||
if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash {
|
||||
code := st.state.GetCode(msg.From)
|
||||
if 0 < len(code) && !bytes.HasPrefix(code, []byte{0xef, 0x01, 0x00}) {
|
||||
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
|
||||
msg.From.Hex(), codeHash)
|
||||
msg.From.Hex(), st.state.GetCodeHash(msg.From))
|
||||
}
|
||||
}
|
||||
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
|
||||
|
|
@ -357,6 +364,27 @@ func (st *StateTransition) preCheck() error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that auth list isn't empty.
|
||||
if msg.AuthList != nil && len(msg.AuthList) == 0 {
|
||||
return fmt.Errorf("%w: address %v", ErrEmptyAuthList, msg.From.Hex())
|
||||
}
|
||||
|
||||
// TODO: remove after this spec change is merged:
|
||||
// https://github.com/ethereum/EIPs/pull/8845
|
||||
if msg.AuthList != nil {
|
||||
var (
|
||||
secp256k1N = secp256k1.S256().Params().N
|
||||
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
|
||||
)
|
||||
for _, auth := range msg.AuthList {
|
||||
if auth.V.Cmp(common.Big1) > 0 || auth.S.Cmp(secp256k1halfN) > 0 {
|
||||
w := fmt.Errorf("set code transaction with invalid auth signature")
|
||||
return fmt.Errorf("%w: address %v", w, msg.From.Hex())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return st.buyGas()
|
||||
}
|
||||
|
||||
|
|
@ -394,7 +422,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
)
|
||||
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
gas, err := IntrinsicGas(msg.Data, msg.AccessList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.AuthList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -433,6 +461,42 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
// - reset transient storage(eip 1153)
|
||||
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
||||
|
||||
if !contractCreation {
|
||||
// Increment the nonce for the next transaction
|
||||
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
|
||||
}
|
||||
|
||||
// Check authorizations list validity.
|
||||
if msg.AuthList != nil {
|
||||
for _, auth := range msg.AuthList {
|
||||
// Verify chain ID is 0 or equal to current chain ID.
|
||||
if auth.ChainID.Sign() != 0 && st.evm.ChainConfig().ChainID.Cmp(auth.ChainID) != 0 {
|
||||
continue
|
||||
}
|
||||
authority, err := auth.Authority()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Check the authority account 1) doesn't have code or has exisiting
|
||||
// delegation 2) matches the auth's nonce
|
||||
st.state.AddAddressToAccessList(authority)
|
||||
code := st.state.GetCode(authority)
|
||||
if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok {
|
||||
continue
|
||||
}
|
||||
if have := st.state.GetNonce(authority); have != auth.Nonce {
|
||||
continue
|
||||
}
|
||||
// If the account already exists in state, refund the new account cost
|
||||
// charged in the initrinsic calculation.
|
||||
if exists := st.state.Exist(authority); exists {
|
||||
st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas)
|
||||
}
|
||||
st.state.SetNonce(authority, auth.Nonce+1)
|
||||
st.state.SetCode(authority, types.AddressToDelegation(auth.Address))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
|
|
@ -440,8 +504,6 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
if contractCreation {
|
||||
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
|
||||
} else {
|
||||
// Increment the nonce for the next transaction
|
||||
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
|
||||
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ func New(config Config, chain BlockChain) *LegacyPool {
|
|||
// pool, specifically, whether it is a Legacy, AccessList or Dynamic transaction.
|
||||
func (pool *LegacyPool) Filter(tx *types.Transaction) bool {
|
||||
switch tx.Type() {
|
||||
case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType:
|
||||
case types.SetCodeTxType, types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
@ -610,7 +610,8 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
|
|||
Accept: 0 |
|
||||
1<<types.LegacyTxType |
|
||||
1<<types.AccessListTxType |
|
||||
1<<types.DynamicFeeTxType,
|
||||
1<<types.DynamicFeeTxType |
|
||||
1<<types.SetCodeTxType,
|
||||
MaxSize: txMaxSize,
|
||||
MinTip: pool.gasTip.Load().ToBig(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
}
|
||||
// Ensure the transaction has more gas than the bare minimum needed to cover
|
||||
// the transaction metadata
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time))
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.AuthList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
74
core/types/gen_authorization.go
Normal file
74
core/types/gen_authorization.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
var _ = (*authorizationMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (a Authorization) MarshalJSON() ([]byte, error) {
|
||||
type Authorization struct {
|
||||
ChainID *hexutil.Big
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
Nonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
V *hexutil.Big `json:"v" gencodec:"required"`
|
||||
R *hexutil.Big `json:"r" gencodec:"required"`
|
||||
S *hexutil.Big `json:"s" gencodec:"required"`
|
||||
}
|
||||
var enc Authorization
|
||||
enc.ChainID = (*hexutil.Big)(a.ChainID)
|
||||
enc.Address = a.Address
|
||||
enc.Nonce = hexutil.Uint64(a.Nonce)
|
||||
enc.V = (*hexutil.Big)(a.V)
|
||||
enc.R = (*hexutil.Big)(a.R)
|
||||
enc.S = (*hexutil.Big)(a.S)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (a *Authorization) UnmarshalJSON(input []byte) error {
|
||||
type Authorization struct {
|
||||
ChainID *hexutil.Big
|
||||
Address *common.Address `json:"address" gencodec:"required"`
|
||||
Nonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
V *hexutil.Big `json:"v" gencodec:"required"`
|
||||
R *hexutil.Big `json:"r" gencodec:"required"`
|
||||
S *hexutil.Big `json:"s" gencodec:"required"`
|
||||
}
|
||||
var dec Authorization
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.ChainID != nil {
|
||||
a.ChainID = (*big.Int)(dec.ChainID)
|
||||
}
|
||||
if dec.Address == nil {
|
||||
return errors.New("missing required field 'address' for Authorization")
|
||||
}
|
||||
a.Address = *dec.Address
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' for Authorization")
|
||||
}
|
||||
a.Nonce = uint64(*dec.Nonce)
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' for Authorization")
|
||||
}
|
||||
a.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' for Authorization")
|
||||
}
|
||||
a.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' for Authorization")
|
||||
}
|
||||
a.S = (*big.Int)(dec.S)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -204,7 +204,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
|
|||
return errShortTypedReceipt
|
||||
}
|
||||
switch b[0] {
|
||||
case DynamicFeeTxType, AccessListTxType, BlobTxType:
|
||||
case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType:
|
||||
var data receiptRLP
|
||||
err := rlp.DecodeBytes(b[1:], &data)
|
||||
if err != nil {
|
||||
|
|
@ -312,7 +312,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
|||
}
|
||||
w.WriteByte(r.Type)
|
||||
switch r.Type {
|
||||
case AccessListTxType, DynamicFeeTxType, BlobTxType:
|
||||
case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType:
|
||||
rlp.Encode(w, data)
|
||||
default:
|
||||
// For unsupported types, write nothing. Since this is for
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ const (
|
|||
AccessListTxType = 0x01
|
||||
DynamicFeeTxType = 0x02
|
||||
BlobTxType = 0x03
|
||||
SetCodeTxType = 0x04
|
||||
)
|
||||
|
||||
// Transaction is an Ethereum transaction.
|
||||
|
|
@ -206,6 +207,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
|||
inner = new(DynamicFeeTx)
|
||||
case BlobTxType:
|
||||
inner = new(BlobTx)
|
||||
case SetCodeTxType:
|
||||
inner = new(SetCodeTx)
|
||||
default:
|
||||
return nil, ErrTxTypeNotSupported
|
||||
}
|
||||
|
|
@ -466,6 +469,15 @@ func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction {
|
|||
return cpy
|
||||
}
|
||||
|
||||
// AuthList returns the authorizations list of the transaction.
|
||||
func (tx *Transaction) AuthList() AuthorizationList {
|
||||
setcodetx, ok := tx.inner.(*SetCodeTx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return setcodetx.AuthList
|
||||
}
|
||||
|
||||
// SetTime sets the decoding time of a transaction. This is used by tests to set
|
||||
// arbitrary times and by persistent transaction pools when loading old txs from
|
||||
// disk.
|
||||
|
|
|
|||
|
|
@ -31,22 +31,23 @@ import (
|
|||
type txJSON struct {
|
||||
Type hexutil.Uint64 `json:"type"`
|
||||
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
|
||||
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
|
||||
MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Input *hexutil.Bytes `json:"input"`
|
||||
AccessList *AccessList `json:"accessList,omitempty"`
|
||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
|
||||
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
|
||||
MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Input *hexutil.Bytes `json:"input"`
|
||||
AccessList *AccessList `json:"accessList,omitempty"`
|
||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||
AuthorizationList *AuthorizationList `json:"authorizationList,omitempty"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
|
||||
|
||||
// Blob transaction sidecar encoding:
|
||||
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
|
||||
|
|
@ -153,6 +154,23 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
|||
enc.Commitments = itx.Sidecar.Commitments
|
||||
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)
|
||||
}
|
||||
|
|
@ -409,6 +427,81 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
|||
}
|
||||
}
|
||||
|
||||
case SetCodeTxType:
|
||||
var itx SetCodeTx
|
||||
inner = &itx
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
itx.ChainID = uint256.MustFromBig((*big.Int)(dec.ChainID))
|
||||
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
|
||||
var overflow bool
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ type sigCache struct {
|
|||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
|
||||
var signer Signer
|
||||
switch {
|
||||
case config.IsPrague(blockNumber, blockTime):
|
||||
signer = NewPragueSigner(config.ChainID)
|
||||
case config.IsCancun(blockNumber, blockTime):
|
||||
signer = NewCancunSigner(config.ChainID)
|
||||
case config.IsLondon(blockNumber):
|
||||
|
|
@ -65,6 +67,9 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint
|
|||
// have the current block number available, use MakeSigner instead.
|
||||
func LatestSigner(config *params.ChainConfig) Signer {
|
||||
if config.ChainID != nil {
|
||||
if config.PragueTime != nil {
|
||||
return NewPragueSigner(config.ChainID)
|
||||
}
|
||||
if config.CancunTime != nil {
|
||||
return NewCancunSigner(config.ChainID)
|
||||
}
|
||||
|
|
@ -168,6 +173,76 @@ type Signer interface {
|
|||
Equal(Signer) bool
|
||||
}
|
||||
|
||||
type pragueSigner struct{ cancunSigner }
|
||||
|
||||
// NewPragueSigner 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 NewPragueSigner(chainId *big.Int) Signer {
|
||||
return pragueSigner{cancunSigner{londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}}}
|
||||
}
|
||||
|
||||
func (s pragueSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.Type() != SetCodeTxType {
|
||||
return s.cancunSigner.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 pragueSigner) Equal(s2 Signer) bool {
|
||||
x, ok := s2.(pragueSigner)
|
||||
return ok && x.chainId.Cmp(s.chainId) == 0
|
||||
}
|
||||
|
||||
func (s pragueSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
txdata, ok := tx.inner.(*SetCodeTx)
|
||||
if !ok {
|
||||
return s.cancunSigner.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.Sign() != 0 && txdata.ChainID.ToBig().Cmp(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 pragueSigner) Hash(tx *Transaction) common.Hash {
|
||||
if tx.Type() != SetCodeTxType {
|
||||
return s.cancunSigner.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.AuthList(),
|
||||
})
|
||||
}
|
||||
|
||||
type cancunSigner struct{ londonSigner }
|
||||
|
||||
// NewCancunSigner returns a signer that accepts
|
||||
|
|
|
|||
256
core/types/tx_setcode.go
Normal file
256
core/types/tx_setcode.go
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
var addr common.Address
|
||||
copy(addr[:], b[len(DelegationPrefix):])
|
||||
return addr, true
|
||||
}
|
||||
|
||||
// AddressToDelegation adds the delegation prefix to the specified address.
|
||||
func AddressToDelegation(addr common.Address) []byte {
|
||||
return append(DelegationPrefix, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// 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 AuthorizationList
|
||||
|
||||
// Signature values
|
||||
V *uint256.Int `json:"v" gencodec:"required"`
|
||||
R *uint256.Int `json:"r" gencodec:"required"`
|
||||
S *uint256.Int `json:"s" gencodec:"required"`
|
||||
}
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type Authorization -field-override authorizationMarshaling -out gen_authorization.go
|
||||
|
||||
// Authorization is an authorization from an account to deploy code at it's
|
||||
// address.
|
||||
type Authorization struct {
|
||||
ChainID *big.Int
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
Nonce uint64 `json:"nonce" gencodec:"required"`
|
||||
V *big.Int `json:"v" gencodec:"required"`
|
||||
R *big.Int `json:"r" gencodec:"required"`
|
||||
S *big.Int `json:"s" gencodec:"required"`
|
||||
}
|
||||
|
||||
// field type overrides for gencodec
|
||||
type authorizationMarshaling struct {
|
||||
ChainID *hexutil.Big
|
||||
Nonce hexutil.Uint64
|
||||
V *hexutil.Big
|
||||
R *hexutil.Big
|
||||
S *hexutil.Big
|
||||
}
|
||||
|
||||
func SignAuth(auth *Authorization, prv *ecdsa.PrivateKey) (*Authorization, error) {
|
||||
h := prefixedRlpHash(
|
||||
0x05,
|
||||
[]interface{}{
|
||||
auth.ChainID,
|
||||
auth.Address,
|
||||
auth.Nonce,
|
||||
})
|
||||
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return auth.WithSignature(sig), nil
|
||||
}
|
||||
|
||||
func (a *Authorization) WithSignature(sig []byte) *Authorization {
|
||||
r, s, _ := decodeSignature(sig)
|
||||
v := big.NewInt(int64(sig[64]))
|
||||
cpy := Authorization{
|
||||
ChainID: a.ChainID,
|
||||
Address: a.Address,
|
||||
Nonce: a.Nonce,
|
||||
V: v,
|
||||
R: r,
|
||||
S: s,
|
||||
}
|
||||
return &cpy
|
||||
}
|
||||
|
||||
type AuthorizationList []*Authorization
|
||||
|
||||
func (a Authorization) Authority() (common.Address, error) {
|
||||
sighash := prefixedRlpHash(
|
||||
0x05,
|
||||
[]interface{}{
|
||||
a.ChainID,
|
||||
a.Address,
|
||||
a.Nonce,
|
||||
})
|
||||
|
||||
v := byte(a.V.Uint64())
|
||||
if !crypto.ValidateSignatureValues(v, a.R, a.S, true) {
|
||||
return common.Address{}, ErrInvalidSig
|
||||
}
|
||||
// encode the signature in uncompressed format
|
||||
r, s := a.R.Bytes(), a.S.Bytes()
|
||||
sig := make([]byte, crypto.SignatureLength)
|
||||
copy(sig[32-len(r):32], r)
|
||||
copy(sig[64-len(s):64], s)
|
||||
sig[64] = 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
|
||||
}
|
||||
|
||||
type SetCodeDelegation struct {
|
||||
From common.Address
|
||||
Nonce uint64
|
||||
Target common.Address
|
||||
}
|
||||
|
||||
/*
|
||||
func (a AuthorizationList) WithAddress() []AuthorizationTuple {
|
||||
var (
|
||||
list = make([]AuthorizationTuple, len(a))
|
||||
sha = hasherPool.Get().(crypto.KeccakState)
|
||||
h = common.Hash{}
|
||||
)
|
||||
defer hasherPool.Put(sha)
|
||||
for i, auth := range a {
|
||||
sha.Reset()
|
||||
sha.Write([]byte{0x05})
|
||||
sha.Write(auth.Code)
|
||||
sha.Read(h[:])
|
||||
addr, err := recoverPlain(h, auth.R, auth.S, auth.V, false)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
list[i].Address = addr
|
||||
copy(list[i].Code, auth.Code)
|
||||
}
|
||||
return list
|
||||
}
|
||||
*/
|
||||
|
||||
// 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(AuthorizationList, 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) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
if baseFee == nil {
|
||||
return dst.Set(tx.GasFeeCap.ToBig())
|
||||
}
|
||||
tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee)
|
||||
if tip.Cmp(tx.GasTipCap.ToBig()) > 0 {
|
||||
tip.Set(tx.GasTipCap.ToBig())
|
||||
}
|
||||
return tip.Add(tip, baseFee)
|
||||
}
|
||||
|
||||
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.SetFromBig(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)
|
||||
}
|
||||
15
core/types/tx_setcode_test.go
Normal file
15
core/types/tx_setcode_test.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestParseDelegation(t *testing.T) {
|
||||
addr := common.Address{0x42}
|
||||
d := append(DelegationPrefix, addr.Bytes()...)
|
||||
if got, ok := ParseDelegation(d); !ok || addr != got {
|
||||
t.Fatalf("failed to parse, got %s %v", got.Hex(), ok)
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ var activators = map[int]func(*JumpTable){
|
|||
1344: enable1344,
|
||||
1153: enable1153,
|
||||
4762: enable4762,
|
||||
7702: enable7702,
|
||||
}
|
||||
|
||||
// EnableEIP enables the given EIP on the config.
|
||||
|
|
@ -533,3 +534,26 @@ func enable4762(jt *JumpTable) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enable7702(jt *JumpTable) {
|
||||
jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP7702
|
||||
|
||||
jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODESIZE].dynamicGas = gasEip7702CodeCheck
|
||||
|
||||
jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODEHASH].dynamicGas = gasEip7702CodeCheck
|
||||
|
||||
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[CALL].dynamicGas = gasCallEIP7702
|
||||
|
||||
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP7702
|
||||
|
||||
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
|
||||
|
||||
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
} else {
|
||||
// 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.
|
||||
code := evm.StateDB.GetCode(addr)
|
||||
code := evm.StateDB.ResolveCode(addr)
|
||||
if witness := evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddCode(code)
|
||||
}
|
||||
|
|
@ -241,7 +241,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
// If the account has no code, we can abort here
|
||||
// The depth-check is already done, and precompiles handled above
|
||||
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.ResolveCodeHash(addrCopy), code)
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
|
|
@ -302,9 +302,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
|||
// The contract is a scoped environment for this execution context only.
|
||||
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
|
||||
if witness := evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddCode(evm.StateDB.GetCode(addrCopy))
|
||||
witness.AddCode(evm.StateDB.ResolveCode(addrCopy))
|
||||
}
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.ResolveCodeHash(addrCopy), evm.StateDB.ResolveCode(addrCopy))
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
|
|
@ -352,9 +352,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
|||
// Initialise a new contract and make initialise the delegate values
|
||||
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
|
||||
if witness := evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddCode(evm.StateDB.GetCode(addrCopy))
|
||||
witness.AddCode(evm.StateDB.ResolveCode(addrCopy))
|
||||
}
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.ResolveCodeHash(addrCopy), evm.StateDB.ResolveCode(addrCopy))
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
|
|
@ -410,9 +410,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
// The contract is a scoped environment for this execution context only.
|
||||
contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas)
|
||||
if witness := evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddCode(evm.StateDB.GetCode(addrCopy))
|
||||
witness.AddCode(evm.StateDB.ResolveCode(addrCopy))
|
||||
}
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.ResolveCodeHash(addrCopy), evm.StateDB.ResolveCode(addrCopy))
|
||||
// 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
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
|
|
@ -476,7 +476,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
// - the nonce is non-zero
|
||||
// - the code is non-empty
|
||||
// - the storage is non-empty
|
||||
contractHash := evm.StateDB.GetCodeHash(address)
|
||||
contractHash := evm.StateDB.ResolveCodeHash(address)
|
||||
storageRoot := evm.StateDB.GetStorageRoot(address)
|
||||
if evm.StateDB.GetNonce(address) != 0 ||
|
||||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
||||
|
|
|
|||
|
|
@ -342,9 +342,9 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
slot := scope.Stack.peek()
|
||||
address := slot.Bytes20()
|
||||
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddCode(interpreter.evm.StateDB.GetCode(address))
|
||||
witness.AddCode(interpreter.evm.StateDB.ResolveCode(address))
|
||||
}
|
||||
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())))
|
||||
slot.SetUint64(uint64(len(interpreter.evm.StateDB.ResolveCode(slot.Bytes20()))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -382,7 +382,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
uint64CodeOffset = math.MaxUint64
|
||||
}
|
||||
addr := common.Address(a.Bytes20())
|
||||
code := interpreter.evm.StateDB.GetCode(addr)
|
||||
code := interpreter.evm.StateDB.ResolveCode(addr)
|
||||
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddCode(code)
|
||||
}
|
||||
|
|
@ -394,7 +394,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
|
||||
// opExtCodeHash returns the code hash of a specified account.
|
||||
// There are several cases when the function is called, while we can relay everything
|
||||
// to `state.GetCodeHash` function to ensure the correctness.
|
||||
// to `state.ResolveCodeHash` function to ensure the correctness.
|
||||
//
|
||||
// 1. Caller tries to get the code hash of a normal contract account, state
|
||||
// should return the relative code hash and set it as the result.
|
||||
|
|
@ -408,6 +408,9 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
// 4. Caller tries to get the code hash of a precompiled account, the result should be
|
||||
// zero or emptyCodeHash.
|
||||
//
|
||||
// 4. Caller tries to get the code hash of a delegated account, the result should be
|
||||
// equal the result of calling extcodehash on the account directly.
|
||||
//
|
||||
// It is worth noting that in order to avoid unnecessary create and clean, all precompile
|
||||
// accounts on mainnet have been transferred 1 wei, so the return here should be
|
||||
// emptyCodeHash. If the precompile account is not transferred any amount on a private or
|
||||
|
|
@ -424,7 +427,7 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
if interpreter.evm.StateDB.Empty(address) {
|
||||
slot.Clear()
|
||||
} else {
|
||||
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
|
||||
slot.SetBytes(interpreter.evm.StateDB.ResolveCodeHash(address).Bytes())
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ type StateDB interface {
|
|||
SetCode(common.Address, []byte)
|
||||
GetCodeSize(common.Address) int
|
||||
|
||||
ResolveCodeHash(common.Address) common.Hash
|
||||
ResolveCode(common.Address) []byte
|
||||
|
||||
AddRefund(uint64)
|
||||
SubRefund(uint64)
|
||||
GetRefund() uint64
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ func newCancunInstructionSet() JumpTable {
|
|||
enable1153(&instructionSet) // EIP-1153 "Transient Storage"
|
||||
enable5656(&instructionSet) // EIP-5656 (MCOPY opcode)
|
||||
enable6780(&instructionSet) // EIP-6780 SELFDESTRUCT only in same transaction
|
||||
enable7702(&instructionSet)
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
|
@ -248,3 +249,123 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
}
|
||||
return gasFunc
|
||||
}
|
||||
|
||||
var (
|
||||
gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall)
|
||||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall)
|
||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall)
|
||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode)
|
||||
gasSelfdestructEIP7702 = makeSelfdestructGasFn(true)
|
||||
)
|
||||
|
||||
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.Back(1).Bytes20())
|
||||
// Check slot presence in the access list
|
||||
warmAccess := evm.StateDB.AddressInAccessList(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
|
||||
if !warmAccess {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
// Check if code is a delegation and if so, charge for resolution.
|
||||
if addr, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||
var cost uint64
|
||||
if evm.StateDB.AddressInAccessList(addr) {
|
||||
cost += params.WarmStorageReadCostEIP2929
|
||||
} else {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
cost += params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
coldCost += cost
|
||||
}
|
||||
// Now call the old calculator, which takes into account
|
||||
// - create new account
|
||||
// - transfer value
|
||||
// - memory expansion
|
||||
// - 63/64ths rule
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
if warmAccess || err != nil {
|
||||
return gas, err
|
||||
}
|
||||
// In case of a cold access, we temporarily add the cold charge back, and also
|
||||
// add it to the returned gas. By adding it to the return, it will be charged
|
||||
// outside of this function, as part of the dynamic gas, and that will make it
|
||||
// also become correctly reported to tracers.
|
||||
contract.Gas += coldCost
|
||||
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasEip7702CodeCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
cost := uint64(0)
|
||||
// fmt.Println("checking", addr, evm.StateDB.AddressInAccessList(addr))
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The warm storage read cost is already charged as constantGas
|
||||
cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
// Check if code is a delegation and if so, charge for resolution
|
||||
if addr, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||
if evm.StateDB.AddressInAccessList(addr) {
|
||||
cost += params.WarmStorageReadCostEIP2929
|
||||
} else {
|
||||
// fmt.Println("adding ", addr, "to acl")
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
cost += params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
}
|
||||
// fmt.Println("cost is", cost)
|
||||
return cost, nil
|
||||
}
|
||||
|
||||
func gasExtCodeCopyEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
// Check if code is a delegation and if so, charge for resolution
|
||||
if addr, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||
var overflow bool
|
||||
if evm.StateDB.AddressInAccessList(addr) {
|
||||
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
} else {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ const (
|
|||
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
|
||||
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
|
||||
TxAuthTupleGas uint64 = 2500 // Per auth tuple code specified in EIP-7702
|
||||
|
||||
// These have been changed during the course of the chain
|
||||
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
|
||||
|
|
|
|||
74
tests/gen_stauthorization.go
Normal file
74
tests/gen_stauthorization.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
var _ = (*stAuthorizationMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (s stAuthorization) MarshalJSON() ([]byte, error) {
|
||||
type stAuthorization struct {
|
||||
ChainID *math.HexOrDecimal256
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce" gencodec:"required"`
|
||||
V *math.HexOrDecimal256 `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.HexOrDecimal256)(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
|
||||
Address *common.Address `json:"address" gencodec:"required"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce" gencodec:"required"`
|
||||
V *math.HexOrDecimal256 `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 {
|
||||
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 = (*big.Int)(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
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) {
|
|||
Sender *common.Address `json:"sender"`
|
||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||
BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"`
|
||||
AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"`
|
||||
}
|
||||
var enc stTransaction
|
||||
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
|
||||
|
|
@ -50,6 +51,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) {
|
|||
enc.Sender = s.Sender
|
||||
enc.BlobVersionedHashes = s.BlobVersionedHashes
|
||||
enc.BlobGasFeeCap = (*math.HexOrDecimal256)(s.BlobGasFeeCap)
|
||||
enc.AuthorizationList = s.AuthorizationList
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +71,7 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
|
|||
Sender *common.Address `json:"sender"`
|
||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||
BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"`
|
||||
AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"`
|
||||
}
|
||||
var dec stTransaction
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -116,5 +119,8 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
|
|||
if dec.BlobGasFeeCap != nil {
|
||||
s.BlobGasFeeCap = (*big.Int)(dec.BlobGasFeeCap)
|
||||
}
|
||||
if dec.AuthorizationList != nil {
|
||||
s.AuthorizationList = dec.AuthorizationList
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ func TestExecutionSpecState(t *testing.T) {
|
|||
t.Skipf("directory %s does not exist", executionSpecStateTestDir)
|
||||
}
|
||||
st := new(testMatcher)
|
||||
st.runonly("eip7702")
|
||||
|
||||
st.walk(t, executionSpecStateTestDir, func(t *testing.T, name string, test *StateTest) {
|
||||
execStateTest(t, st, test)
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ type stTransaction struct {
|
|||
Sender *common.Address `json:"sender"`
|
||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||
BlobGasFeeCap *big.Int `json:"maxFeePerBlobGas,omitempty"`
|
||||
AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"`
|
||||
}
|
||||
|
||||
type stTransactionMarshaling struct {
|
||||
|
|
@ -135,6 +136,28 @@ type stTransactionMarshaling struct {
|
|||
BlobGasFeeCap *math.HexOrDecimal256
|
||||
}
|
||||
|
||||
//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
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
Nonce uint64 `json:"nonce" gencodec:"required"`
|
||||
V *big.Int `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.HexOrDecimal256
|
||||
R *math.HexOrDecimal256
|
||||
S *math.HexOrDecimal256
|
||||
}
|
||||
|
||||
// GetChainConfig takes a fork definition and returns a chain config.
|
||||
// The fork definition can be
|
||||
// - a plain forkname, e.g. `Byzantium`,
|
||||
|
|
@ -417,6 +440,24 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
|
|||
if gasPrice == nil {
|
||||
return nil, errors.New("no gas price provided")
|
||||
}
|
||||
var authList types.AuthorizationList
|
||||
if tx.AuthorizationList != nil {
|
||||
authList = make(types.AuthorizationList, 0)
|
||||
for _, auth := range tx.AuthorizationList {
|
||||
chainID := auth.ChainID
|
||||
if chainID == nil {
|
||||
chainID = big.NewInt(0)
|
||||
}
|
||||
authList = append(authList, &types.Authorization{
|
||||
ChainID: chainID,
|
||||
Address: auth.Address,
|
||||
Nonce: auth.Nonce,
|
||||
V: auth.V,
|
||||
R: auth.R,
|
||||
S: auth.S,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
msg := &core.Message{
|
||||
From: from,
|
||||
|
|
@ -431,6 +472,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
|
|||
AccessList: accessList,
|
||||
BlobHashes: tx.BlobVersionedHashes,
|
||||
BlobGasFeeCap: tx.BlobGasFeeCap,
|
||||
AuthList: authList,
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
|
|||
return nil, nil, err
|
||||
}
|
||||
// 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.AuthList(), tx.To() == nil, isHomestead, isIstanbul, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue