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
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
// 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) {
|
||||
|
|
|
|||
|
|
@ -341,6 +341,12 @@ web3._extend({
|
|||
},
|
||||
params: 2,
|
||||
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:
|
||||
|
|
|
|||
Loading…
Reference in a new issue