mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
implement precompile override and moveTo feature
This commit is contained in:
parent
6a2c96c490
commit
b3efbad585
4 changed files with 120 additions and 29 deletions
|
|
@ -138,7 +138,21 @@ func (p PrecompiledContracts) Copy() PrecompiledContracts {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActivePrecompiles returns the precompiles enabled with the current configuration.
|
// ActivePrecompiledContracts returns precompiled contracts enabled with the current configuration.
|
||||||
|
func ActivePrecompiledContracts(rules params.Rules) PrecompiledContracts {
|
||||||
|
switch {
|
||||||
|
case rules.IsBerlin:
|
||||||
|
return PrecompiledContractsBerlin
|
||||||
|
case rules.IsIstanbul:
|
||||||
|
return PrecompiledContractsIstanbul
|
||||||
|
case rules.IsByzantium:
|
||||||
|
return PrecompiledContractsByzantium
|
||||||
|
default:
|
||||||
|
return PrecompiledContractsHomestead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivePrecompiles returns the precompile addresses enabled with the current configuration.
|
||||||
func ActivePrecompiles(rules params.Rules) []common.Address {
|
func ActivePrecompiles(rules params.Rules) []common.Address {
|
||||||
switch {
|
switch {
|
||||||
case rules.IsBerlin:
|
case rules.IsBerlin:
|
||||||
|
|
|
||||||
|
|
@ -123,24 +123,18 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
|
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
|
||||||
}
|
}
|
||||||
switch {
|
evm.precompiles = ActivePrecompiledContracts(evm.chainRules)
|
||||||
case evm.chainRules.IsBerlin:
|
|
||||||
evm.precompiles = PrecompiledContractsBerlin.Copy()
|
|
||||||
case evm.chainRules.IsIstanbul:
|
|
||||||
evm.precompiles = PrecompiledContractsIstanbul.Copy()
|
|
||||||
case evm.chainRules.IsByzantium:
|
|
||||||
evm.precompiles = PrecompiledContractsByzantium.Copy()
|
|
||||||
default:
|
|
||||||
evm.precompiles = PrecompiledContractsHomestead.Copy()
|
|
||||||
}
|
|
||||||
// ECRecoverCode can be set only through RPC calls
|
|
||||||
if config.DisableECRecover {
|
|
||||||
delete(evm.precompiles, common.BytesToAddress([]byte{1}))
|
|
||||||
}
|
|
||||||
evm.interpreter = NewEVMInterpreter(evm)
|
evm.interpreter = NewEVMInterpreter(evm)
|
||||||
return evm
|
return evm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPrecompiles sets the precompiled contracts for the EVM.
|
||||||
|
// This method is only used through RPC calls.
|
||||||
|
// It is not thread-safe.
|
||||||
|
func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) {
|
||||||
|
evm.precompiles = precompiles
|
||||||
|
}
|
||||||
|
|
||||||
// Reset resets the EVM with a new transaction context.Reset
|
// Reset resets the EVM with a new transaction context.Reset
|
||||||
// This is not threadsafe and should only be done very cautiously.
|
// This is not threadsafe and should only be done very cautiously.
|
||||||
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
|
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
|
||||||
|
|
|
||||||
|
|
@ -873,6 +873,7 @@ type OverrideAccount struct {
|
||||||
Balance **hexutil.Big `json:"balance"`
|
Balance **hexutil.Big `json:"balance"`
|
||||||
State *map[common.Hash]common.Hash `json:"state"`
|
State *map[common.Hash]common.Hash `json:"state"`
|
||||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||||
|
MoveTo *common.Address `json:"moveTo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateOverride is the collection of overridden accounts.
|
// StateOverride is the collection of overridden accounts.
|
||||||
|
|
@ -917,6 +918,80 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyMulticall overrides the fields of specified accounts into the given state.
|
||||||
|
func (diff *StateOverride) ApplyMulticall(state *state.StateDB, precompiles vm.PrecompiledContracts) error {
|
||||||
|
if diff == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for addr, account := range *diff {
|
||||||
|
p, isPrecompile := precompiles[addr]
|
||||||
|
// The MoveTo feature makes it possible to replace precompiles and EVM
|
||||||
|
// contracts in all the following configurations:
|
||||||
|
// 1. Precompile -> Precompile
|
||||||
|
// 2. Precompile -> EVM contract
|
||||||
|
// 3. EVM contract -> Precompile
|
||||||
|
// 4. EVM contract -> EVM contract
|
||||||
|
if account.MoveTo != nil {
|
||||||
|
if isPrecompile {
|
||||||
|
// Clear destination account which may be an EVM contract.
|
||||||
|
if !state.Empty(*account.MoveTo) {
|
||||||
|
state.SetCode(*account.MoveTo, nil)
|
||||||
|
state.SetNonce(*account.MoveTo, 0)
|
||||||
|
state.SetBalance(*account.MoveTo, big.NewInt(0))
|
||||||
|
state.SetStorage(*account.MoveTo, map[common.Hash]common.Hash{})
|
||||||
|
}
|
||||||
|
// If destination is a precompile, it will be simply replaced.
|
||||||
|
precompiles[*account.MoveTo] = p
|
||||||
|
} else {
|
||||||
|
state.SetBalance(*account.MoveTo, state.GetBalance(addr))
|
||||||
|
state.SetNonce(*account.MoveTo, state.GetNonce(addr))
|
||||||
|
state.SetCode(*account.MoveTo, state.GetCode(addr))
|
||||||
|
// TODO: copy storage over
|
||||||
|
//state.SetState(*account.MoveTo, state.GetState(addr))
|
||||||
|
// Clear source storage
|
||||||
|
state.SetStorage(addr, map[common.Hash]common.Hash{})
|
||||||
|
if precompiles[*account.MoveTo] != nil {
|
||||||
|
delete(precompiles, *account.MoveTo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isPrecompile {
|
||||||
|
// Now that the contract is moved it can be deleted.
|
||||||
|
delete(precompiles, addr)
|
||||||
|
}
|
||||||
|
// Override account nonce.
|
||||||
|
if account.Nonce != nil {
|
||||||
|
state.SetNonce(addr, uint64(*account.Nonce))
|
||||||
|
}
|
||||||
|
// Override account(contract) code.
|
||||||
|
if account.Code != nil {
|
||||||
|
state.SetCode(addr, *account.Code)
|
||||||
|
}
|
||||||
|
// Override account balance.
|
||||||
|
if account.Balance != nil {
|
||||||
|
state.SetBalance(addr, (*big.Int)(*account.Balance))
|
||||||
|
}
|
||||||
|
if account.State != nil && account.StateDiff != nil {
|
||||||
|
return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
|
||||||
|
}
|
||||||
|
// Replace entire state if caller requires.
|
||||||
|
if account.State != nil {
|
||||||
|
state.SetStorage(addr, *account.State)
|
||||||
|
}
|
||||||
|
// Apply state diff into specified accounts.
|
||||||
|
if account.StateDiff != nil {
|
||||||
|
for key, value := range *account.StateDiff {
|
||||||
|
state.SetState(addr, key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now finalize the changes. Finalize is normally performed between transactions.
|
||||||
|
// By using finalize, the overrides are semantically behaving as
|
||||||
|
// if they were created in a transaction just before the tracing occur.
|
||||||
|
state.Finalise(false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// BlockOverrides is a set of header fields to override.
|
// BlockOverrides is a set of header fields to override.
|
||||||
type BlockOverrides struct {
|
type BlockOverrides struct {
|
||||||
Number *hexutil.Big
|
Number *hexutil.Big
|
||||||
|
|
@ -1013,16 +1088,19 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
||||||
// Make sure the context is cancelled when the call has completed
|
// Make sure the context is cancelled when the call has completed
|
||||||
// this makes sure resources are cleaned up.
|
// this makes sure resources are cleaned up.
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return doCall(ctx, b, args, state, header, timeout, new(core.GasPool).AddGas(globalGasCap), &blockCtx, &vm.Config{NoBaseFee: true})
|
return doCall(ctx, b, args, state, header, timeout, new(core.GasPool).AddGas(globalGasCap), &blockCtx, &vm.Config{NoBaseFee: true}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config) (*core.ExecutionResult, error) {
|
func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts) (*core.ExecutionResult, error) {
|
||||||
// Get a new instance of the EVM.
|
// Get a new instance of the EVM.
|
||||||
msg, err := args.ToMessage(gp.Gas(), header.BaseFee)
|
msg, err := args.ToMessage(gp.Gas(), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
evm, vmError := b.GetEVM(ctx, msg, state, header, vmConfig, blockContext)
|
evm, vmError := b.GetEVM(ctx, msg, state, header, vmConfig, blockContext)
|
||||||
|
if precompiles != nil {
|
||||||
|
evm.SetPrecompiles(precompiles)
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the context to be done and cancel the evm. Even if the
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
// EVM has finished, cancelling may be done (repeatedly)
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
|
|
@ -1170,9 +1248,12 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
|
||||||
globalGasCap = s.b.RPCGasCap()
|
globalGasCap = s.b.RPCGasCap()
|
||||||
gp = new(core.GasPool).AddGas(globalGasCap)
|
gp = new(core.GasPool).AddGas(globalGasCap)
|
||||||
prevNumber = header.Number.Uint64()
|
prevNumber = header.Number.Uint64()
|
||||||
|
blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil)
|
||||||
|
rules = s.b.ChainConfig().Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time)
|
||||||
|
precompiles = vm.ActivePrecompiledContracts(rules).Copy()
|
||||||
)
|
)
|
||||||
for bi, block := range blocks {
|
for bi, block := range blocks {
|
||||||
blockContext := core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil)
|
blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil)
|
||||||
if block.BlockOverrides != nil {
|
if block.BlockOverrides != nil {
|
||||||
block.BlockOverrides.Apply(&blockContext)
|
block.BlockOverrides.Apply(&blockContext)
|
||||||
}
|
}
|
||||||
|
|
@ -1182,7 +1263,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
|
||||||
}
|
}
|
||||||
prevNumber = blockContext.BlockNumber.Uint64()
|
prevNumber = blockContext.BlockNumber.Uint64()
|
||||||
// State overrides are applied prior to execution of a block
|
// State overrides are applied prior to execution of a block
|
||||||
if err := block.StateOverrides.Apply(state); err != nil {
|
if err := block.StateOverrides.ApplyMulticall(state, precompiles); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// ECRecover replacement code will be fetched from statedb and executed as a normal EVM bytecode.
|
// ECRecover replacement code will be fetched from statedb and executed as a normal EVM bytecode.
|
||||||
|
|
@ -1211,7 +1292,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
|
||||||
if opts.TraceTransfers {
|
if opts.TraceTransfers {
|
||||||
vmConfig.Tracer = newTracer()
|
vmConfig.Tracer = newTracer()
|
||||||
}
|
}
|
||||||
result, err := doCall(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig)
|
result, err := doCall(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results[bi].Calls[i] = callResult{Error: err.Error(), Status: hexutil.Uint64(types.ReceiptStatusFailed)}
|
results[bi].Calls[i] = callResult{Error: err.Error(), Status: hexutil.Uint64(types.ReceiptStatusFailed)}
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -1018,8 +1018,7 @@ func TestMulticallV1(t *testing.T) {
|
||||||
// }
|
// }
|
||||||
Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"),
|
Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"),
|
||||||
},
|
},
|
||||||
},
|
common.BytesToAddress([]byte{0x01}): OverrideAccount{
|
||||||
BlockOverrides: &BlockOverrides{},
|
|
||||||
// Yul code that returns the address of the caller.
|
// Yul code that returns the address of the caller.
|
||||||
// object "Test" {
|
// object "Test" {
|
||||||
// code {
|
// code {
|
||||||
|
|
@ -1028,7 +1027,10 @@ func TestMulticallV1(t *testing.T) {
|
||||||
// return(0xc, 0x14)
|
// return(0xc, 0x14)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
ECRecoverOverride: hex2Bytes("33806000526014600cf3"),
|
Code: hex2Bytes("33806000526014600cf3"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BlockOverrides: &BlockOverrides{},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
From: &randomAccounts[0].addr,
|
From: &randomAccounts[0].addr,
|
||||||
To: &randomAccounts[2].addr,
|
To: &randomAccounts[2].addr,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue