WIP call (different call variants depending on fork)
This commit is contained in:
parent
69dccb1520
commit
b9eb74fa26
|
@ -94,7 +94,7 @@ method prepareChildMessage*(
|
||||||
options: MessageOptions = newMessageOptions()): Message =
|
options: MessageOptions = newMessageOptions()): Message =
|
||||||
|
|
||||||
var childOptions = options
|
var childOptions = options
|
||||||
childOptions.depth = c.msg.depth + 1.i256
|
childOptions.depth = c.msg.depth + 1
|
||||||
result = newMessage(
|
result = newMessage(
|
||||||
gas,
|
gas,
|
||||||
c.msg.gasPrice,
|
c.msg.gasPrice,
|
||||||
|
|
|
@ -92,7 +92,7 @@ let
|
||||||
CREATE_CONTRACT_ADDRESS* = cstring""
|
CREATE_CONTRACT_ADDRESS* = cstring""
|
||||||
ZERO_ADDRESS* = repeat(cstring"\x00", 20)
|
ZERO_ADDRESS* = repeat(cstring"\x00", 20)
|
||||||
ZERO_HASH32* = repeat(cstring"\x00", 20)
|
ZERO_HASH32* = repeat(cstring"\x00", 20)
|
||||||
STACKDEPTHLIMIT* = 1024
|
STACK_DEPTH_LIMIT* = 1024
|
||||||
|
|
||||||
GAS_NULL* = 0.i256
|
GAS_NULL* = 0.i256
|
||||||
GAS_ZERO* = 0.i256
|
GAS_ZERO* = 0.i256
|
||||||
|
@ -118,7 +118,7 @@ let
|
||||||
GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.i256
|
GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.i256
|
||||||
GAS_CREATE* = 32_000.i256
|
GAS_CREATE* = 32_000.i256
|
||||||
GAS_CALL* = 40.i256
|
GAS_CALL* = 40.i256
|
||||||
GASCALLVALUE = 9_000.i256
|
GAS_CALL_VALUE* = 9_000.i256
|
||||||
GAS_CALL_STIPEND* = 2_300.i256
|
GAS_CALL_STIPEND* = 2_300.i256
|
||||||
GAS_NEW_ACCOUNT* = 25_000.i256
|
GAS_NEW_ACCOUNT* = 25_000.i256
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,8 @@ type
|
||||||
TypeError* = object of VMError
|
TypeError* = object of VMError
|
||||||
## Error when invalid values are found
|
## Error when invalid values are found
|
||||||
|
|
||||||
|
NotImplementedError* = object of VMError
|
||||||
|
## Not implemented error
|
||||||
|
|
||||||
proc makeVMError*(): VMError =
|
proc makeVMError*(): VMError =
|
||||||
result.burnsGas = true
|
result.burnsGas = true
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,262 @@
|
||||||
|
import
|
||||||
|
strformat,
|
||||||
|
../constants, ../errors, ../computation, ../opcode, ../opcode_values, ../logging,
|
||||||
|
.. / vm / [stack, memory, gas_meter, message],
|
||||||
|
.. / utils / [address, bytes],
|
||||||
|
bigints
|
||||||
|
|
||||||
|
type
|
||||||
|
BaseCall* = ref object of Opcode
|
||||||
|
|
||||||
|
Call* = ref object of BaseCall
|
||||||
|
|
||||||
|
CallCode* = ref object of BaseCall
|
||||||
|
|
||||||
|
DelegateCall* = ref object of BaseCall
|
||||||
|
|
||||||
|
CallEIP150* = ref object of Call
|
||||||
|
|
||||||
|
CallCodeEIP150* = ref object of CallCode
|
||||||
|
|
||||||
|
DelegateCallEIP150* = ref object of DelegateCall
|
||||||
|
|
||||||
|
CallEIP161* = ref object of CallEIP150
|
||||||
|
|
||||||
|
# Byzantium
|
||||||
|
StaticCall* = ref object of CallEIP161
|
||||||
|
|
||||||
|
CallByzantium* = ref object of CallEIP161
|
||||||
|
|
||||||
|
using
|
||||||
|
computation: var BaseComputation
|
||||||
|
|
||||||
|
method msgExtraGas*(call: BaseCall, computation; gas: Int256, to: cstring, value: Int256): Int256 {.base.} =
|
||||||
|
raise newException(NotImplementedError, "Must be implemented by subclasses")
|
||||||
|
|
||||||
|
method msgGas*(call: BaseCall, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) {.base.} =
|
||||||
|
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
||||||
|
let totalFee = gas + extraGas
|
||||||
|
let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0.i256)
|
||||||
|
(childMsgGas, totalFee)
|
||||||
|
|
||||||
|
method callParams*(call: BaseCall, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) {.base.} =
|
||||||
|
raise newException(NotImplementedError, "Must be implemented subclasses")
|
||||||
|
|
||||||
|
method runLogic*(call: BaseCall, computation) =
|
||||||
|
computation.gasMeter.consumeGas(call.gasCost(computation), reason = $call.kind)
|
||||||
|
let (gas, value, to, sender,
|
||||||
|
codeAddress,
|
||||||
|
memoryInputStartPosition, memoryInputSize,
|
||||||
|
memoryOutputStartPosition, memoryOutputSize,
|
||||||
|
shouldTransferValue,
|
||||||
|
isStatic) = call.callParams(computation)
|
||||||
|
|
||||||
|
computation.extendMemory(memoryInputStartPosition, memoryInputSize)
|
||||||
|
computation.extendMemory(memoryOutputStartPosition, memoryOutputSize)
|
||||||
|
|
||||||
|
let callData = computation.memory.read(memoryInputStartPosition, memoryInputSize)
|
||||||
|
let (childMsgGas, childMsgGasFee) = call.msgGas(computation, gas, to, value)
|
||||||
|
computation.gasMeter.consumeGas(childMsgGasFee, reason = $call.kind)
|
||||||
|
|
||||||
|
# TODO: Pre-call checks
|
||||||
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
|
# sender_balance = state_db.get_balance(computation.msg.storage_address)
|
||||||
|
let senderBalance = 0.i256
|
||||||
|
|
||||||
|
let insufficientFunds = shouldTransferValue and senderBalance < value
|
||||||
|
let stackTooDeep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT
|
||||||
|
|
||||||
|
if insufficientFunds or stackTooDeep:
|
||||||
|
computation.returnData = cstring""
|
||||||
|
var errMessage: string
|
||||||
|
if insufficientFunds:
|
||||||
|
errMessage = &"Insufficient Funds: have: {senderBalance} | need: {value}"
|
||||||
|
elif stackTooDeep:
|
||||||
|
errMessage = "Stack Limit Reached"
|
||||||
|
else:
|
||||||
|
raise newException(VMError, "Invariant: Unreachable code path")
|
||||||
|
|
||||||
|
call.logger.debug(&"{call.kind} failure: {errMessage}")
|
||||||
|
computation.gasMeter.returnGas(childMsgGas)
|
||||||
|
computation.stack.push(0.i256)
|
||||||
|
else:
|
||||||
|
# TODO: with
|
||||||
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
|
# if code_address:
|
||||||
|
# code = state_db.get_code(code_address)
|
||||||
|
# else:
|
||||||
|
# code = state_db.get_code(to)
|
||||||
|
let code = cstring""
|
||||||
|
|
||||||
|
let childMsg = computation.prepareChildMessage(
|
||||||
|
childMsgGas,
|
||||||
|
to,
|
||||||
|
value,
|
||||||
|
callData,
|
||||||
|
code,
|
||||||
|
MessageOptions(
|
||||||
|
shouldTransferValue: shouldTransferValue,
|
||||||
|
isStatic: isStatic))
|
||||||
|
if not sender.isNil:
|
||||||
|
childMsg.sender = sender
|
||||||
|
# let childComputation = computation.applyChildComputation(childMsg)
|
||||||
|
# TODO
|
||||||
|
var childComputation: BaseComputation
|
||||||
|
if childComputation.isError:
|
||||||
|
computation.stack.push(0.i256)
|
||||||
|
else:
|
||||||
|
computation.stack.push(1.i256)
|
||||||
|
if not childComputation.shouldEraseReturnData:
|
||||||
|
let actualOutputSize = min(memoryOutputSize, childComputation.output.len.i256)
|
||||||
|
computation.memory.write(
|
||||||
|
memoryOutputStartPosition,
|
||||||
|
actualOutputSize,
|
||||||
|
childComputation.output.toBytes[0 ..< actualOutputSize.getInt])
|
||||||
|
if not childComputation.shouldBurnGas:
|
||||||
|
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
||||||
|
|
||||||
|
method msgExtraGas(call: Call, computation; gas: Int256, to: cstring, value: Int256): Int256 =
|
||||||
|
# TODO: db
|
||||||
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
|
# let accountExists = db.accountExists(to)
|
||||||
|
let accountExists = false
|
||||||
|
|
||||||
|
let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0.i256
|
||||||
|
let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0.i256
|
||||||
|
transferGasFee + createGasFee
|
||||||
|
|
||||||
|
method callParams(call: CallCode, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) =
|
||||||
|
let gas = computation.stack.popInt()
|
||||||
|
let to = cstring(forceBytesToAddress(computation.stack.popBinary))
|
||||||
|
|
||||||
|
let (value,
|
||||||
|
memoryInputStartPosition, memoryInputSize,
|
||||||
|
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(5)
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
value,
|
||||||
|
to,
|
||||||
|
nil, # sender
|
||||||
|
nil, # code_address
|
||||||
|
memoryInputStartPosition,
|
||||||
|
memoryInputSize,
|
||||||
|
memoryOutputStartPosition,
|
||||||
|
memoryOutputSize,
|
||||||
|
true, # should_transfer_value,
|
||||||
|
computation.msg.isStatic)
|
||||||
|
|
||||||
|
method msgExtraGas(call: CallCode, computation; gas: Int256, to: cstring, value: Int256): Int256 =
|
||||||
|
if value != 0: GAS_CALL_VALUE else: 0.i256
|
||||||
|
|
||||||
|
method callParams(call: Call, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) =
|
||||||
|
let gas = computation.stack.popInt()
|
||||||
|
let codeAddress = cstring(forceBytesToAddress(computation.stack.popBinary))
|
||||||
|
|
||||||
|
let (value,
|
||||||
|
memoryInputStartPosition, memoryInputSize,
|
||||||
|
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(5)
|
||||||
|
|
||||||
|
let to = computation.msg.storageAddress
|
||||||
|
let sender = computation.msg.storageAddress
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
value,
|
||||||
|
to,
|
||||||
|
sender,
|
||||||
|
codeAddress,
|
||||||
|
memoryInputStartPosition,
|
||||||
|
memoryInputSize,
|
||||||
|
memoryOutputStartPosition,
|
||||||
|
memoryOutputSize,
|
||||||
|
true, # should_transfer_value,
|
||||||
|
computation.msg.isStatic)
|
||||||
|
|
||||||
|
method msgGas(call: DelegateCall, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) =
|
||||||
|
(gas, gas)
|
||||||
|
|
||||||
|
method msgExtraGas(call: DelegateCall, computation; gas: Int256, to: cstring, value: Int256): Int256 =
|
||||||
|
0.i256
|
||||||
|
|
||||||
|
method callParams(call: DelegateCall, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) =
|
||||||
|
let gas = computation.stack.popInt()
|
||||||
|
let codeAddress = cstring(forceBytesToAddress(computation.stack.popBinary))
|
||||||
|
|
||||||
|
let (memoryInputStartPosition, memoryInputSize,
|
||||||
|
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4)
|
||||||
|
|
||||||
|
let to = computation.msg.storageAddress
|
||||||
|
let sender = computation.msg.storageAddress
|
||||||
|
let value = computation.msg.value
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
value,
|
||||||
|
to,
|
||||||
|
sender,
|
||||||
|
codeAddress,
|
||||||
|
memoryInputStartPosition,
|
||||||
|
memoryInputSize,
|
||||||
|
memoryOutputStartPosition,
|
||||||
|
memoryOutputSize,
|
||||||
|
false, # should_transfer_value,
|
||||||
|
computation.msg.isStatic)
|
||||||
|
|
||||||
|
proc maxChildGasEIP150(gas: Int256): Int256 =
|
||||||
|
gas - gas div 64
|
||||||
|
|
||||||
|
proc computeEIP150MsgGas(computation; gas: Int256, extraGas: Int256, value: Int256, name: string, callStipend: Int256): (Int256, Int256) =
|
||||||
|
if computation.gasMeter.gasRemaining < extraGas:
|
||||||
|
raise newException(OutOfGas, &"Out of gas: Needed {extraGas} - Remaining {computation.gasMeter.gasRemaining} - Reason: {name}")
|
||||||
|
let gas = min(gas, maxChildGasEIP150(computation.gasMeter.gasRemaining - extraGas))
|
||||||
|
let totalFee = gas + extraGas
|
||||||
|
let childMsgGas = gas + (if value != 0: callStipend else: 0.i256)
|
||||||
|
(childMsgGas, totalFee)
|
||||||
|
|
||||||
|
method msgGas(call: CallEIP150, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) =
|
||||||
|
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
||||||
|
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
|
||||||
|
|
||||||
|
method msgGas(call: CallCodeEIP150, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) =
|
||||||
|
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
||||||
|
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
|
||||||
|
|
||||||
|
method msgGas(call: DelegateCallEIP150, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) =
|
||||||
|
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
||||||
|
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0.i256)
|
||||||
|
|
||||||
|
proc msgExtraGas*(call: CallEIP161, computation; gas: Int256, to: cstring, value: Int256): Int256 =
|
||||||
|
# TODO: with
|
||||||
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
|
# account_is_dead = (
|
||||||
|
# not state_db.account_exists(to) or
|
||||||
|
# state_db.account_is_empty(to))
|
||||||
|
let accountIsDead = true
|
||||||
|
|
||||||
|
let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0.i256
|
||||||
|
let createGasFee = if accountIsDead and value != 0: GAS_NEW_ACCOUNT else: 0.i256
|
||||||
|
transferGasFee + createGasFee
|
||||||
|
|
||||||
|
|
||||||
|
method callParams(call: StaticCall, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) =
|
||||||
|
let gas = computation.stack.popInt()
|
||||||
|
let to = cstring(forceBytesToAddress(computation.stack.popBinary))
|
||||||
|
|
||||||
|
let (memoryInputStartPosition, memoryInputSize,
|
||||||
|
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4)
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
0.i256, # value
|
||||||
|
to,
|
||||||
|
nil, # sender
|
||||||
|
nil, # codeAddress
|
||||||
|
memoryInputStartPosition,
|
||||||
|
memoryInputSize,
|
||||||
|
memoryOutputStartPosition,
|
||||||
|
memoryOutputSize,
|
||||||
|
false, # should_transfer_value,
|
||||||
|
true) # is_static
|
||||||
|
|
||||||
|
|
||||||
|
method callParams(call: CallByzantium, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) =
|
||||||
|
result = procCall callParams(call, computation)
|
||||||
|
if computation.msg.isStatic and result[1] != 0:
|
||||||
|
raise newException(WriteProtection, "Cannot modify state while inside of a STATICCALL context")
|
|
@ -2,7 +2,7 @@ import
|
||||||
strformat, strutils, tables, macros,
|
strformat, strutils, tables, macros,
|
||||||
constants, bigints, errors, logging, vm_state,
|
constants, bigints, errors, logging, vm_state,
|
||||||
vm / [gas_meter, stack, code_stream, memory, message, value, gas_costs], db / chain, computation, opcode, opcode_values, utils / [header, address],
|
vm / [gas_meter, stack, code_stream, memory, message, value, gas_costs], db / chain, computation, opcode, opcode_values, utils / [header, address],
|
||||||
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid]
|
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call]
|
||||||
|
|
||||||
var opcodes = initOpcodes:
|
var opcodes = initOpcodes:
|
||||||
# arithmetic
|
# arithmetic
|
||||||
|
@ -97,12 +97,16 @@ var opcodes = initOpcodes:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Op.Create: GAS_CREATE create
|
# call
|
||||||
# Op.Call: 0.i256 callOp
|
opcodes[Op.Call] = Call(kind: Op.Call)
|
||||||
# Op.CallCode: 0.i256 callCodeOp
|
opcodes[Op.CallCode] = CallCode(kind: Op.CallCode)
|
||||||
# Op.Return: 0.i256 returnOp
|
opcodes[Op.DelegateCall] = DelegateCall(kind: Op.DelegateCall)
|
||||||
# Op.DelegateCall: 0.i256 delegateCall
|
|
||||||
# Op.SelfDestruct: GAS_SELF_DESTRUCT_COST selfDestruct
|
|
||||||
|
# system
|
||||||
|
# Op.Create: GAS_CREATE create
|
||||||
|
# Op.Return: 0.i256 returnOp
|
||||||
|
# Op.SelfDestruct: GAS_SELF_DESTRUCT_COST selfDestruct
|
||||||
|
|
||||||
var mem = newMemory(pow(1024.int256, 2))
|
var mem = newMemory(pow(1024.int256, 2))
|
||||||
|
|
||||||
|
@ -120,7 +124,7 @@ var msg = newMessage(
|
||||||
0.int256,
|
0.int256,
|
||||||
data,
|
data,
|
||||||
code,
|
code,
|
||||||
MessageOptions(depth: 1.int256))
|
MessageOptions(depth: 1))
|
||||||
|
|
||||||
var c = BaseComputation(
|
var c = BaseComputation(
|
||||||
vmState: BaseVMState(
|
vmState: BaseVMState(
|
||||||
|
|
|
@ -18,6 +18,11 @@ proc validateGte*(value: Int256, minimum: int, title: string = "Value") =
|
||||||
raise newException(ValidationError,
|
raise newException(ValidationError,
|
||||||
fmt"{title} {value} is not greater than or equal to {minimum}")
|
fmt"{title} {value} is not greater than or equal to {minimum}")
|
||||||
|
|
||||||
|
proc validateGte*(value: int, minimum: int, title: string = "Value") =
|
||||||
|
if value <= minimum:
|
||||||
|
raise newException(ValidationError,
|
||||||
|
fmt"{title} {value} is not greater than or equal to {minimum}")
|
||||||
|
|
||||||
proc validateGt*(value: Int256, minimum: int, title: string = "Value") =
|
proc validateGt*(value: Int256, minimum: int, title: string = "Value") =
|
||||||
if value < minimum.int256:
|
if value < minimum.int256:
|
||||||
raise newException(ValidationError,
|
raise newException(ValidationError,
|
||||||
|
|
|
@ -26,7 +26,7 @@ type
|
||||||
code*: cstring
|
code*: cstring
|
||||||
internalOrigin: cstring
|
internalOrigin: cstring
|
||||||
internalCodeAddress: cstring
|
internalCodeAddress: cstring
|
||||||
depth*: Int256
|
depth*: int
|
||||||
internalStorageAddress: cstring
|
internalStorageAddress: cstring
|
||||||
shouldTransferValue*: bool
|
shouldTransferValue*: bool
|
||||||
isStatic*: bool
|
isStatic*: bool
|
||||||
|
@ -34,7 +34,7 @@ type
|
||||||
|
|
||||||
MessageOptions* = ref object
|
MessageOptions* = ref object
|
||||||
origin*: cstring
|
origin*: cstring
|
||||||
depth*: Int256
|
depth*: int
|
||||||
createAddress*: cstring
|
createAddress*: cstring
|
||||||
codeAddress*: cstring
|
codeAddress*: cstring
|
||||||
shouldTransferValue*: bool
|
shouldTransferValue*: bool
|
||||||
|
@ -51,7 +51,7 @@ proc `storageAddress=`*(message: var Message, value: cstring) =
|
||||||
|
|
||||||
proc newMessageOptions*(
|
proc newMessageOptions*(
|
||||||
origin: cstring = nil,
|
origin: cstring = nil,
|
||||||
depth: Int256 = 0.int256,
|
depth: int = 0,
|
||||||
createAddress: cstring = nil,
|
createAddress: cstring = nil,
|
||||||
codeAddress: cstring = nil,
|
codeAddress: cstring = nil,
|
||||||
shouldTransferValue: bool = true,
|
shouldTransferValue: bool = true,
|
||||||
|
|
Loading…
Reference in New Issue