mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-11 21:04:11 +00:00
Fixes for Call gas usage, Calls use precompiles, pass down their opCode
This commit is contained in:
parent
e2087f0922
commit
36270ff4d5
@ -11,7 +11,7 @@ import
|
||||
../constants, ../errors, ../validation, ../vm_state, ../vm_types,
|
||||
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
||||
./code_stream, ./memory, ./message, ./stack, ../db/[state_db, db_chain],
|
||||
../utils/header, byteutils, ranges
|
||||
../utils/header, byteutils, ranges, eth_keys, precompiles
|
||||
|
||||
logScope:
|
||||
topics = "vm computation"
|
||||
@ -85,7 +85,7 @@ proc prepareChildMessage*(
|
||||
code,
|
||||
childOptions)
|
||||
|
||||
proc applyMessage(computation: var BaseComputation) =
|
||||
proc applyMessage(computation: var BaseComputation, opCode: static[Op]) =
|
||||
var transaction = computation.vmState.beginTransaction()
|
||||
defer: transaction.dispose()
|
||||
|
||||
@ -97,28 +97,66 @@ proc applyMessage(computation: var BaseComputation) =
|
||||
computation.vmState.chainDb.getStateDb(
|
||||
computation.vmState.blockHeader.hash, false).
|
||||
getBalance(computation.msg.sender)
|
||||
var newBalance = senderBalance
|
||||
|
||||
if sender_balance < computation.msg.value:
|
||||
raise newException(InsufficientFunds,
|
||||
&"Insufficient funds: {senderBalance} < {computation.msg.value}"
|
||||
)
|
||||
when opCode in {Call, CallCode}:
|
||||
let
|
||||
insufficientFunds = senderBalance < computation.msg.value
|
||||
stackTooDeep = computation.msg.depth >= MaxCallDepth
|
||||
|
||||
computation.vmState.mutateStateDb:
|
||||
db.setBalance(computation.msg.sender, db.getBalance(computation.msg.sender) - computation.msg.value)
|
||||
db.addBalance(computation.msg.storage_address, computation.msg.value)
|
||||
if insufficientFunds or stackTooDeep:
|
||||
computation.returnData = @[]
|
||||
var errMessage: string
|
||||
if insufficientFunds:
|
||||
errMessage = &"Insufficient Funds: have: {$senderBalance} need: {$computation.msg.value}"
|
||||
elif stackTooDeep:
|
||||
errMessage = "Stack Limit Reached"
|
||||
else:
|
||||
raise newException(VMError, "Invariant: Unreachable code path")
|
||||
|
||||
debug "Computation failure", msg = errMessage
|
||||
computation.gasMeter.returnGas(computation.msg.gas)
|
||||
push: 0
|
||||
return
|
||||
|
||||
newBalance = senderBalance - computation.msg.value
|
||||
computation.vmState.mutateStateDb:
|
||||
db.setBalance(computation.msg.sender, newBalance)
|
||||
db.addBalance(computation.msg.storage_address, computation.msg.value)
|
||||
|
||||
debug "Value transferred",
|
||||
source = computation.msg.sender,
|
||||
dest = computation.msg.storage_address,
|
||||
value = computation.msg.value,
|
||||
oldSenderBalance = senderBalance,
|
||||
newSenderBalance = newBalance,
|
||||
gasPrice = computation.msg.gasPrice,
|
||||
gas = computation.msg.gas
|
||||
|
||||
debug "Apply message",
|
||||
value = computation.msg.value,
|
||||
senderBalance = newBalance,
|
||||
sender = computation.msg.sender.toHex,
|
||||
address = computation.msg.storage_address.toHex
|
||||
address = computation.msg.storage_address.toHex,
|
||||
gasPrice = computation.msg.gasPrice,
|
||||
gas = computation.msg.gas
|
||||
|
||||
computation.opcodeExec(computation)
|
||||
# Run code
|
||||
if not computation.execPrecompiles:
|
||||
computation.opcodeExec(computation)
|
||||
|
||||
if not computation.isError:
|
||||
debug "Computation committed"
|
||||
transaction.commit()
|
||||
else:
|
||||
debug "Computation rolled back due to error"
|
||||
|
||||
proc applyCreateMessage(fork: Fork, computation: var BaseComputation) =
|
||||
computation.applyMessage()
|
||||
proc applyCreateMessage(fork: Fork, computation: var BaseComputation, opCode: static[Op]) =
|
||||
computation.applyMessage(opCode)
|
||||
|
||||
var transaction: DbTransaction
|
||||
defer: transaction.safeDispose()
|
||||
@ -157,13 +195,13 @@ proc applyCreateMessage(fork: Fork, computation: var BaseComputation) =
|
||||
else:
|
||||
# Different from Frontier:
|
||||
# Reverts state on gas failure while writing contract code.
|
||||
# TODO: Revert snapshot
|
||||
# Transaction are reverted automatically by safeDispose.
|
||||
discard
|
||||
else:
|
||||
if transaction != nil:
|
||||
transaction.commit()
|
||||
|
||||
proc generateChildComputation*(fork: Fork, computation: BaseComputation, childMsg: Message): BaseComputation =
|
||||
proc generateChildComputation*(fork: Fork, computation: BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
|
||||
var childComp = newBaseComputation(
|
||||
computation.vmState,
|
||||
computation.vmState.blockHeader.blockNumber,
|
||||
@ -171,11 +209,11 @@ proc generateChildComputation*(fork: Fork, computation: BaseComputation, childMs
|
||||
|
||||
# Copy the fork op code executor proc (assumes child computation is in the same fork)
|
||||
childComp.opCodeExec = computation.opCodeExec
|
||||
|
||||
|
||||
if childMsg.isCreate:
|
||||
fork.applyCreateMessage(childComp)
|
||||
fork.applyCreateMessage(childComp, opCode)
|
||||
else:
|
||||
applyMessage(childComp)
|
||||
applyMessage(childComp, opCode)
|
||||
return childComp
|
||||
|
||||
proc addChildComputation(fork: Fork, computation: BaseComputation, child: BaseComputation) =
|
||||
@ -193,10 +231,10 @@ proc addChildComputation(fork: Fork, computation: BaseComputation, child: BaseCo
|
||||
computation.returnData = child.output
|
||||
computation.children.add(child)
|
||||
|
||||
proc applyChildComputation*(computation: BaseComputation, childMsg: Message): BaseComputation =
|
||||
proc applyChildComputation*(computation: BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
|
||||
## Apply the vm message childMsg as a child computation.
|
||||
let fork = computation.vmState.blockHeader.blockNumber.toFork
|
||||
result = fork.generateChildComputation(computation, childMsg)
|
||||
result = fork.generateChildComputation(computation, childMsg, opCode)
|
||||
fork.addChildComputation(computation, result)
|
||||
|
||||
proc registerAccountForDeletion*(c: var BaseComputation, beneficiary: EthAddress) =
|
||||
|
@ -14,6 +14,9 @@ import
|
||||
../../vm_state, ../../errors, ../../constants, ../../vm_types,
|
||||
../../db/[db_chain, state_db], ../../utils/addresses
|
||||
|
||||
logScope:
|
||||
topics = "opcode impl"
|
||||
|
||||
# ##################################
|
||||
# Syntactic sugar
|
||||
|
||||
@ -545,6 +548,18 @@ op create, inline = false, value, startPosition, size:
|
||||
contractAddress = generateAddress(computation.msg.storageAddress, creationNonce)
|
||||
isCollision = db.hasCodeOrNonce(contractAddress)
|
||||
|
||||
|
||||
let (gasCost, childMsgGas) = computation.gasCosts[Op.Create].c_handler(
|
||||
value,
|
||||
GasParams(kind: Call,
|
||||
c_isNewAccount: true, # TODO stub
|
||||
c_gasBalance: 0,
|
||||
c_contractGas: 0,
|
||||
c_currentMemSize: computation.memory.len,
|
||||
c_memOffset: 0, # TODO make sure if we pass the largest mem requested
|
||||
c_memLength: 0 # or an addition of mem requested
|
||||
))
|
||||
|
||||
if isCollision:
|
||||
debug("Address collision while creating contract", address = contractAddress.toHex)
|
||||
push: 0
|
||||
@ -552,7 +567,7 @@ op create, inline = false, value, startPosition, size:
|
||||
|
||||
let childMsg = prepareChildMessage(
|
||||
computation,
|
||||
gas = 0, # TODO refactor gas
|
||||
gas = childMsgGas,
|
||||
to = CREATE_CONTRACT_ADDRESS,
|
||||
value = value,
|
||||
data = @[],
|
||||
@ -560,10 +575,7 @@ op create, inline = false, value, startPosition, size:
|
||||
options = MessageOptions(createAddress: contractAddress)
|
||||
)
|
||||
|
||||
# let childComputation = applyChildBaseComputation(computation, childMsg)
|
||||
var childComputation: BaseComputation # TODO - stub
|
||||
new childComputation
|
||||
childComputation.gasMeter.init(0)
|
||||
let childComputation = computation.applyChildComputation(childMsg, Create)
|
||||
|
||||
if childComputation.isError:
|
||||
push: 0
|
||||
@ -652,13 +664,12 @@ proc staticCallParams(computation: var BaseComputation): (UInt256, UInt256, EthA
|
||||
memoryOutputSize,
|
||||
emvcStatic) # is_static
|
||||
|
||||
template genCall(callName: untyped): untyped =
|
||||
template genCall(callName: untyped, opCode: Op): untyped =
|
||||
op callName, inline = false:
|
||||
## CALL, 0xf1, Message-Call into an account
|
||||
## CALLCODE, 0xf2, Message-call into this account with an alternative account's code.
|
||||
## DELEGATECALL, 0xf4, Message-call into this account with an alternative account's code, but persisting the current values for sender and value.
|
||||
## STATICCALL, 0xfa, Static message-call into an account.
|
||||
# TODO: forked calls for Homestead
|
||||
|
||||
let (gas, value, to, sender,
|
||||
codeAddress,
|
||||
@ -668,7 +679,7 @@ template genCall(callName: untyped): untyped =
|
||||
|
||||
let (memInPos, memInLen, memOutPos, memOutLen) = (memoryInputStartPosition.cleanMemRef, memoryInputSize.cleanMemRef, memoryOutputStartPosition.cleanMemRef, memoryOutputSize.cleanMemRef)
|
||||
|
||||
let (gasCost, childMsgGas) = computation.gasCosts[Op.Call].c_handler(
|
||||
let (gasCost, childMsgGas) = computation.gasCosts[opCode].c_handler(
|
||||
value,
|
||||
GasParams(kind: Call,
|
||||
c_isNewAccount: true, # TODO stub
|
||||
@ -678,41 +689,19 @@ template genCall(callName: untyped): untyped =
|
||||
c_memOffset: 0, # TODO make sure if we pass the largest mem requested
|
||||
c_memLength: 0 # or an addition of mem requested
|
||||
))
|
||||
debug "Call", gasCost = gasCost, childCost = childMsgGas
|
||||
computation.gasMeter.consumeGas(gasCost, reason = $opCode)
|
||||
|
||||
computation.memory.extend(memInPos, memInLen)
|
||||
computation.memory.extend(memOutPos, memOutLen)
|
||||
|
||||
let
|
||||
callData = computation.memory.read(memInPos, memInLen)
|
||||
senderBalance = computation.vmState.readOnlyStateDb.getBalance(computation.msg.storageAddress)
|
||||
# TODO check gas balance rollover
|
||||
# TODO: shouldTransferValue in py-evm is:
|
||||
# True for call and callCode
|
||||
# False for callDelegate and callStatic
|
||||
insufficientFunds = senderBalance < value # TODO: and shouldTransferValue
|
||||
stackTooDeep = computation.msg.depth >= MaxCallDepth
|
||||
|
||||
if insufficientFunds or stackTooDeep:
|
||||
computation.returnData = @[]
|
||||
var errMessage: string
|
||||
if insufficientFunds:
|
||||
# Note: for some reason we can't use strformat here, we get undeclared identifiers
|
||||
errMessage = &"Insufficient Funds: have: " & $senderBalance & "need: " & $value
|
||||
elif stackTooDeep:
|
||||
errMessage = "Stack Limit Reached"
|
||||
else:
|
||||
raise newException(VMError, "Invariant: Unreachable code path")
|
||||
|
||||
# computation.logger.debug(&"failure: {errMessage}") # TODO: Error: expression 'logger' has no type (or is ambiguous)
|
||||
computation.gasMeter.returnGas(childMsgGas)
|
||||
push: 0
|
||||
return
|
||||
|
||||
let code =
|
||||
if codeAddress != ZERO_ADDRESS:
|
||||
computation.vmState.readOnlyStateDb.getCode(codeAddress)
|
||||
else:
|
||||
computation.vmState.readOnlyStateDb.getCode(to)
|
||||
code =
|
||||
if codeAddress != ZERO_ADDRESS:
|
||||
computation.vmState.readOnlyStateDb.getCode(codeAddress)
|
||||
else:
|
||||
computation.vmState.readOnlyStateDb.getCode(to)
|
||||
|
||||
var childMsg = prepareChildMessage(
|
||||
computation,
|
||||
@ -727,13 +716,13 @@ template genCall(callName: untyped): untyped =
|
||||
if sender != ZERO_ADDRESS:
|
||||
childMsg.sender = sender
|
||||
|
||||
var childComputation = applyChildComputation(computation, childMsg)
|
||||
var childComputation = applyChildComputation(computation, childMsg, opCode)
|
||||
|
||||
if childComputation.isError:
|
||||
push: 0
|
||||
else:
|
||||
push: 1
|
||||
|
||||
|
||||
if not childComputation.shouldEraseReturnData:
|
||||
let actualOutputSize = min(memOutLen, childComputation.output.len)
|
||||
computation.memory.write(
|
||||
@ -742,10 +731,10 @@ template genCall(callName: untyped): untyped =
|
||||
if not childComputation.shouldBurnGas:
|
||||
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
||||
|
||||
genCall(call)
|
||||
genCall(callCode)
|
||||
genCall(delegateCall)
|
||||
genCall(staticCall)
|
||||
genCall(call, Call)
|
||||
genCall(callCode, CallCode)
|
||||
genCall(delegateCall, DelegateCall)
|
||||
genCall(staticCall, StaticCall)
|
||||
|
||||
op returnOp, inline = false, startPos, size:
|
||||
## 0xf3, Halt execution returning output data.
|
||||
|
Loading…
x
Reference in New Issue
Block a user