Transaction: Move contract address generation outside the EVM

The current EVM generates its own new contract addresses, and this is why there
are separate `msg.contractAddress` and `msg.codeAddress` fields in the
computation start message.

In EVMC, account updates are only allowed on the host side, including contract
generation, and the start message has one destination field, `msg.destination`.
The EVM cannot select addresses, only use them.  It's a sensible design.

The difference makes the current EVM incompatible with EVMC and its message
format, so this patch corrects the difference.  It moves contract address
generation to the host side.  This simplifies the EVM and its API a little.

(As an API change, this is incompatible with vm2, so it's guarded under
`evmc_enabled` to allow vm2 to continue to build and run at this time.  This is
also why there are fewer deletions than would otherwise be expected.)

Signed-off-by: Jamie Lokier <jamie@shareable.org>
This commit is contained in:
Jamie Lokier 2021-05-18 14:43:38 +01:00
parent 775231eef1
commit b3a788c7ce
No known key found for this signature in database
GPG Key ID: CBC25C68435C30A2
2 changed files with 56 additions and 9 deletions

View File

@ -9,7 +9,7 @@
import
eth/common/eth_types, stint, options, stew/ranges/ptr_arith,
".."/[vm_types, vm_types2, vm_state, vm_computation, vm_state_transactions],
".."/[db/accounts_cache, vm_precompiles, vm_gas_costs],
".."/[db/accounts_cache, utils, vm_precompiles, vm_gas_costs],
".."/vm_internals,
./host_types
@ -136,15 +136,44 @@ proc setupHost(call: CallParams): TransactionHost =
# All other defaults in `TransactionHost` are fine.
)
if call.input.len > 0:
host.msg.input_size = call.input.len.csize_t
# Must copy the data so the `host.msg.input_data` pointer
# remains valid after the end of `call` lifetime.
host.input = call.input
host.msg.input_data = host.input[0].addr
# Generate new contract address, prepare code, and update message `destination`
# with the contract address. This differs from the previous Nimbus EVM API.
# Guarded under `evmc_enabled` for now so it doesn't break vm2.
when defined(evmc_enabled):
var code: seq[byte]
if call.isCreate:
let sender = call.sender
let contractAddress =
generateAddress(sender, call.vmState.readOnlyStateDB.getNonce(sender))
host.msg.destination = contractAddress.toEvmc
host.msg.input_size = 0
host.msg.input_data = nil
code = call.input
else:
# TODO: Share the underlying data, but only after checking this does not
# cause problems with the database.
code = host.vmState.readOnlyStateDB.getCode(host.msg.destination.fromEvmc)
if call.input.len > 0:
host.msg.input_size = call.input.len.csize_t
# Must copy the data so the `host.msg.input_data` pointer
# remains valid after the end of `call` lifetime.
host.input = call.input
host.msg.input_data = host.input[0].addr
let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, cMsg, code)
else:
if call.input.len > 0:
host.msg.input_size = call.input.len.csize_t
# Must copy the data so the `host.msg.input_data` pointer
# remains valid after the end of `call` lifetime.
host.input = call.input
host.msg.input_data = host.input[0].addr
let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, cMsg)
let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, cMsg)
return host
proc runComputation*(call: CallParams): CallResult =

View File

@ -163,6 +163,24 @@ proc newComputation*(vmState: BaseVMState, message: Message, salt= 0.u256): Comp
cast[evmc_host_context](result)
)
proc newComputation*(vmState: BaseVMState, message: Message, code: seq[byte]): Computation =
new result
result.vmState = vmState
result.msg = message
result.memory = Memory()
result.stack = newStack()
result.returnStack = @[]
result.gasMeter.init(message.gas)
result.touchedAccounts = initHashSet[EthAddress]()
result.selfDestructs = initHashSet[EthAddress]()
result.code = newCodeStream(code)
when evmc_enabled:
result.host.init(
nim_host_get_interface(),
cast[evmc_host_context](result)
)
template gasCosts*(c: Computation): untyped =
c.vmState.gasCosts