moveTo only for precompiles

This commit is contained in:
Sina Mahmoodi 2023-09-11 19:51:53 +02:00
parent 8e026758fe
commit a0d2706907
3 changed files with 28 additions and 88 deletions

View file

@ -902,10 +902,12 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
// Apply the customization rules if required. // Apply the customization rules if required.
if config != nil { if config != nil {
if err := config.StateOverrides.Apply(statedb); err != nil { config.BlockOverrides.Apply(&vmctx)
rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time)
precompiles := vm.ActivePrecompiledContracts(rules)
if err := config.StateOverrides.Apply(statedb, precompiles); err != nil {
return nil, err return nil, err
} }
config.BlockOverrides.Apply(&vmctx)
} }
// Execute the trace // Execute the trace
msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee(), true) msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee(), true)

View file

@ -868,100 +868,35 @@ func (s *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address
// if statDiff is set, all diff will be applied first and then execute the call // if statDiff is set, all diff will be applied first and then execute the call
// message. // message.
type OverrideAccount struct { type OverrideAccount struct {
Nonce *hexutil.Uint64 `json:"nonce"` Nonce *hexutil.Uint64 `json:"nonce"`
Code *hexutil.Bytes `json:"code"` Code *hexutil.Bytes `json:"code"`
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:"moveToAddress"` MovePrecompileTo *common.Address `json:"movePrecompileToAddress"`
} }
// StateOverride is the collection of overridden accounts. // StateOverride is the collection of overridden accounts.
type StateOverride map[common.Address]OverrideAccount type StateOverride map[common.Address]OverrideAccount
// Apply overrides the fields of specified accounts into the given state. // Apply overrides the fields of specified accounts into the given state.
func (diff *StateOverride) Apply(state *state.StateDB) error { func (diff *StateOverride) Apply(state *state.StateDB, precompiles vm.PrecompiledContracts) error {
if diff == nil {
return nil
}
for addr, account := range *diff {
// 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
}
// ApplyMulticall overrides the fields of specified accounts into the given state.
// TODO: consider MoveTo by address mapping
func (diff *StateOverride) ApplyMulticall(state *state.StateDB, precompiles vm.PrecompiledContracts) error {
if diff == nil { if diff == nil {
return nil return nil
} }
for addr, account := range *diff { for addr, account := range *diff {
p, isPrecompile := precompiles[addr] p, isPrecompile := precompiles[addr]
// The MoveTo feature makes it possible to replace precompiles and EVM // The MoveTo feature makes it possible to move a precompile
// contracts in all the following configurations: // code to another address. If the target address is another precompile
// 1. Precompile -> Precompile // the code for the latter is lost for this session.
// 2. Precompile -> EVM contract // Note the destination account is not cleared upon move.
// 3. EVM contract -> Precompile if account.MovePrecompileTo != nil {
// 4. EVM contract -> EVM contract if !isPrecompile {
if account.MoveTo != nil { return fmt.Errorf("account %s is not a precompile", addr.Hex())
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))
// Copy storage over
// TODO: Use snaps
state.ForEachStorage(addr, func(key, value common.Hash) bool {
state.SetState(*account.MoveTo, key, value)
return true
})
// Clear source storage
state.SetStorage(addr, map[common.Hash]common.Hash{})
if precompiles[*account.MoveTo] != nil {
delete(precompiles, *account.MoveTo)
}
} }
precompiles[*account.MovePrecompileTo] = p
} }
if isPrecompile { if isPrecompile {
// Now that the contract is moved it can be deleted.
delete(precompiles, addr) delete(precompiles, addr)
} }
// Override account nonce. // Override account nonce.
@ -1075,13 +1010,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
if state == nil || err != nil { if state == nil || err != nil {
return nil, err return nil, err
} }
if err := overrides.Apply(state); err != nil {
return nil, err
}
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
if blockOverrides != nil { if blockOverrides != nil {
blockOverrides.Apply(&blockCtx) blockOverrides.Apply(&blockCtx)
} }
rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time)
precompiles := vm.ActivePrecompiledContracts(rules).Copy()
if err := overrides.Apply(state, precompiles); err != nil {
return nil, err
}
// Setup context so it may be cancelled the call has completed // Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout. // or, in case of unmetered gas, setup a context with a timeout.
var cancel context.CancelFunc var cancel context.CancelFunc
@ -1264,7 +1202,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo
blockContext = blockContexts[bi] blockContext = blockContexts[bi]
hash := crypto.Keccak256Hash(blockContext.BlockNumber.Bytes()) hash := crypto.Keccak256Hash(blockContext.BlockNumber.Bytes())
// State overrides are applied prior to execution of a block // State overrides are applied prior to execution of a block
if err := block.StateOverrides.ApplyMulticall(state, precompiles); err != nil { if err := block.StateOverrides.Apply(state, precompiles); err != nil {
return nil, err return nil, err
} }
results[bi] = blockResult{ results[bi] = blockResult{

View file

@ -1114,8 +1114,8 @@ func TestMulticallV1(t *testing.T) {
// return(memPtr, size) // return(memPtr, size)
// } // }
// } // }
Code: hex2Bytes("365981600082378181f3"), Code: hex2Bytes("365981600082378181f3"),
MoveTo: &randomAccounts[2].addr, MovePrecompileTo: &randomAccounts[2].addr,
}, },
}, },
Calls: []TransactionArgs{{ Calls: []TransactionArgs{{