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:
Jeffrey Wilcke 2017-01-26 10:02:40 +01:00
parent 8b57c49490
commit ea363998d0
3 changed files with 94 additions and 0 deletions

View file

@ -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 }

View file

@ -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) {

View file

@ -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: