Transaction: `runComputation` options for non-standard EVM behaviour

The following four flags are added, to change various steps in EVM processing
when a call doesn't come from a real transaction:

- `noIntrinsic`:  Don't charge intrinsic gas.
- `noAccessList`: Don't initialise EIP2929 access list.
- `noGasCharge`:  Don't charge sender account for gas.
- `noRefund`:     Don't apply gas refund/burn rule.

This is to support RPC and GraphQL `call` operations, which behave differently
in some ways from regular transaction calls, and to support some test suites.

In EVMC terms, all these alterations can be performed on the host side.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
This commit is contained in:
Jamie Lokier 2021-05-17 15:16:44 +01:00
parent 12bf0fd346
commit 306c8e92c2
No known key found for this signature in database
GPG Key ID: CBC25C68435C30A2
1 changed files with 14 additions and 6 deletions

View File

@ -27,6 +27,10 @@ type
value*: HostValue # Value sent from sender to recipient. value*: HostValue # Value sent from sender to recipient.
input*: seq[byte] # Input data. input*: seq[byte] # Input data.
accessList*: AccessList # EIP-2930 (Berlin) tx access list. accessList*: AccessList # EIP-2930 (Berlin) tx access list.
noIntrinsic*: bool # Don't charge intrinsic gas.
noAccessList*: bool # Don't initialise EIP-2929 access list.
noGasCharge*: bool # Don't charge sender account for gas.
noRefund*: bool # Don't apply gas refund/burn rule.
# Standard call result. (Some fields are beyond what EVMC can return, # Standard call result. (Some fields are beyond what EVMC can return,
# and must only be used from tests because they will not always be set). # and must only be used from tests because they will not always be set).
@ -114,7 +118,7 @@ proc setupCall(call: CallParams, useIntrinsic: bool): TransactionHost =
) )
var intrinsicGas: GasInt = 0 var intrinsicGas: GasInt = 0
if useIntrinsic: if useIntrinsic and not call.noIntrinsic:
intrinsicGas = intrinsicGas(call, vmState.fork) intrinsicGas = intrinsicGas(call, vmState.fork)
let host = TransactionHost( let host = TransactionHost(
@ -150,9 +154,11 @@ proc runComputation*(call: CallParams): CallResult =
let c = host.computation let c = host.computation
# Must come after `setupCall` for correct fork. # Must come after `setupCall` for correct fork.
if not call.noAccessList:
initialAccessListEIP2929(call) initialAccessListEIP2929(call)
# Charge for gas. # Charge for gas.
if not call.noGasCharge:
host.vmState.mutateStateDB: host.vmState.mutateStateDB:
db.subBalance(call.sender, call.gasLimit.u256 * call.gasPrice.u256) db.subBalance(call.sender, call.gasLimit.u256 * call.gasPrice.u256)
@ -160,14 +166,16 @@ proc runComputation*(call: CallParams): CallResult =
# Calculated gas used, taking into account refund rules. # Calculated gas used, taking into account refund rules.
var gasRemaining: GasInt = 0 var gasRemaining: GasInt = 0
if not c.shouldBurnGas: if call.noRefund:
gasRemaining = c.gasMeter.gasRemaining
elif not c.shouldBurnGas:
let maxRefund = (call.gasLimit - c.gasMeter.gasRemaining) div 2 let maxRefund = (call.gasLimit - c.gasMeter.gasRemaining) div 2
let refund = min(c.getGasRefund(), maxRefund) let refund = min(c.getGasRefund(), maxRefund)
c.gasMeter.returnGas(refund) c.gasMeter.returnGas(refund)
gasRemaining = c.gasMeter.gasRemaining gasRemaining = c.gasMeter.gasRemaining
# Refund for unused gas. # Refund for unused gas.
if gasRemaining > 0: if gasRemaining > 0 and not call.noGasCharge:
host.vmState.mutateStateDB: host.vmState.mutateStateDB:
db.addBalance(call.sender, gasRemaining.u256 * call.gasPrice.u256) db.addBalance(call.sender, gasRemaining.u256 * call.gasPrice.u256)