mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
core/vm, internal/ethapi: implemented eth_injectCode_
The EVM gained a new method called which allows one to call using the caller as context and runs with the given code as if it were a delegate call. The only difference is that does not take an address to get the code *from*. This functions as code injection tool.
This commit is contained in:
parent
8b57c49490
commit
ea363998d0
3 changed files with 94 additions and 0 deletions
|
|
@ -292,6 +292,37 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||||
return ret, contractAddr, contract.Gas, err
|
return ret, contractAddr, contract.Gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InjectCode executes the given code in the context of caller, i.e. code injection.
|
||||||
|
func (evm *EVM) InjectCall(caller ContractRef, code, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||||
|
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||||
|
return nil, gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
|
// limit.
|
||||||
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
|
return nil, gas, ErrDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
snapshot = evm.StateDB.Snapshot()
|
||||||
|
to = evm.StateDB.GetAccount(caller.Address())
|
||||||
|
)
|
||||||
|
|
||||||
|
// Iinitialise a new contract and make initialise the delegate values
|
||||||
|
contract := NewContract(caller, to, new(big.Int), gas).AsDelegate()
|
||||||
|
contract.SetCallCode(nil, crypto.Sha3Hash(code), code)
|
||||||
|
|
||||||
|
ret, err = evm.interpreter.Run(contract, input)
|
||||||
|
if err != nil {
|
||||||
|
contract.UseGas(contract.Gas)
|
||||||
|
|
||||||
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, contract.Gas, err
|
||||||
|
}
|
||||||
|
|
||||||
// ChainConfig returns the evmironment's chain configuration
|
// ChainConfig returns the evmironment's chain configuration
|
||||||
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -570,6 +570,63 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
||||||
return res, gas, err
|
return res, gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InjectCodeArgs struct {
|
||||||
|
CallArgs
|
||||||
|
Code hexutil.Bytes `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PublicBlockChainAPI) InjectCall(ctx context.Context, args InjectCodeArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||||
|
statedb, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
|
if statedb == nil || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.To == nil {
|
||||||
|
return nil, errors.New("requires 'to' be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sender address or use a default if none specified
|
||||||
|
addr := args.From
|
||||||
|
if addr == (common.Address{}) {
|
||||||
|
accounts := s.b.AccountManager().Accounts()
|
||||||
|
if len(accounts) > 0 {
|
||||||
|
addr = accounts[0].Address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt()
|
||||||
|
// Create new call message
|
||||||
|
msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)
|
||||||
|
|
||||||
|
// Setup context so it may be cancelled the call has completed
|
||||||
|
// or, in case of unmetered gas, setup a context with a timeout.
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
|
||||||
|
// Make sure the context is cancelled when the call has completed
|
||||||
|
// this makes sure resources are cleaned up.
|
||||||
|
defer func() { cancel() }()
|
||||||
|
|
||||||
|
// Get a new instance of the EVM.
|
||||||
|
evm, _, err := s.b.GetEVM(ctx, msg, statedb, header, vm.Config{DisableGasMetering: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
evm.Cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
caller := evm.StateDB.GetAccount(*args.To)
|
||||||
|
if !evm.StateDB.Exist(*args.To) {
|
||||||
|
caller = evm.StateDB.CreateAccount(*args.To)
|
||||||
|
}
|
||||||
|
res, _, err := evm.InjectCall(vm.NewContract(caller, caller, new(big.Int), 0), args.Code, args.Data, gas.Uint64())
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,12 @@ web3._extend({
|
||||||
},
|
},
|
||||||
params: 2,
|
params: 2,
|
||||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.utils.toHex]
|
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.utils.toHex]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'injectCall',
|
||||||
|
call: "eth_injectCall",
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, web3._extend.formatters.inputDefaultBlockNumberFormatter],
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
properties:
|
properties:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue