EVM write contract transformation

This commit is contained in:
andri lim 2019-03-12 08:41:51 +07:00 committed by zah
parent 5f1879c709
commit 9f8191d408
2 changed files with 44 additions and 52 deletions

View File

@ -151,7 +151,7 @@ proc applyMessageAux(computation: var BaseComputation, opCode: static[Op]) =
computation.vmState.mutateStateDb:
db.addBalance(computation.msg.storageAddress, computation.msg.value)
proc applyMessage(computation: var BaseComputation, opCode: static[Op]) =
proc applyMessage(computation: var BaseComputation, opCode: static[Op]): bool =
var snapshot = computation.snapshot()
defer: snapshot.dispose()
@ -185,46 +185,31 @@ proc applyMessage(computation: var BaseComputation, opCode: static[Op]) =
msg = computation.error.info,
depth = computation.msg.depth
proc applyCreateMessage(fork: Fork, computation: var BaseComputation, opCode: static[Op]) =
computation.applyMessage(opCode)
if computation.isError: return
result = not computation.isError
proc writeContract(fork: Fork, computation: var BaseComputation, opCode: static[Op]): bool =
result = true
let contractCode = computation.output
if contractCode.len == 0: return
var snapshot = computation.snapshot()
defer: snapshot.dispose()
if fork >= FkSpurious and contractCode.len >= EIP170_CODE_SIZE_LIMIT:
raise newException(OutOfGas, &"Contract code size exceeds EIP170 limit of {EIP170_CODE_SIZE_LIMIT}. Got code of size: {contractCode.len}")
debug "Contract code size exceeds EIP170", limit=EIP170_CODE_SIZE_LIMIT, actual=contractCode.len
return false
try:
let storageAddr = computation.msg.storageAddress
if not computation.isSuicided(storageAddr):
# tricky gasCost: 1,0,0 -> createCost. 0,0,x -> depositCost
let gasCost = computation.gasCosts[Create].m_handler(0, 0, contractCode.len)
computation.gasMeter.consumeGas(gasCost,
reason = "Write contract code for CREATE")
let storageAddr = computation.msg.storageAddress
if computation.isSuicided(storageAddr): return
trace "SETTING CODE",
address = storageAddr.toHex,
length = len(contract_code),
hash = contractCode.rlpHash
computation.vmState.mutateStateDb:
db.setCode(storageAddr, contractCode.toRange)
snapshot.commit()
except OutOfGas:
debug "applyCreateMessage failed: ",
msg = getCurrentExceptionMsg(),
depth = computation.msg.depth
if fork < FkHomestead:
computation.output = @[]
else:
# Different from Frontier:
# Reverts state on gas failure while writing contract code.
snapshot.revert()
# tricky gasCost: 1,0,0 -> createCost. 0,0,x -> depositCost
let codeCost = computation.gasCosts[Create].m_handler(0, 0, contractCode.len)
if computation.gasMeter.gasRemaining >= codeCost:
computation.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE")
computation.vmState.mutateStateDb:
db.setCode(storageAddr, contractCode.toRange)
result = true
else:
if fork < FkHomestead: computation.output = @[]
result = false
proc generateChildComputation*(fork: Fork, computation: var BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
var childComp = newBaseComputation(
@ -236,10 +221,19 @@ proc generateChildComputation*(fork: Fork, computation: var BaseComputation, chi
# 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, opCode)
var snapshot = computation.snapshot()
defer: snapshot.dispose()
var contractOK = true
if applyMessage(childComp, opCode):
if childMsg.isCreate:
contractOK = fork.writeContract(childComp, opCode)
if not contractOK and fork == FkHomestead:
# consume all gas
snapshot.revert(true)
else:
applyMessage(childComp, opCode)
snapshot.commit()
return childComp

View File

@ -100,22 +100,20 @@ proc refundGas*(computation: BaseComputation, tx: Transaction, sender: EthAddres
result = gasUsed - gasRefund
proc writeContract*(computation: var BaseComputation): bool =
let codeCost = computation.gasCosts[Create].m_handler(0, 0, computation.output.len)
let contractAddress = computation.msg.storageAddress
result = true
if not computation.isSuicided(contractAddress):
# make changes only if it not selfdestructed
if computation.gasMeter.gasRemaining >= codeCost:
computation.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE")
computation.vmState.mutateStateDB:
db.setCode(contractAddress, computation.output.toRange)
result = true
else:
# XXX: Homestead behaves differently; reverts state on gas failure
# https://github.com/ethereum/py-evm/blob/master/eth/vm/forks/homestead/computation.py
computation.vmState.mutateStateDB:
db.setCode(contractAddress, ByteRange())
result = false
let contractAddress = computation.msg.storageAddress
if computation.isSuicided(contractAddress): return
let codeCost = computation.gasCosts[Create].m_handler(0, 0, computation.output.len)
if computation.gasMeter.gasRemaining >= codeCost:
computation.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE")
computation.vmState.mutateStateDB:
db.setCode(contractAddress, computation.output.toRange)
result = true
else:
computation.vmState.mutateStateDB:
db.setCode(contractAddress, ByteRange())
result = false
#[
method executeTransaction(vmState: BaseVMState, transaction: Transaction): (BaseComputation, BlockHeader) {.base.}=