Fix eth_estimateGas and simulated txs (#142)

This commit is contained in:
Austin Abell 2019-11-04 11:59:16 -05:00 committed by GitHub
parent 3ad2bb1de1
commit 42227a1b8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 26 additions and 18 deletions

View File

@ -374,7 +374,10 @@ type account struct {
// DoCall performs a simulated call operation through the evm // DoCall performs a simulated call operation through the evm
func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (sdk.Result, error) { func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (sdk.Result, error) {
// Set height for historical queries // Set height for historical queries
ctx := e.cliCtx.WithHeight(blockNr.Int64()) ctx := e.cliCtx
if blockNr.Int64() != 0 {
ctx = e.cliCtx.WithHeight(blockNr.Int64())
}
// Set sender address or use a default if none specified // Set sender address or use a default if none specified
var addr common.Address var addr common.Address
@ -452,13 +455,14 @@ func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasC
} }
// EstimateGas estimates gas usage for the given smart contract call. // EstimateGas estimates gas usage for the given smart contract call.
func (e *PublicEthAPI) EstimateGas(args CallArgs, blockNr rpc.BlockNumber) (hexutil.Uint64, error) { func (e *PublicEthAPI) EstimateGas(args CallArgs) (hexutil.Uint64, error) {
result, err := e.doCall(args, blockNr, big.NewInt(emint.DefaultRPCGasLimit)) result, err := e.doCall(args, 0, big.NewInt(emint.DefaultRPCGasLimit))
if err != nil { if err != nil {
return 0, err return 0, err
} }
return hexutil.Uint64(result.GasUsed), nil // TODO: change 1000 buffer for more accurate buffer (must be at least 1 to not run OOG)
return hexutil.Uint64(result.GasUsed + 1000), nil
} }
// GetBlockByHash returns the block identified by hash. // GetBlockByHash returns the block identified by hash.
@ -916,7 +920,7 @@ func (e *PublicEthAPI) GenerateFromArgs(args params.SendTxArgs) (msg *types.Ethe
Value: args.Value, Value: args.Value,
Data: args.Data, Data: args.Data,
} }
g, _ := e.EstimateGas(callArgs, rpc.BlockNumber(e.cliCtx.Height)) g, _ := e.EstimateGas(callArgs)
gasLimit = uint64(g) gasLimit = uint64(g)
} else { } else {
gasLimit = (uint64)(*args.Gas) gasLimit = (uint64)(*args.Gas)

View File

@ -44,13 +44,19 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
// This gas limit the the transaction gas limit with intrinsic gas subtracted // This gas limit the the transaction gas limit with intrinsic gas subtracted
gasLimit := st.GasLimit - ctx.GasMeter().GasConsumed() gasLimit := st.GasLimit - ctx.GasMeter().GasConsumed()
var snapshot int csdb := st.Csdb
if st.Simulate { if st.Simulate {
// gasLimit is set here because stdTxs incur gaskv charges in the ante handler, but for eth_call // gasLimit is set here because stdTxs incur gaskv charges in the ante handler, but for eth_call
// the cost needs to be the same as an Ethereum transaction sent through the web3 API // the cost needs to be the same as an Ethereum transaction sent through the web3 API
consumedGas := ctx.GasMeter().GasConsumed()
gasLimit = st.GasLimit - cost gasLimit = st.GasLimit - cost
if consumedGas < cost {
// If Cosmos standard tx ante handler cost is less than EVM intrinsic cost
// gas must be consumed to match to accurately simulate an Ethereum transaction
ctx.GasMeter().ConsumeGas(cost-consumedGas, "Intrinsic gas match")
}
snapshot = st.Csdb.Snapshot() csdb = st.Csdb.Copy()
} }
// Create context for evm // Create context for evm
@ -70,7 +76,7 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
evmGasMeter := sdk.NewInfiniteGasMeter() evmGasMeter := sdk.NewInfiniteGasMeter()
vmenv := vm.NewEVM( vmenv := vm.NewEVM(
context, st.Csdb.WithContext(ctx.WithGasMeter(evmGasMeter)), context, csdb.WithContext(ctx.WithGasMeter(evmGasMeter)),
GenerateChainConfig(st.ChainID), vm.Config{}, GenerateChainConfig(st.ChainID), vm.Config{},
) )
@ -86,15 +92,15 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
ret, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount) ret, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount)
} else { } else {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
st.Csdb.SetNonce(st.Sender, st.Csdb.GetNonce(st.Sender)+1) csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1)
ret, leftOverGas, vmerr = vmenv.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount) ret, leftOverGas, vmerr = vmenv.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
} }
// Generate bloom filter to be saved in tx receipt data // Generate bloom filter to be saved in tx receipt data
bloomInt := big.NewInt(0) bloomInt := big.NewInt(0)
var bloomFilter ethtypes.Bloom var bloomFilter ethtypes.Bloom
if st.THash != nil { if st.THash != nil && !st.Simulate {
logs := st.Csdb.GetLogs(*st.THash) logs := csdb.GetLogs(*st.THash)
bloomInt = ethtypes.LogsBloom(logs) bloomInt = ethtypes.LogsBloom(logs)
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes()) bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
} }
@ -114,13 +120,11 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
return nil, res return nil, res
} }
if st.Simulate {
st.Csdb.RevertToSnapshot(snapshot)
}
// TODO: Refund unused gas here, if intended in future // TODO: Refund unused gas here, if intended in future
if !st.Simulate {
st.Csdb.Finalise(true) // Change to depend on config // Finalise state if not a simulated transaction
st.Csdb.Finalise(true) // Change to depend on config
}
// Consume gas from evm execution // Consume gas from evm execution
// Out of gas check does not need to be done here since it is done within the EVM execution // Out of gas check does not need to be done here since it is done within the EVM execution

View File

@ -571,7 +571,7 @@ func (csdb *CommitStateDB) CreateAccount(addr ethcmn.Address) {
// Copy creates a deep, independent copy of the state. // Copy creates a deep, independent copy of the state.
// //
// NOTE: Snapshots of the copied state cannot be applied to the copy. // NOTE: Snapshots of the copied state cannot be applied to the copy.
func (csdb *CommitStateDB) Copy() ethvm.StateDB { func (csdb *CommitStateDB) Copy() *CommitStateDB {
csdb.lock.Lock() csdb.lock.Lock()
defer csdb.lock.Unlock() defer csdb.lock.Unlock()