mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-23 10:40:51 +00:00
Gas refactoring - decouple opcode logic and gas (#49)
* Decoupling op logic and gas - introduce gasometer, rework opcode declaration * Remove gas constants for gas opcode computation * Remove gas constants for precompiled contracts * make vm_types compile * Make opcode, call and computation compile * Distinguish between dynamic and complex gas costs, fix arithmetic * Fix context and sha3 * update memory and storage ops * Log opcode uses memory expansion code * update/stub system_ops with gas costs * Make test compile. Deactivate stub test_vm * all tests compiles, opcode fails due to https://github.com/nim-lang/Nim/issues/8007 (const object variant in tables reset at runtime) * Create an enum without holes - workaround: https://github.com/nim-lang/Nim/issues/8007 * Use arrays instead of tables for GasCosts, remove some unused imports - passing all basic tests! * Make test_vm_json compile * Fix test_vm_json - workaround https://github.com/nim-lang/Nim/issues/8015 * fix memory expansion cost bug * Remove leftover special handling from before GckMemExpansion * cleanup outdated comment, better align = * Fix sha3 gas cost not taking memory expansion into account * Improve gas error reporting of test_vm_json * Fix gas computation regression due to mem expansion * mass replace for memExpansion->RequestedMemSize was too eager * fix log gas cost (no tests :/) * missed a static FeeSchedule * static as expression is fickle
This commit is contained in:
parent
8528f1b704
commit
90c3ca4a96
24
VMTests.md
24
VMTests.md
@ -9,7 +9,7 @@ VMTests
|
|||||||
+ add4.json OK
|
+ add4.json OK
|
||||||
+ addmod0.json OK
|
+ addmod0.json OK
|
||||||
+ addmod1.json OK
|
+ addmod1.json OK
|
||||||
+ addmod1_overflow2.json OK
|
- addmod1_overflow2.json Fail
|
||||||
+ addmod1_overflow3.json OK
|
+ addmod1_overflow3.json OK
|
||||||
+ addmod1_overflow4.json OK
|
+ addmod1_overflow4.json OK
|
||||||
+ addmod1_overflowDiff.json OK
|
+ addmod1_overflowDiff.json OK
|
||||||
@ -135,7 +135,7 @@ VMTests
|
|||||||
+ mulUnderFlow.json OK
|
+ mulUnderFlow.json OK
|
||||||
+ mulmod0.json OK
|
+ mulmod0.json OK
|
||||||
+ mulmod1.json OK
|
+ mulmod1.json OK
|
||||||
+ mulmod1_overflow.json OK
|
- mulmod1_overflow.json Fail
|
||||||
+ mulmod1_overflow2.json OK
|
+ mulmod1_overflow2.json OK
|
||||||
+ mulmod1_overflow3.json OK
|
+ mulmod1_overflow3.json OK
|
||||||
+ mulmod1_overflow4.json OK
|
+ mulmod1_overflow4.json OK
|
||||||
@ -177,7 +177,7 @@ VMTests
|
|||||||
+ signextend_BitIsNotSet.json OK
|
+ signextend_BitIsNotSet.json OK
|
||||||
+ signextend_BitIsNotSetInHigherByte.json OK
|
+ signextend_BitIsNotSetInHigherByte.json OK
|
||||||
+ signextend_BitIsSetInHigherByte.json OK
|
+ signextend_BitIsSetInHigherByte.json OK
|
||||||
- signextend_Overflow_dj42.json Fail
|
+ signextend_Overflow_dj42.json OK
|
||||||
+ signextend_bigBytePlus1.json OK
|
+ signextend_bigBytePlus1.json OK
|
||||||
+ signextend_bitIsSet.json OK
|
+ signextend_bitIsSet.json OK
|
||||||
+ smod0.json OK
|
+ smod0.json OK
|
||||||
@ -198,7 +198,7 @@ VMTests
|
|||||||
+ sub3.json OK
|
+ sub3.json OK
|
||||||
+ sub4.json OK
|
+ sub4.json OK
|
||||||
```
|
```
|
||||||
OK: 190/195 Fail: 4/195 Skip: 1/195
|
OK: 189/195 Fail: 5/195 Skip: 1/195
|
||||||
## vmBitwiseLogicOperation
|
## vmBitwiseLogicOperation
|
||||||
```diff
|
```diff
|
||||||
+ and0.json OK
|
+ and0.json OK
|
||||||
@ -265,20 +265,20 @@ OK: 190/195 Fail: 4/195 Skip: 1/195
|
|||||||
OK: 60/60 Fail: 0/60 Skip: 0/60
|
OK: 60/60 Fail: 0/60 Skip: 0/60
|
||||||
## vmBlockInfoTest
|
## vmBlockInfoTest
|
||||||
```diff
|
```diff
|
||||||
- blockhash257Block.json Fail
|
+ blockhash257Block.json OK
|
||||||
- blockhash258Block.json Fail
|
+ blockhash258Block.json OK
|
||||||
- blockhashInRange.json Fail
|
+ blockhashInRange.json OK
|
||||||
- blockhashMyBlock.json Fail
|
+ blockhashMyBlock.json OK
|
||||||
- blockhashNotExistingBlock.json Fail
|
+ blockhashNotExistingBlock.json OK
|
||||||
- blockhashOutOfRange.json Fail
|
+ blockhashOutOfRange.json OK
|
||||||
+ blockhashUnderFlow.json OK
|
+ blockhashUnderFlow.json OK
|
||||||
- coinbase.json Fail
|
+ coinbase.json OK
|
||||||
+ difficulty.json OK
|
+ difficulty.json OK
|
||||||
+ gaslimit.json OK
|
+ gaslimit.json OK
|
||||||
+ number.json OK
|
+ number.json OK
|
||||||
+ timestamp.json OK
|
+ timestamp.json OK
|
||||||
```
|
```
|
||||||
OK: 5/12 Fail: 7/12 Skip: 0/12
|
OK: 12/12 Fail: 0/12 Skip: 0/12
|
||||||
## vmEnvironmentalInfo
|
## vmEnvironmentalInfo
|
||||||
```diff
|
```diff
|
||||||
ExtCodeSizeAddressInputTooBigLeftMyAddress.json Skip
|
ExtCodeSizeAddressInputTooBigLeftMyAddress.json Skip
|
||||||
|
@ -6,25 +6,15 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
strformat, strutils, sequtils, tables, macros, stint, terminal, math, eth_common, byteutils,
|
strformat, strutils, sequtils, macros, stint, terminal, math, eth_common, byteutils, tables,
|
||||||
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging, opcode_values, vm_types,
|
./constants, ./errors, ./utils/hexadecimal, ./utils_numeric, ./validation, ./vm_state, ./logging, ./opcode_values, ./vm_types,
|
||||||
vm / [code_stream, gas_meter, memory, message, stack],
|
./vm/[code_stream, gas_meter, memory, message, stack],
|
||||||
|
|
||||||
# TODO further refactoring of gas cost
|
# TODO further refactoring of gas cost
|
||||||
vm/forks/gas_costs,
|
vm/forks/gas_costs,
|
||||||
vm/forks/f20150730_frontier/frontier_vm_state,
|
vm/forks/f20150730_frontier/frontier_vm_state,
|
||||||
vm/forks/f20161018_tangerine_whistle/tangerine_vm_state
|
vm/forks/f20161018_tangerine_whistle/tangerine_vm_state
|
||||||
|
|
||||||
proc memoryGasCost*(sizeInBytes: Natural): GasInt =
|
|
||||||
let
|
|
||||||
sizeInWords = ceil32(sizeInBytes) div 32
|
|
||||||
linearCost = sizeInWords * GAS_MEMORY
|
|
||||||
quadraticCost = sizeInWords ^ 2 div GAS_MEMORY_QUADRATIC_DENOMINATOR
|
|
||||||
totalCost = linearCost + quadraticCost
|
|
||||||
result = totalCost
|
|
||||||
|
|
||||||
#const VARIABLE_GAS_COST_OPS* = {Op.Exp}
|
|
||||||
|
|
||||||
method newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation {.base.}=
|
method newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation {.base.}=
|
||||||
raise newException(ValueError, "Must be implemented by subclasses")
|
raise newException(ValueError, "Must be implemented by subclasses")
|
||||||
|
|
||||||
@ -105,26 +95,6 @@ method prepareChildMessage*(
|
|||||||
code,
|
code,
|
||||||
childOptions)
|
childOptions)
|
||||||
|
|
||||||
method extendMemory*(c: var BaseComputation, startPosition: Natural, size: Natural) =
|
|
||||||
# Memory Management
|
|
||||||
|
|
||||||
let beforeSize = ceil32(len(c.memory))
|
|
||||||
let afterSize = ceil32(startPosition + size)
|
|
||||||
|
|
||||||
let beforeCost = memoryGasCost(beforeSize)
|
|
||||||
let afterCost = memoryGasCost(afterSize)
|
|
||||||
|
|
||||||
c.logger.debug(&"MEMORY: size ({beforeSize} -> {afterSize}) | cost ({beforeCost} -> {afterCost})")
|
|
||||||
|
|
||||||
if size > 0:
|
|
||||||
if beforeCost < afterCost:
|
|
||||||
var gasFee = afterCost - beforeCost
|
|
||||||
c.gasMeter.consumeGas(
|
|
||||||
gasFee,
|
|
||||||
reason = &"Expanding memory {beforeSize} -> {afterSize}")
|
|
||||||
|
|
||||||
c.memory.extend(startPosition, size)
|
|
||||||
|
|
||||||
method output*(c: BaseComputation): string =
|
method output*(c: BaseComputation): string =
|
||||||
if c.shouldEraseReturnData:
|
if c.shouldEraseReturnData:
|
||||||
""
|
""
|
||||||
@ -261,10 +231,10 @@ template inComputation*(c: untyped, handler: untyped): untyped =
|
|||||||
c.gasMeter.gasRemaining,
|
c.gasMeter.gasRemaining,
|
||||||
reason="Zeroing gas due to VM Exception: $1" % getCurrentExceptionMsg())
|
reason="Zeroing gas due to VM Exception: $1" % getCurrentExceptionMsg())
|
||||||
|
|
||||||
|
|
||||||
method getOpcodeFn*(computation: var BaseComputation, op: Op): Opcode =
|
method getOpcodeFn*(computation: var BaseComputation, op: Op): Opcode =
|
||||||
|
# TODO use isValidOpcode and remove the Op --> Opcode indirection
|
||||||
if computation.opcodes.len > 0 and computation.opcodes.hasKey(op):
|
if computation.opcodes.len > 0 and computation.opcodes.hasKey(op):
|
||||||
computation.opcodes[op]
|
OpCode(kind: op, runLogic: computation.opcodes[op])
|
||||||
else:
|
else:
|
||||||
raise newException(InvalidInstruction,
|
raise newException(InvalidInstruction,
|
||||||
&"Invalid opcode {op}")
|
&"Invalid opcode {op}")
|
||||||
|
@ -114,66 +114,8 @@ let
|
|||||||
ZERO_HASH32* = Hash256()
|
ZERO_HASH32* = Hash256()
|
||||||
STACK_DEPTH_LIMIT* = 1024
|
STACK_DEPTH_LIMIT* = 1024
|
||||||
|
|
||||||
# GAS_NULL* = 0.u256
|
|
||||||
# GAS_ZERO* = 0.u256
|
|
||||||
# GAS_BASE* = 2.u256
|
|
||||||
# GAS_VERY_LOW* = 3.u256
|
|
||||||
# GAS_LOW* = 5.u256
|
|
||||||
# GAS_MID* = 8.u256
|
|
||||||
# GAS_HIGH* = 10.u256
|
|
||||||
# GAS_EXT_CODE* = 20.u256 # TODO: this is pre-eip150, see also GAS_EXT_CODE_COST
|
|
||||||
# GAS_BALANCE* = 20.u256 # TODO: this is pre-eip150, see also GAS_COST_BALANCE
|
|
||||||
# GAS_SLOAD* = 200.u256 # TODO: pre eip150
|
|
||||||
# GAS_JUMP_DEST* = 1.u256
|
|
||||||
# GAS_SSET* = 20_000.u256
|
|
||||||
# GAS_SRESET* = 5_000.u256
|
|
||||||
# GAS_EXT_CODE_COST* = 700.u256
|
|
||||||
# GAS_COINBASE* = 20.u256
|
|
||||||
# GAS_SLOAD_COST* = 20.u256
|
|
||||||
# GAS_SELF_DESTRUCT_COST* = 0.u256
|
|
||||||
# GAS_IN_HANDLER* = 0.u256 # to be calculated in handler
|
|
||||||
REFUND_SCLEAR* = 15_000
|
|
||||||
|
|
||||||
# GAS_SELF_DESTRUCT* = 0.u256
|
|
||||||
# GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.u256
|
|
||||||
GAS_CREATE* = 32_000.u256
|
|
||||||
# GAS_CALL* = 40.u256
|
|
||||||
GAS_CALL_VALUE* = 9_000
|
|
||||||
GAS_CALL_STIPEND* = 2_300
|
|
||||||
GAS_NEW_ACCOUNT* = 25_000
|
|
||||||
|
|
||||||
# GAS_COST_BALANCE* = 400.u256 # TODO: this is post-eip150, see also GAS_BALANCE
|
|
||||||
|
|
||||||
# GAS_EXP* = 10.u256
|
|
||||||
# GAS_EXP_BYTE* = 10.u256
|
|
||||||
GAS_MEMORY* = 3
|
|
||||||
GAS_TX_CREATE* = 32_000.u256
|
|
||||||
GAS_TX_DATA_ZERO* = 4.u256
|
|
||||||
GAS_TX_DATA_NON_ZERO* = 68.u256
|
|
||||||
GAS_TX* = 21_000
|
|
||||||
GAS_LOG* = 375.u256
|
|
||||||
GAS_LOG_DATA* = 8
|
|
||||||
GAS_LOG_TOPIC* = 375
|
|
||||||
# GAS_SHA3* = 30.u256
|
|
||||||
GAS_SHA3_WORD* = 6
|
|
||||||
GAS_COPY* = 3
|
|
||||||
GAS_BLOCK_HASH* = 20.u256
|
|
||||||
GAS_CODE_DEPOSIT* = 200.u256
|
|
||||||
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512
|
|
||||||
GAS_SHA256* = 60.u256
|
|
||||||
GAS_SHA256WORD* = 12.u256
|
|
||||||
GAS_RIP_EMD160* = 600.u256
|
|
||||||
GAS_RIP_EMD160WORD* = 120.u256
|
|
||||||
GAS_IDENTITY* = 15.u256
|
|
||||||
GAS_IDENTITY_WORD* = 3
|
|
||||||
GAS_ECRECOVER* = 3_000.u256
|
|
||||||
GAS_ECADD* = 500.u256
|
|
||||||
GAS_ECMUL* = 40_000.u256
|
|
||||||
GAS_ECPAIRING_BASE* = 100_000.u256
|
|
||||||
GAS_ECPAIRING_PER_POINT* = 80_000.u256
|
|
||||||
GAS_LIMIT_EMA_DENOMINATOR* = 1_024
|
GAS_LIMIT_EMA_DENOMINATOR* = 1_024
|
||||||
GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024
|
GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024
|
||||||
GAS_LIMIT_MAXIMUM* = high(GasInt)
|
|
||||||
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3
|
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3
|
||||||
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2
|
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import strformat, tables, stint, rlp, ranges, state_db, backends / memory_backend,
|
import stint, tables, rlp, ranges, state_db, backends / memory_backend,
|
||||||
../errors, ../utils/header, ../constants, eth_common, byteutils
|
../errors, ../utils/header, ../constants, eth_common, byteutils
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
strformat, tables, eth_common,
|
eth_common, tables,
|
||||||
../constants, ../errors, ../validation, ../account, ../logging, ../utils_numeric, .. / utils / [padding, bytes, keccak],
|
../constants, ../errors, ../validation, ../account, ../logging, ../utils_numeric, .. / utils / [padding, bytes, keccak],
|
||||||
stint, rlp
|
stint, rlp
|
||||||
|
|
||||||
|
@ -90,10 +90,10 @@ proc exp*(computation: var BaseComputation) =
|
|||||||
# Exponentiation
|
# Exponentiation
|
||||||
let (base, exponent) = computation.stack.popInt(2)
|
let (base, exponent) = computation.stack.popInt(2)
|
||||||
|
|
||||||
var gasCost = computation.gasCosts[GasExp]
|
computation.gasMeter.consumeGas(
|
||||||
if not exponent.isZero:
|
computation.gasCosts[Exp].d_handler(exponent),
|
||||||
gasCost += gasCost * (1 + log256(exponent))
|
reason="EXP: exponent bytes"
|
||||||
computation.gasMeter.consumeGas(gasCost, reason="EXP: exponent bytes")
|
)
|
||||||
|
|
||||||
let res = if base.isZero: 0.u256 # 0^0 is 0 in py-evm
|
let res = if base.isZero: 0.u256 # 0^0 is 0 in py-evm
|
||||||
else: base.pow(exponent)
|
else: base.pow(exponent)
|
||||||
|
@ -39,20 +39,10 @@ type
|
|||||||
using
|
using
|
||||||
computation: var BaseComputation
|
computation: var BaseComputation
|
||||||
|
|
||||||
method msgExtraGas*(call: BaseCall, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt {.base.} =
|
|
||||||
raise newException(NotImplementedError, "Must be implemented by subclasses")
|
|
||||||
|
|
||||||
method msgGas*(call: BaseCall, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) {.base.} =
|
|
||||||
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
|
||||||
let totalFee = gas + extraGas
|
|
||||||
let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0)
|
|
||||||
(childMsgGas, totalFee)
|
|
||||||
|
|
||||||
method callParams*(call: BaseCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) {.base.} =
|
method callParams*(call: BaseCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) {.base.} =
|
||||||
raise newException(NotImplementedError, "Must be implemented subclasses")
|
raise newException(NotImplementedError, "Must be implemented subclasses")
|
||||||
|
|
||||||
method runLogic*(call: BaseCall, computation) =
|
method runLogic*(call: BaseCall, computation) =
|
||||||
computation.gasMeter.consumeGas(computation.gasCosts[call.gasCostKind], reason = $call.kind) # TODO: Refactoring call gas costs
|
|
||||||
let (gas, value, to, sender,
|
let (gas, value, to, sender,
|
||||||
codeAddress,
|
codeAddress,
|
||||||
memoryInputStartPosition, memoryInputSize,
|
memoryInputStartPosition, memoryInputSize,
|
||||||
@ -62,12 +52,15 @@ method runLogic*(call: BaseCall, computation) =
|
|||||||
|
|
||||||
let (memInPos, memInLen, memOutPos, memOutLen) = (memoryInputStartPosition.toInt, memoryInputSize.toInt, memoryOutputStartPosition.toInt, memoryOutputSize.toInt)
|
let (memInPos, memInLen, memOutPos, memOutLen) = (memoryInputStartPosition.toInt, memoryInputSize.toInt, memoryOutputStartPosition.toInt, memoryOutputSize.toInt)
|
||||||
|
|
||||||
computation.extendMemory(memInPos, memInLen)
|
let (gasCost, childMsgGas) = computation.gasCosts[Op.Call].c_handler(
|
||||||
computation.extendMemory(memOutPos, memOutLen)
|
value,
|
||||||
|
GasParams() # TODO - stub
|
||||||
|
)
|
||||||
|
|
||||||
|
computation.memory.extend(memInPos, memInLen)
|
||||||
|
computation.memory.extend(memOutPos, memOutLen)
|
||||||
|
|
||||||
let callData = computation.memory.read(memInPos, memInLen)
|
let callData = computation.memory.read(memInPos, memInLen)
|
||||||
let (childMsgGas, childMsgGasFee) = call.msgGas(computation, gas.toInt, to, value)
|
|
||||||
computation.gasMeter.consumeGas(childMsgGasFee, reason = $call.kind)
|
|
||||||
|
|
||||||
# TODO: Pre-call checks
|
# TODO: Pre-call checks
|
||||||
# with computation.vm_state.state_db(read_only=True) as state_db:
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
@ -125,16 +118,6 @@ method runLogic*(call: BaseCall, computation) =
|
|||||||
if not childComputation.shouldBurnGas:
|
if not childComputation.shouldBurnGas:
|
||||||
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
||||||
|
|
||||||
method msgExtraGas(call: Call, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
|
|
||||||
# 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
|
|
||||||
let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0
|
|
||||||
transferGasFee + createGasFee
|
|
||||||
|
|
||||||
method callParams(call: CallCode, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
method callParams(call: CallCode, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
||||||
let gas = computation.stack.popInt()
|
let gas = computation.stack.popInt()
|
||||||
let to = computation.stack.popAddress()
|
let to = computation.stack.popAddress()
|
||||||
@ -155,9 +138,6 @@ method callParams(call: CallCode, computation): (UInt256, UInt256, EthAddress, E
|
|||||||
true, # should_transfer_value,
|
true, # should_transfer_value,
|
||||||
computation.msg.isStatic)
|
computation.msg.isStatic)
|
||||||
|
|
||||||
method msgExtraGas(call: CallCode, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
|
|
||||||
if value != 0: GAS_CALL_VALUE else: 0
|
|
||||||
|
|
||||||
method callParams(call: Call, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
method callParams(call: Call, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
||||||
let gas = computation.stack.popInt()
|
let gas = computation.stack.popInt()
|
||||||
let codeAddress = computation.stack.popAddress()
|
let codeAddress = computation.stack.popAddress()
|
||||||
@ -181,12 +161,6 @@ method callParams(call: Call, computation): (UInt256, UInt256, EthAddress, EthAd
|
|||||||
true, # should_transfer_value,
|
true, # should_transfer_value,
|
||||||
computation.msg.isStatic)
|
computation.msg.isStatic)
|
||||||
|
|
||||||
method msgGas(call: DelegateCall, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
|
|
||||||
(gas, gas)
|
|
||||||
|
|
||||||
method msgExtraGas(call: DelegateCall, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
|
|
||||||
0
|
|
||||||
|
|
||||||
method callParams(call: DelegateCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
method callParams(call: DelegateCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
||||||
let gas = computation.stack.popInt()
|
let gas = computation.stack.popInt()
|
||||||
let codeAddress = computation.stack.popAddress()
|
let codeAddress = computation.stack.popAddress()
|
||||||
@ -210,42 +184,6 @@ method callParams(call: DelegateCall, computation): (UInt256, UInt256, EthAddres
|
|||||||
false, # should_transfer_value,
|
false, # should_transfer_value,
|
||||||
computation.msg.isStatic)
|
computation.msg.isStatic)
|
||||||
|
|
||||||
proc maxChildGasEIP150*(gas: GasInt): GasInt =
|
|
||||||
gas - gas div 64
|
|
||||||
|
|
||||||
proc computeEIP150MsgGas(computation; gas, extraGas: GasInt, value: UInt256, name: string, callStipend: GasInt): (GasInt, GasInt) =
|
|
||||||
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)
|
|
||||||
(childMsgGas, totalFee)
|
|
||||||
|
|
||||||
method msgGas(call: CallEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
|
|
||||||
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
|
||||||
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
|
|
||||||
|
|
||||||
method msgGas(call: CallCodeEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
|
|
||||||
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
|
||||||
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
|
|
||||||
|
|
||||||
method msgGas(call: DelegateCallEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
|
|
||||||
let extraGas = call.msgExtraGas(computation, gas, to, value)
|
|
||||||
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0)
|
|
||||||
|
|
||||||
proc msgExtraGas*(call: CallEIP161, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
|
|
||||||
# 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
|
|
||||||
let createGasFee = if accountIsDead and value != 0: GAS_NEW_ACCOUNT else: 0
|
|
||||||
transferGasFee + createGasFee
|
|
||||||
|
|
||||||
|
|
||||||
method callParams(call: StaticCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
method callParams(call: StaticCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
||||||
let gas = computation.stack.popInt()
|
let gas = computation.stack.popInt()
|
||||||
let to = computation.stack.popAddress()
|
let to = computation.stack.popAddress()
|
||||||
@ -265,7 +203,6 @@ method callParams(call: StaticCall, computation): (UInt256, UInt256, EthAddress,
|
|||||||
false, # should_transfer_value,
|
false, # should_transfer_value,
|
||||||
true) # is_static
|
true) # is_static
|
||||||
|
|
||||||
|
|
||||||
method callParams(call: CallByzantium, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
method callParams(call: CallByzantium, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
|
||||||
result = procCall callParams(call, computation)
|
result = procCall callParams(call, computation)
|
||||||
if computation.msg.isStatic and result[1] != 0:
|
if computation.msg.isStatic and result[1] != 0:
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
import
|
import
|
||||||
strformat,
|
strformat,
|
||||||
../constants, ../vm_types, ../errors, ../utils_numeric, ../computation, ../vm_state, ../account, ../db/state_db, ../validation,
|
../constants, ../vm_types, ../errors, ../utils_numeric, ../computation, ../vm_state, ../account, ../db/state_db, ../validation,
|
||||||
.. / vm / [stack, message, gas_meter, memory, code_stream], .. / utils / [address, padding, bytes], stint
|
.. /vm/[stack, message, gas_meter, memory, code_stream], ../utils/[address, padding, bytes], stint,
|
||||||
|
../opcode_values
|
||||||
|
|
||||||
proc balance*(computation: var BaseComputation) =
|
proc balance*(computation: var BaseComputation) =
|
||||||
let address = computation.stack.popAddress()
|
let address = computation.stack.popAddress()
|
||||||
@ -47,12 +48,14 @@ proc callDataCopy*(computation: var BaseComputation) =
|
|||||||
let (memStartPosition,
|
let (memStartPosition,
|
||||||
calldataStartPosition,
|
calldataStartPosition,
|
||||||
size) = computation.stack.popInt(3)
|
size) = computation.stack.popInt(3)
|
||||||
let (memPos, callPos, len) = (memStartPosition.toInt, calldataStartPosition.toInt, size.toInt)
|
|
||||||
computation.extendMemory(memPos, len)
|
|
||||||
|
|
||||||
let wordCount = ceil32(len) div 32
|
computation.gasMeter.consumeGas(
|
||||||
let copyGasCost = wordCount * constants.GAS_COPY
|
computation.gasCosts[CallDataCopy].d_handler(size),
|
||||||
computation.gasMeter.consumeGas(copyGasCost, reason="CALLDATACOPY fee")
|
reason="CALLDATACOPY fee")
|
||||||
|
|
||||||
|
let (memPos, callPos, len) = (memStartPosition.toInt, calldataStartPosition.toInt, size.toInt)
|
||||||
|
computation.memory.extend(memPos, len)
|
||||||
|
|
||||||
let value = computation.msg.data[callPos ..< callPos + len]
|
let value = computation.msg.data[callPos ..< callPos + len]
|
||||||
let paddedValue = padRight(value, len, 0.byte)
|
let paddedValue = padRight(value, len, 0.byte)
|
||||||
computation.memory.write(memPos, paddedValue)
|
computation.memory.write(memPos, paddedValue)
|
||||||
@ -67,13 +70,14 @@ proc codecopy*(computation: var BaseComputation) =
|
|||||||
let (memStartPosition,
|
let (memStartPosition,
|
||||||
codeStartPosition,
|
codeStartPosition,
|
||||||
size) = computation.stack.popInt(3)
|
size) = computation.stack.popInt(3)
|
||||||
|
|
||||||
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[CodeCopy].d_handler(size),
|
||||||
|
reason="CODECOPY: word gas cost")
|
||||||
|
|
||||||
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
|
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
|
||||||
computation.extendMemory(memPos, len)
|
computation.memory.extend(memPos, len)
|
||||||
|
|
||||||
let wordCount = ceil32(len) div 32
|
|
||||||
let copyGasCost = constants.GAS_COPY * wordCount
|
|
||||||
|
|
||||||
computation.gasMeter.consumeGas(copyGasCost, reason="CODECOPY: word gas cost")
|
|
||||||
# TODO
|
# TODO
|
||||||
# with computation.code.seek(code_start_position):
|
# with computation.code.seek(code_start_position):
|
||||||
# code_bytes = computation.code.read(size)
|
# code_bytes = computation.code.read(size)
|
||||||
@ -96,12 +100,15 @@ proc extCodeSize*(computation: var BaseComputation) =
|
|||||||
proc extCodeCopy*(computation: var BaseComputation) =
|
proc extCodeCopy*(computation: var BaseComputation) =
|
||||||
let account = computation.stack.popAddress()
|
let account = computation.stack.popAddress()
|
||||||
let (memStartPosition, codeStartPosition, size) = computation.stack.popInt(3)
|
let (memStartPosition, codeStartPosition, size) = computation.stack.popInt(3)
|
||||||
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
|
|
||||||
computation.extendMemory(memPos, len)
|
|
||||||
let wordCount = ceil32(len) div 32
|
|
||||||
let copyGasCost = constants.GAS_COPY * wordCount
|
|
||||||
|
|
||||||
computation.gasMeter.consumeGas(copyGasCost, reason="EXTCODECOPY: word gas cost")
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[ExtCodeCopy].d_handler(size),
|
||||||
|
reason="EXTCODECOPY: word gas cost"
|
||||||
|
)
|
||||||
|
|
||||||
|
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
|
||||||
|
computation.memory.extend(memPos, len)
|
||||||
|
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# with computation.vm_state.state_db(read_only=True) as state_db:
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
@ -116,6 +123,12 @@ proc returnDataSize*(computation: var BaseComputation) =
|
|||||||
|
|
||||||
proc returnDataCopy*(computation: var BaseComputation) =
|
proc returnDataCopy*(computation: var BaseComputation) =
|
||||||
let (memStartPosition, returnDataStartPosition, size) = computation.stack.popInt(3)
|
let (memStartPosition, returnDataStartPosition, size) = computation.stack.popInt(3)
|
||||||
|
|
||||||
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[ReturnDataCopy].d_handler(size),
|
||||||
|
reason="RETURNDATACOPY fee"
|
||||||
|
)
|
||||||
|
|
||||||
let (memPos, returnPos, len) = (memStartPosition.toInt, returnDataStartPosition.toInt, size.toInt)
|
let (memPos, returnPos, len) = (memStartPosition.toInt, returnDataStartPosition.toInt, size.toInt)
|
||||||
if returnPos + len > computation.returnData.len:
|
if returnPos + len > computation.returnData.len:
|
||||||
raise newException(OutOfBoundsRead,
|
raise newException(OutOfBoundsRead,
|
||||||
@ -123,9 +136,7 @@ proc returnDataCopy*(computation: var BaseComputation) =
|
|||||||
&"for data from index {returnDataStartPosition} to {returnDataStartPosition + size}. Return data is {computation.returnData.len} in \n" &
|
&"for data from index {returnDataStartPosition} to {returnDataStartPosition + size}. Return data is {computation.returnData.len} in \n" &
|
||||||
"length")
|
"length")
|
||||||
|
|
||||||
computation.extendMemory(memPos, len)
|
computation.memory.extend(memPos, len)
|
||||||
let wordCount = ceil32(len) div 32
|
|
||||||
let copyGasCost = wordCount * constants.GAS_COPY
|
|
||||||
computation.gasMeter.consumeGas(copyGasCost, reason="RETURNDATACOPY fee")
|
|
||||||
let value = ($computation.returnData)[returnPos ..< returnPos + len]
|
let value = ($computation.returnData)[returnPos ..< returnPos + len]
|
||||||
computation.memory.write(memPos, len, value)
|
computation.memory.write(memPos, len, value)
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
strformat, macros,
|
strformat, macros,
|
||||||
../constants, ../errors, ../vm_types, ../computation, .. / vm / [stack, memory, gas_meter, message], .. / utils / bytes, stint
|
../constants, ../errors, ../vm_types, ../computation, ../vm/[stack, memory, gas_meter, message], ../utils/bytes,
|
||||||
|
../opcode_values,
|
||||||
|
stint
|
||||||
|
|
||||||
{.this: computation.}
|
{.this: computation.}
|
||||||
{.experimental.}
|
{.experimental.}
|
||||||
@ -50,12 +52,12 @@ macro logXX(topicCount: static[int]): untyped =
|
|||||||
|
|
||||||
result.body.add(topicCode)
|
result.body.add(topicCode)
|
||||||
|
|
||||||
|
let OpName = ident(&"Log{topicCount}")
|
||||||
let logicCode = quote:
|
let logicCode = quote:
|
||||||
let dataGasCost = constants.GAS_LOG_DATA * `len`
|
`computation`.gasMeter.consumeGas(
|
||||||
let topicGasCost = constants.GAS_LOG_TOPIC * `topicCount`
|
`computation`.gasCosts[`OpName`].m_handler(`computation`.memory.len, `memPos` + `len`),
|
||||||
let totalGasCost = dataGasCost + topicGasCost
|
reason="Memory expansion, Log topic and data gas cost")
|
||||||
`computation`.gasMeter.consumeGas(totalGasCost, reason="Log topic and data gas cost")
|
`computation`.memory.extend(`memPos`, `len`)
|
||||||
`computation`.extendMemory(`memPos`, `len`)
|
|
||||||
let logData = `computation`.memory.read(`memPos`, `len`).toString
|
let logData = `computation`.memory.read(`memPos`, `len`).toString
|
||||||
`computation`.addLogEntry(
|
`computation`.addLogEntry(
|
||||||
account=`computation`.msg.storageAddress,
|
account=`computation`.msg.storageAddress,
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../constants, ../computation, ../vm_types, .. / vm / [stack, memory], .. / utils / [padding, bytes],
|
../constants, ../computation, ../vm_types, ../vm/[stack, memory, gas_meter], .. /utils/[padding, bytes],
|
||||||
|
../opcode_values,
|
||||||
stint
|
stint
|
||||||
|
|
||||||
|
|
||||||
@ -22,7 +23,12 @@ proc mstore*(computation) =
|
|||||||
let start = stack.popInt().toInt
|
let start = stack.popInt().toInt
|
||||||
let normalizedValue = stack.popInt().toByteArrayBE
|
let normalizedValue = stack.popInt().toByteArrayBE
|
||||||
|
|
||||||
extendMemory(start, 32)
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[MStore].m_handler(computation.memory.len, start + 32),
|
||||||
|
reason="MSTORE: GasVeryLow + memory expansion"
|
||||||
|
)
|
||||||
|
|
||||||
|
memory.extend(start, 32)
|
||||||
memory.write(start, normalizedValue)
|
memory.write(start, normalizedValue)
|
||||||
|
|
||||||
proc mstore8*(computation) =
|
proc mstore8*(computation) =
|
||||||
@ -30,14 +36,23 @@ proc mstore8*(computation) =
|
|||||||
let value = stack.popInt()
|
let value = stack.popInt()
|
||||||
let normalizedValue = (value and 0xff).toByteArrayBE
|
let normalizedValue = (value and 0xff).toByteArrayBE
|
||||||
|
|
||||||
extendMemory(start, 1)
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[MStore8].m_handler(computation.memory.len, start + 1),
|
||||||
|
reason="MSTORE8: GasVeryLow + memory expansion"
|
||||||
|
)
|
||||||
|
|
||||||
|
memory.extend(start, 1)
|
||||||
memory.write(start, [normalizedValue[0]])
|
memory.write(start, [normalizedValue[0]])
|
||||||
|
|
||||||
proc mload*(computation) =
|
proc mload*(computation) =
|
||||||
let start = stack.popInt().toInt
|
let start = stack.popInt().toInt
|
||||||
|
|
||||||
extendMemory(start, 32)
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[MLoad].m_handler(computation.memory.len, start + 32),
|
||||||
|
reason="MLOAD: GasVeryLow + memory expansion"
|
||||||
|
)
|
||||||
|
|
||||||
|
memory.extend(start, 32)
|
||||||
let value = memory.read(start, 32)
|
let value = memory.read(start, 32)
|
||||||
stack.push(value)
|
stack.push(value)
|
||||||
|
|
||||||
|
@ -6,15 +6,21 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
../constants, ../utils_numeric, .. / utils / [keccak, bytes], .. / vm / [stack, memory, gas_meter], ../computation, ../vm_types, helpers, stint
|
../constants, ../utils_numeric, ../utils/[keccak, bytes], ../vm/[stack, memory, gas_meter],
|
||||||
|
../computation, ../vm_types, ../opcode_values,
|
||||||
|
./helpers,
|
||||||
|
stint
|
||||||
|
|
||||||
proc sha3op*(computation: var BaseComputation) =
|
proc sha3op*(computation: var BaseComputation) =
|
||||||
let (startPosition, size) = computation.stack.popInt(2)
|
let (startPosition, size) = computation.stack.popInt(2)
|
||||||
let (pos, len) = (startPosition.toInt, size.toInt)
|
let (pos, len) = (startPosition.toInt, size.toInt)
|
||||||
computation.extendMemory(pos, len)
|
|
||||||
let sha3Bytes = computation.memory.read(pos, len)
|
computation.gasMeter.consumeGas(
|
||||||
let wordCount = sha3Bytes.len.ceil32 div 32 # TODO, can't we just shr instead of rounding + div
|
computation.gasCosts[Sha3].m_handler(computation.memory.len, pos + len),
|
||||||
let gasCost = constants.GAS_SHA3_WORD * wordCount
|
reason="SHA3: word gas cost"
|
||||||
computation.gasMeter.consumeGas(gasCost, reason="SHA3: word gas cost")
|
)
|
||||||
var res = keccak("")
|
|
||||||
|
computation.memory.extend(pos, len)
|
||||||
|
|
||||||
|
var res = keccak("") # TODO: stub
|
||||||
pushRes()
|
pushRes()
|
||||||
|
@ -9,6 +9,7 @@ import
|
|||||||
../constants, ../vm_types, ../errors, ../computation, ../vm_state,
|
../constants, ../vm_types, ../errors, ../computation, ../vm_state,
|
||||||
../utils/header,
|
../utils/header,
|
||||||
../db/[db_chain, state_db], ../vm/[stack, gas_meter, message],
|
../db/[db_chain, state_db], ../vm/[stack, gas_meter, message],
|
||||||
|
../opcode_values,
|
||||||
strformat, stint
|
strformat, stint
|
||||||
|
|
||||||
{.this: computation.}
|
{.this: computation.}
|
||||||
@ -26,15 +27,14 @@ proc sstore*(computation) =
|
|||||||
computation.vmState.db(readOnly=false):
|
computation.vmState.db(readOnly=false):
|
||||||
(currentValue, existing) = db.getStorage(computation.msg.storageAddress, slot)
|
(currentValue, existing) = db.getStorage(computation.msg.storageAddress, slot)
|
||||||
|
|
||||||
let isCurrentlyEmpty = not existing # currentValue == 0
|
let
|
||||||
let isGoingToBeEmpty = value == 0
|
gasParam = GasParams(kind: Op.Sstore, s_isStorageEmpty: not existing)
|
||||||
|
(gasCost, gasRefund) = computation.gasCosts[Sstore].c_handler(currentValue, gasParam)
|
||||||
|
|
||||||
let gasRefund = if isCurrentlyEmpty or not isGoingToBeEmpty: 0 else: REFUND_SCLEAR
|
computation.gasMeter.consumeGas(gasCost, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} ({currentValue})")
|
||||||
let gasCost = if isCurrentlyEmpty and not isGoingToBeEmpty: GAS_SSET else: GAS_SRESET
|
|
||||||
|
|
||||||
computation.gasMeter.consumeGas(computation.gasCosts[gasCost], &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} ({currentValue})")
|
if gasRefund > 0:
|
||||||
|
computation.gasMeter.refundGas(gasRefund)
|
||||||
if gasRefund > 0: computation.gasMeter.refundGas(gasRefund)
|
|
||||||
|
|
||||||
computation.vmState.db(readOnly=false):
|
computation.vmState.db(readOnly=false):
|
||||||
db.setStorage(computation.msg.storageAddress, slot, value)
|
db.setStorage(computation.msg.storageAddress, slot, value)
|
||||||
|
@ -9,6 +9,7 @@ import
|
|||||||
strformat,
|
strformat,
|
||||||
../constants, ../vm_types, ../errors, ../computation, ../opcode, ../opcode_values, ../logging, ../vm_state, call,
|
../constants, ../vm_types, ../errors, ../computation, ../opcode, ../opcode_values, ../logging, ../vm_state, call,
|
||||||
.. / vm / [stack, gas_meter, memory, message], .. / utils / [address, hexadecimal, bytes],
|
.. / vm / [stack, gas_meter, memory, message], .. / utils / [address, hexadecimal, bytes],
|
||||||
|
../opcode_values,
|
||||||
stint, byteutils, eth_common
|
stint, byteutils, eth_common
|
||||||
|
|
||||||
{.this: computation.}
|
{.this: computation.}
|
||||||
@ -24,14 +25,14 @@ type
|
|||||||
|
|
||||||
CreateByzantium* = ref object of CreateEIP150 # TODO: Refactoring - put that in VM forks
|
CreateByzantium* = ref object of CreateEIP150 # TODO: Refactoring - put that in VM forks
|
||||||
|
|
||||||
method maxChildGasModifier(create: Create, gas: GasInt): GasInt {.base.} =
|
# method maxChildGasModifier(create: Create, gas: GasInt): GasInt {.base.} =
|
||||||
gas
|
# gas
|
||||||
|
|
||||||
method runLogic*(create: Create, computation) =
|
method runLogic*(create: Create, computation) =
|
||||||
computation.gasMeter.consumeGas(computation.gasCosts[create.gasCost(computation)], reason = $create.kind) # TODO: Refactoring create gas costs
|
# computation.gasMeter.consumeGas(computation.gasCosts[create.gasCost(computation)], reason = $create.kind) # TODO: Refactoring create gas costs
|
||||||
let (value, startPosition, size) = computation.stack.popInt(3)
|
let (value, startPosition, size) = computation.stack.popInt(3)
|
||||||
let (pos, len) = (startPosition.toInt, size.toInt)
|
let (pos, len) = (startPosition.toInt, size.toInt)
|
||||||
computation.extendMemory(pos, len)
|
computation.memory.extend(pos, len)
|
||||||
|
|
||||||
# TODO: with
|
# TODO: with
|
||||||
# with computation.vm_state.state_db(read_only=True) as state_db:
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
@ -44,8 +45,9 @@ method runLogic*(create: Create, computation) =
|
|||||||
# return
|
# return
|
||||||
|
|
||||||
let callData = computation.memory.read(pos, len)
|
let callData = computation.memory.read(pos, len)
|
||||||
let createMsgGas = create.maxChildGasModifier(computation.gasMeter.gasRemaining)
|
# TODO refactor gas
|
||||||
computation.gasMeter.consumeGas(createMsgGas, reason="CREATE")
|
# let createMsgGas = create.maxChildGasModifier(computation.gasMeter.gasRemaining)
|
||||||
|
# computation.gasMeter.consumeGas(createMsgGas, reason="CREATE")
|
||||||
|
|
||||||
# TODO: with
|
# TODO: with
|
||||||
# with computation.vm_state.state_db() as state_db:
|
# with computation.vm_state.state_db() as state_db:
|
||||||
@ -67,7 +69,7 @@ method runLogic*(create: Create, computation) =
|
|||||||
return
|
return
|
||||||
|
|
||||||
let childMsg = computation.prepareChildMessage(
|
let childMsg = computation.prepareChildMessage(
|
||||||
gas=createMsgGas,
|
gas=0, # TODO refactor gas
|
||||||
to=constants.CREATE_CONTRACT_ADDRESS,
|
to=constants.CREATE_CONTRACT_ADDRESS,
|
||||||
value=value,
|
value=value,
|
||||||
data=cast[seq[byte]](@[]),
|
data=cast[seq[byte]](@[]),
|
||||||
@ -83,8 +85,9 @@ method runLogic*(create: Create, computation) =
|
|||||||
computation.stack.push(contractAddress)
|
computation.stack.push(contractAddress)
|
||||||
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
||||||
|
|
||||||
method maxChildGasModifier(create: CreateEIP150, gas: GasInt): GasInt =
|
# TODO refactor gas
|
||||||
maxChildGasEIP150(gas)
|
# method maxChildGasModifier(create: CreateEIP150, gas: GasInt): GasInt =
|
||||||
|
# maxChildGasEIP150(gas)
|
||||||
|
|
||||||
method runLogic*(create: CreateByzantium, computation) =
|
method runLogic*(create: CreateByzantium, computation) =
|
||||||
if computation.msg.isStatic:
|
if computation.msg.isStatic:
|
||||||
@ -144,7 +147,13 @@ proc selfdestruct(computation; beneficiary: EthAddress) =
|
|||||||
proc returnOp*(computation) =
|
proc returnOp*(computation) =
|
||||||
let (startPosition, size) = stack.popInt(2)
|
let (startPosition, size) = stack.popInt(2)
|
||||||
let (pos, len) = (startPosition.toInt, size.toInt)
|
let (pos, len) = (startPosition.toInt, size.toInt)
|
||||||
computation.extendMemory(pos, len)
|
|
||||||
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[Return].m_handler(computation.memory.len, pos + len),
|
||||||
|
reason = "RETURN"
|
||||||
|
)
|
||||||
|
|
||||||
|
computation.memory.extend(pos, len)
|
||||||
let output = memory.read(pos, len)
|
let output = memory.read(pos, len)
|
||||||
computation.output = output.toString
|
computation.output = output.toString
|
||||||
raise newException(Halt, "RETURN")
|
raise newException(Halt, "RETURN")
|
||||||
@ -152,7 +161,13 @@ proc returnOp*(computation) =
|
|||||||
proc revert*(computation) =
|
proc revert*(computation) =
|
||||||
let (startPosition, size) = stack.popInt(2)
|
let (startPosition, size) = stack.popInt(2)
|
||||||
let (pos, len) = (startPosition.toInt, size.toInt)
|
let (pos, len) = (startPosition.toInt, size.toInt)
|
||||||
computation.extendMemory(pos, len)
|
|
||||||
|
computation.gasMeter.consumeGas(
|
||||||
|
computation.gasCosts[Op.Revert].m_handler(computation.memory.len, pos + len),
|
||||||
|
reason = "REVERT"
|
||||||
|
)
|
||||||
|
|
||||||
|
computation.memory.extend(pos, len)
|
||||||
let output = memory.read(pos, len).toString
|
let output = memory.read(pos, len).toString
|
||||||
computation.output = output
|
computation.output = output
|
||||||
raise newException(Revert, $output)
|
raise newException(Revert, $output)
|
||||||
|
@ -20,68 +20,15 @@ from logic.call import runLogic, BaseCall
|
|||||||
template run*(opcode: Opcode, computation: var BaseComputation) =
|
template run*(opcode: Opcode, computation: var BaseComputation) =
|
||||||
# Hook for performing the actual VM execution
|
# Hook for performing the actual VM execution
|
||||||
# opcode.consumeGas(computation)
|
# opcode.consumeGas(computation)
|
||||||
computation.gasMeter.consumeGas(computation.gasCosts[opcode.gasCost(computation)], reason = $opcode.kind) # TODO: further refactoring of gas costs
|
|
||||||
|
|
||||||
if opcode.kind == Op.Call: # Super dirty fix for https://github.com/status-im/nimbus/issues/46
|
if opcode.kind == Op.Call: # Super dirty fix for https://github.com/status-im/nimbus/issues/46
|
||||||
|
# TODO remove this branch
|
||||||
runLogic(BaseCall(opcode), computation)
|
runLogic(BaseCall(opcode), computation)
|
||||||
|
elif computation.gasCosts[opcode.kind].kind != GckFixed:
|
||||||
|
opcode.runLogic(computation)
|
||||||
else:
|
else:
|
||||||
|
computation.gasMeter.consumeGas(computation.gasCosts[opcode.kind].cost, reason = $opcode.kind)
|
||||||
opcode.runLogic(computation)
|
opcode.runLogic(computation)
|
||||||
|
|
||||||
method logger*(opcode: Opcode): Logger =
|
method logger*(opcode: Opcode): Logger =
|
||||||
logging.getLogger(&"vm.opcode.{opcode.kind}")
|
logging.getLogger(&"vm.opcode.{opcode.kind}")
|
||||||
|
|
||||||
method gasCost*(opcode: Opcode, computation: var BaseComputation): GasCostKind =
|
|
||||||
#if opcode.kind in VARIABLE_GAS_COST_OPS:
|
|
||||||
# opcode.gasCostHandler(computation)
|
|
||||||
#else:
|
|
||||||
opcode.gasCostKind
|
|
||||||
|
|
||||||
template newOpcode*(kind: Op, gasCost: UInt256, logic: proc(computation: var BaseComputation)): Opcode =
|
|
||||||
Opcode(kind: kind, gasCostKind: gasCost, runLogic: logic)
|
|
||||||
|
|
||||||
template newOpcode*(kind: Op, gasHandler: proc(computation: var BaseComputation): UInt256, logic: proc(computation: var BaseComputation)): Opcode =
|
|
||||||
Opcode(kind: kind, gasCostHandler: gasHandler, runLogic: logic)
|
|
||||||
|
|
||||||
method `$`*(opcode: Opcode): string =
|
|
||||||
let gasCost = $opcode.gasCostKind
|
|
||||||
# if opcode.kind in VARIABLE_GAS_COST_OPS:
|
|
||||||
# "variable"
|
|
||||||
# else:
|
|
||||||
# $opcode.gasCostKind
|
|
||||||
&"{opcode.kind}(0x{opcode.kind.int.toHex(2)}: {gasCost})"
|
|
||||||
|
|
||||||
macro initOpcodes*(spec: untyped): untyped =
|
|
||||||
var value = ident("value")
|
|
||||||
result = quote:
|
|
||||||
block:
|
|
||||||
var `value` = initTable[Op, Opcode]()
|
|
||||||
|
|
||||||
for child in spec:
|
|
||||||
var ops, gasCosts, handlers: seq[NimNode]
|
|
||||||
if child.kind == nnkInfix and child[0].repr == "..":
|
|
||||||
ops = @[]
|
|
||||||
gasCosts = @[]
|
|
||||||
handlers = @[]
|
|
||||||
let first = child[1].repr.parseInt
|
|
||||||
let last = child[2][0].repr.parseInt
|
|
||||||
let op = child[2][1][1].repr
|
|
||||||
for z in first .. last:
|
|
||||||
ops.add(nnkDotExpr.newTree(ident("Op"), ident(op.replace("XX", $z))))
|
|
||||||
gasCosts.add(child[3][0][0])
|
|
||||||
handlers.add(ident(child[3][0][1].repr.replace("XX", $z)))
|
|
||||||
else:
|
|
||||||
ops = @[child[0]]
|
|
||||||
gasCosts = @[child[1][0][0]]
|
|
||||||
handlers = @[child[1][0][1]]
|
|
||||||
for z in 0 ..< ops.len:
|
|
||||||
let (op, gasCost, handler) = (ops[z], gasCosts[z], handlers[z])
|
|
||||||
let opcode = if gasCost.repr[0].isLowerAscii():
|
|
||||||
quote:
|
|
||||||
`value`[`op`] = Opcode(kind: `op`, gasCostHandler: `gasCost`, runLogic: `handler`)
|
|
||||||
else:
|
|
||||||
quote:
|
|
||||||
`value`[`op`] = Opcode(kind: `op`, gasCostKind: `gasCost`, runLogic: `handler`)
|
|
||||||
result[1].add(opcode)
|
|
||||||
|
|
||||||
result[1].add(value)
|
|
||||||
|
|
||||||
|
@ -6,114 +6,168 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
strformat, strutils, tables, macros,
|
tables,
|
||||||
constants, stint, errors, logging, vm_state,
|
stint, logging,
|
||||||
vm / [gas_meter, stack, code_stream, memory, message], db / db_chain, computation, opcode, opcode_values, utils / [header, address],
|
vm / [gas_meter, stack, code_stream, memory, message], db / 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, call, system_ops],
|
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops],
|
||||||
./vm_types
|
./vm_types
|
||||||
|
|
||||||
var OPCODE_TABLE* = initOpcodes:
|
const
|
||||||
# arithmetic
|
OpLogic*: Table[Op, proc(computation: var BaseComputation){.nimcall.}] = {
|
||||||
Op.Add: GasVeryLow add
|
# 0s: Stop and Arithmetic Operations
|
||||||
Op.Mul: GasLow mul
|
Stop: stop,
|
||||||
Op.Sub: GasVeryLow sub
|
Add: arithmetic.add,
|
||||||
Op.Div: GasLow divide
|
Mul: mul,
|
||||||
Op.SDiv: GasLow sdiv
|
Sub: sub,
|
||||||
Op.Mod: GasLow modulo
|
Div: divide,
|
||||||
Op.SMod: GasLow smod
|
Sdiv: sdiv,
|
||||||
Op.AddMod: GasMid addmod
|
Mod: modulo,
|
||||||
Op.MulMod: GasMid mulmod
|
Smod: smod,
|
||||||
Op.Exp: GasInHandler arithmetic.exp
|
Addmod: arithmetic.addmod,
|
||||||
Op.SignExtend: GasLow signextend
|
Mulmod: arithmetic.mulmod,
|
||||||
|
Exp: arithmetic.exp,
|
||||||
|
SignExtend: signextend,
|
||||||
|
|
||||||
|
# 10s: Comparison & Bitwise Logic Operations
|
||||||
|
Lt: lt,
|
||||||
|
Gt: gt,
|
||||||
|
Slt: slt,
|
||||||
|
Sgt: sgt,
|
||||||
|
Eq: eq,
|
||||||
|
IsZero: comparison.isZero,
|
||||||
|
And: andOp,
|
||||||
|
Or: orOp,
|
||||||
|
Xor: xorOp,
|
||||||
|
Not: notOp,
|
||||||
|
Byte: byteOp,
|
||||||
|
|
||||||
# comparison
|
# 20s: SHA3
|
||||||
Op.Lt: GasVeryLow lt
|
Sha3: sha3op,
|
||||||
Op.Gt: GasVeryLow gt
|
|
||||||
Op.SLt: GasVeryLow slt
|
|
||||||
Op.SGt: GasVeryLow sgt
|
|
||||||
Op.Eq: GasVeryLow eq
|
|
||||||
Op.IsZero: GasVeryLow iszero
|
|
||||||
Op.And: GasVeryLow andOp
|
|
||||||
Op.Or: GasVeryLow orOp
|
|
||||||
Op.Xor: GasVeryLow xorOp
|
|
||||||
Op.Not: GasVeryLow notOp
|
|
||||||
Op.Byte: GasVeryLow byteOp
|
|
||||||
|
|
||||||
|
# 30s: Environmental Information
|
||||||
|
Address: context.address,
|
||||||
|
Balance: balance,
|
||||||
|
Origin: context.origin,
|
||||||
|
Caller: caller,
|
||||||
|
CallValue: callValue,
|
||||||
|
CallDataLoad: callDataLoad,
|
||||||
|
CallDataSize: callDataSize,
|
||||||
|
CallDataCopy: callDataCopy,
|
||||||
|
CodeSize: codesize,
|
||||||
|
CodeCopy: codecopy,
|
||||||
|
GasPrice: gasPrice, # TODO this wasn't used previously
|
||||||
|
ExtCodeSize: extCodeSize,
|
||||||
|
ExtCodeCopy: extCodeCopy,
|
||||||
|
ReturnDataSize: returnDataSize, # TODO this wasn't used previously
|
||||||
|
ReturnDataCopy: returnDataCopy,
|
||||||
|
|
||||||
# sha3
|
# 40s: Block Information
|
||||||
Op.SHA3: GasSHA3 sha3op
|
Blockhash: block_ops.blockhash,
|
||||||
|
Coinbase: block_ops.coinbase,
|
||||||
|
Timestamp: block_ops.timestamp,
|
||||||
|
Number: block_ops.number,
|
||||||
|
Difficulty: block_ops.difficulty,
|
||||||
|
GasLimit: block_ops.gaslimit,
|
||||||
|
|
||||||
|
# 50s: Stack, Memory, Storage and Flow Operations
|
||||||
|
Pop: stack_ops.pop,
|
||||||
|
Mload: mload,
|
||||||
|
Mstore: mstore,
|
||||||
|
Mstore8: mstore8,
|
||||||
|
Sload: sload,
|
||||||
|
Sstore: sstore,
|
||||||
|
Jump: jump,
|
||||||
|
JumpI: jumpi,
|
||||||
|
Pc: pc,
|
||||||
|
Msize: msize,
|
||||||
|
Gas: flow.gas,
|
||||||
|
JumpDest: jumpDest,
|
||||||
|
|
||||||
# context
|
# 60s & 70s: Push Operations
|
||||||
Op.Address: GasBase context.address
|
Push1: push1,
|
||||||
Op.Balance: GasBalance balance
|
Push2: push2,
|
||||||
Op.Origin: GasBase origin
|
Push3: push3,
|
||||||
Op.Caller: GasBase caller
|
Push4: push4,
|
||||||
Op.CallValue: GasBase callValue
|
Push5: push5,
|
||||||
Op.CallDataLoad: GasVeryLow callDataLoad
|
Push6: push6,
|
||||||
Op.CallDataSize: GasBase callDataSize
|
Push7: push7,
|
||||||
Op.CallDataCopy: GasBase callDataCopy
|
Push8: push8,
|
||||||
Op.CodeSize: GasBase codesize
|
Push9: push9,
|
||||||
Op.CodeCopy: GasBase codecopy
|
Push10: push10,
|
||||||
Op.ExtCodeSize: GasExtCode extCodeSize
|
Push11: push11,
|
||||||
Op.ExtCodeCopy: GasExtCode extCodeCopy
|
Push12: push12,
|
||||||
|
Push13: push13,
|
||||||
|
Push14: push14,
|
||||||
|
Push15: push15,
|
||||||
|
Push16: push16,
|
||||||
|
Push17: push17,
|
||||||
|
Push18: push18,
|
||||||
|
Push19: push19,
|
||||||
|
Push20: push20,
|
||||||
|
Push21: push21,
|
||||||
|
Push22: push22,
|
||||||
|
Push23: push23,
|
||||||
|
Push24: push24,
|
||||||
|
Push25: push25,
|
||||||
|
Push26: push26,
|
||||||
|
Push27: push27,
|
||||||
|
Push28: push28,
|
||||||
|
Push29: push29,
|
||||||
|
Push30: push30,
|
||||||
|
Push31: push31,
|
||||||
|
Push32: push32,
|
||||||
|
|
||||||
|
# 80s: Duplication Operations
|
||||||
|
Dup1: dup1,
|
||||||
|
Dup2: dup2,
|
||||||
|
Dup3: dup3,
|
||||||
|
Dup4: dup4,
|
||||||
|
Dup5: dup5,
|
||||||
|
Dup6: dup6,
|
||||||
|
Dup7: dup7,
|
||||||
|
Dup8: dup8,
|
||||||
|
Dup9: dup9,
|
||||||
|
Dup10: dup10,
|
||||||
|
Dup11: dup11,
|
||||||
|
Dup12: dup12,
|
||||||
|
Dup13: dup13,
|
||||||
|
Dup14: dup14,
|
||||||
|
Dup15: dup15,
|
||||||
|
Dup16: dup16,
|
||||||
|
|
||||||
# block
|
# 90s: Exchange Operations
|
||||||
Op.Blockhash: GasBase block_ops.blockhash
|
Swap1: swap1,
|
||||||
Op.Coinbase: GasCoinbase coinbase
|
Swap2: swap2,
|
||||||
Op.Timestamp: GasBase timestamp
|
Swap3: swap3,
|
||||||
Op.Number: GasBase number
|
Swap4: swap4,
|
||||||
Op.Difficulty: GasBase difficulty
|
Swap5: swap5,
|
||||||
Op.GasLimit: GasBase gaslimit
|
Swap6: swap6,
|
||||||
|
Swap7: swap7,
|
||||||
|
Swap8: swap8,
|
||||||
|
Swap9: swap9,
|
||||||
|
Swap10: swap10,
|
||||||
|
Swap11: swap11,
|
||||||
|
Swap12: swap12,
|
||||||
|
Swap13: swap13,
|
||||||
|
Swap14: swap14,
|
||||||
|
Swap15: swap15,
|
||||||
|
Swap16: swap16,
|
||||||
|
|
||||||
|
# a0s: Logging Operations
|
||||||
|
Log0: log0,
|
||||||
|
Log1: log1,
|
||||||
|
Log2: log2,
|
||||||
|
Log3: log3,
|
||||||
|
Log4: log4,
|
||||||
|
|
||||||
# stack
|
# f0s: System operations
|
||||||
Op.Pop: GasBase stack_ops.pop
|
# Create: create,
|
||||||
1..32 Op.PushXX: GasVeryLow pushXX # XX replaced by macro
|
# Call: call,
|
||||||
1..16 Op.DupXX: GasVeryLow dupXX
|
# CallCode: callCode,
|
||||||
1..16 Op.SwapXX: GasVeryLow swapXX
|
Return: returnOp,
|
||||||
|
# DelegateCall: delegateCall,
|
||||||
|
# StaticCall: staticCall,
|
||||||
# memory
|
Op.Revert: revert,
|
||||||
Op.MLoad: GasVeryLow mload
|
Invalid: invalidOp,
|
||||||
Op.MStore: GasVeryLow mstore
|
SelfDestruct: selfDestruct
|
||||||
Op.MStore8: GasVeryLow mstore8
|
}.toTable
|
||||||
Op.MSize: GasBase msize
|
|
||||||
|
|
||||||
# storage
|
|
||||||
Op.SLoad: GasSload sload
|
|
||||||
Op.SStore: GasInHandler sstore
|
|
||||||
|
|
||||||
|
|
||||||
# flow
|
|
||||||
Op.Jump: GasMid jump
|
|
||||||
Op.JumpI: GasMid jumpi
|
|
||||||
Op.PC: GasHigh pc
|
|
||||||
Op.Gas: GasBase flow.gas
|
|
||||||
Op.JumpDest: GasJumpDest jumpdest
|
|
||||||
Op.Stop: GasZero stop
|
|
||||||
|
|
||||||
|
|
||||||
# logging
|
|
||||||
0..4 Op.LogXX: GasInHandler logXX
|
|
||||||
|
|
||||||
|
|
||||||
# invalid
|
|
||||||
Op.Invalid: GasZero invalidOp
|
|
||||||
|
|
||||||
|
|
||||||
# system
|
|
||||||
Op.Return: GasZero returnOp
|
|
||||||
Op.SelfDestruct: GasSelfDestruct selfdestruct
|
|
||||||
|
|
||||||
|
|
||||||
# call
|
|
||||||
OPCODE_TABLE[Op.Call] = Call(kind: Op.Call)
|
|
||||||
OPCODE_TABLE[Op.CallCode] = CallCode(kind: Op.CallCode)
|
|
||||||
OPCODE_TABLE[Op.DelegateCall] = DelegateCall(kind: Op.DelegateCall)
|
|
||||||
|
|
||||||
|
|
||||||
# system
|
|
||||||
OPCODE_TABLE[Op.Create] = Create(kind: Op.Create)
|
|
||||||
|
@ -5,164 +5,174 @@
|
|||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
type
|
import ./utils/macros_gen_opcodes
|
||||||
|
|
||||||
|
fill_enum_holes:
|
||||||
|
type
|
||||||
|
# Yellow Paper Appendix H - https://ethereum.github.io/yellowpaper/paper.pdf
|
||||||
|
# Special notes from Yellow Paper:
|
||||||
|
# - Signed values are treated as two’s complement signed 256-bit integers.
|
||||||
|
# - When −2^255 is negated, there is an overflow
|
||||||
|
# - For addmod and mulmod, intermediate computations are not subject to the 2^256 modulo.
|
||||||
|
# Nimbus authors note:
|
||||||
|
# - This means that we can't naively do (Uint256 + Uint256) mod uint256,
|
||||||
|
# because the intermediate sum (or multiplication) might roll over if
|
||||||
|
# intermediate result is greater or equal 2^256
|
||||||
|
|
||||||
Op* {.pure.} = enum
|
Op* {.pure.} = enum
|
||||||
STOP = 0x0, # 0
|
# 0s: Stop and Arithmetic Operations
|
||||||
ADD, # 1
|
Stop = 0x00, # Halts execution.
|
||||||
MUL, # 2
|
Add = 0x01, # Addition operation.
|
||||||
SUB, # 3
|
Mul = 0x02, # Multiplication operation.
|
||||||
DIV, # 4
|
Sub = 0x03, # Subtraction operation.
|
||||||
SDIV, # 5
|
Div = 0x04, # Integer division operation.
|
||||||
MOD, # 6
|
Sdiv = 0x05, # Signed integer division operation (truncated).
|
||||||
SMOD, # 7
|
Mod = 0x06, # Modulo remainder operation.
|
||||||
ADDMOD, # 8
|
Smod = 0x07, # Signed modulo remainder operation.
|
||||||
MULMOD, # 9
|
Addmod = 0x08, # Modulo addition operation.
|
||||||
EXP, # 10
|
Mulmod = 0x09, # Modulo multiplication operation.
|
||||||
SIGNEXTEND, # 11
|
Exp = 0x0A, # Exponential operation
|
||||||
|
SignExtend = 0x0B, # Extend length of two’s complement signed integer.
|
||||||
|
|
||||||
LT = 0x10, # 16
|
# 10s: Comparison & Bitwise Logic Operations
|
||||||
GT, # 17
|
Lt = 0x10, # Less-than comparison.
|
||||||
SLT, # 18
|
Gt = 0x11, # Greater-than comparison.
|
||||||
SGT, # 19
|
Slt = 0x12, # Signed less-than comparison.
|
||||||
EQ, # 20
|
Sgt = 0x13, # Signed greater-than comparison.
|
||||||
ISZERO, # 21
|
Eq = 0x14, # Equality comparison.
|
||||||
AND, # 22
|
IsZero = 0x15, # Simple not operator. (Note: real Yellow Paper description)
|
||||||
OR, # 23
|
And = 0x16, # Bitwise AND operation.
|
||||||
XOR, # 24
|
Or = 0x17, # Bitwise OR operation.
|
||||||
NOT, # 25
|
Xor = 0x18, # Bitwise XOR operation.
|
||||||
BYTE, # 26
|
Not = 0x19, # Bitwise NOT operation.
|
||||||
|
Byte = 0x1A, # Retrieve single byte from word.
|
||||||
|
|
||||||
SHA3 = 0x20, # 32
|
# 20s: SHA3
|
||||||
|
Sha3 = 0x20, # Compute Keccak-256 hash.
|
||||||
|
|
||||||
ADDRESS = 0x30,# 48
|
# 30s: Environmental Information
|
||||||
BALANCE, # 49
|
Address = 0x30, # Get address of currently executing account.
|
||||||
ORIGIN, # 50
|
Balance = 0x31, # Get balance of the given account.
|
||||||
|
Origin = 0x32, # Get execution origination address.
|
||||||
|
Caller = 0x33, # Get caller address.
|
||||||
|
CallValue = 0x34, # Get deposited value by the instruction/transaction responsible for this execution.
|
||||||
|
CallDataLoad = 0x35, # Get input data of current environment.
|
||||||
|
CallDataSize = 0x36, # Get size of input data in current environment.
|
||||||
|
CallDataCopy = 0x37, # Copy input data in current environment to memory.
|
||||||
|
CodeSize = 0x38, # Get size of code running in current environment.
|
||||||
|
CodeCopy = 0x39, # Copy code running in current environment to memory.
|
||||||
|
GasPrice = 0x3a, # Get price of gas in current environment.
|
||||||
|
ExtCodeSize = 0x3b, # Get size of an account's code
|
||||||
|
ExtCodeCopy = 0x3c, # Copy an account's code to memory.
|
||||||
|
ReturnDataSize = 0x3d, # Get size of output data from the previous call from the current environment.
|
||||||
|
ReturnDataCopy = 0x3e, # Copy output data from the previous call to memory.
|
||||||
|
|
||||||
CALLER, # 51
|
# 40s: Block Information
|
||||||
CALLVALUE, # 52
|
Blockhash = 0x40, # Get the hash of one of the 256 most recent complete blocks.
|
||||||
CALLDATALOAD, # 53
|
Coinbase = 0x41, # Get the block's beneficiary address.
|
||||||
CALLDATASIZE, # 54
|
Timestamp = 0x42, # Get the block's timestamp.
|
||||||
CALLDATACOPY, # 55
|
Number = 0x43, # Get the block's number.
|
||||||
|
Difficulty = 0x44, # Get the block's difficulty.
|
||||||
|
GasLimit = 0x45, # Get the block's gas limit.
|
||||||
|
|
||||||
CODESIZE, # 56
|
# 50s: Stack, Memory, Storage and Flow Operations
|
||||||
CODECOPY, # 57
|
Pop = 0x50, # Remove item from stack.
|
||||||
|
Mload = 0x51, # Load word from memory.
|
||||||
|
Mstore = 0x52, # Save word to memory.
|
||||||
|
Mstore8 = 0x53, # Save byte to memory.
|
||||||
|
Sload = 0x54, # Load word from storage.
|
||||||
|
Sstore = 0x55, # Save word to storage.
|
||||||
|
Jump = 0x56, # Alter the program counter.
|
||||||
|
JumpI = 0x57, # Conditionally alter the program counter.
|
||||||
|
Pc = 0x58, # Get the value of the program counter prior to the increment corresponding to this instruction.
|
||||||
|
Msize = 0x59, # Get the size of active memory in bytes.
|
||||||
|
Gas = 0x5a, # Get the amount of available gas, including the corresponding reduction for the cost of this instruction.
|
||||||
|
JumpDest = 0x5b, # Mark a valid destination for jumps. This operation has no effect on machine state during execution.
|
||||||
|
|
||||||
GASPRICE, # 58
|
# 60s & 70s: Push Operations.
|
||||||
|
Push1 = 0x60, # Place 1-byte item on stack.
|
||||||
|
Push2 = 0x61, # Place 2-byte item on stack.
|
||||||
|
Push3,
|
||||||
|
Push4,
|
||||||
|
Push5,
|
||||||
|
Push6,
|
||||||
|
Push7,
|
||||||
|
Push8,
|
||||||
|
Push9,
|
||||||
|
Push10,
|
||||||
|
Push11,
|
||||||
|
Push12,
|
||||||
|
Push13,
|
||||||
|
Push14,
|
||||||
|
Push15,
|
||||||
|
Push16,
|
||||||
|
Push17,
|
||||||
|
Push18,
|
||||||
|
Push19,
|
||||||
|
Push20,
|
||||||
|
Push21,
|
||||||
|
Push22,
|
||||||
|
Push23,
|
||||||
|
Push24,
|
||||||
|
Push25,
|
||||||
|
Push26,
|
||||||
|
Push27,
|
||||||
|
Push28,
|
||||||
|
Push29,
|
||||||
|
Push30,
|
||||||
|
Push31,
|
||||||
|
Push32 = 0x7f, # Place 32-byte (full word) item on stack.
|
||||||
|
|
||||||
EXTCODESIZE, # 59
|
# 80s: Duplication Operations
|
||||||
EXTCODECOPY, # 60
|
Dup1 = 0x80, # Duplicate 1st stack item.
|
||||||
|
Dup2 = 0x81, # Duplicate 2nd stack item.
|
||||||
|
Dup3,
|
||||||
|
Dup4,
|
||||||
|
Dup5,
|
||||||
|
Dup6,
|
||||||
|
Dup7,
|
||||||
|
Dup8,
|
||||||
|
Dup9,
|
||||||
|
Dup10,
|
||||||
|
Dup11,
|
||||||
|
Dup12,
|
||||||
|
Dup13,
|
||||||
|
Dup14,
|
||||||
|
Dup15,
|
||||||
|
Dup16 = 0x8f, # Duplicate 16th stack item.
|
||||||
|
|
||||||
RETURNDATASIZE, # 61
|
# 90s: Exchange Operations
|
||||||
RETURNDATACOPY, # 62
|
Swap1 = 0x90, # Exchange 1st and 2nd stack items.
|
||||||
|
Swap2 = 0x91, # Exchange 1st and 3rd stack items.
|
||||||
|
Swap3,
|
||||||
|
Swap4,
|
||||||
|
Swap5,
|
||||||
|
Swap6,
|
||||||
|
Swap7,
|
||||||
|
Swap8,
|
||||||
|
Swap9,
|
||||||
|
Swap10,
|
||||||
|
Swap11,
|
||||||
|
Swap12,
|
||||||
|
Swap13,
|
||||||
|
Swap14,
|
||||||
|
Swap15,
|
||||||
|
Swap16 = 0x9f, # Exchange 1st and 17th stack items.
|
||||||
|
|
||||||
BLOCKHASH = 0x40,# 64
|
# a0s: Logging Operations
|
||||||
|
Log0 = 0xa0, # Append log record with no topics.
|
||||||
COINBASE, # 65
|
Log1 = 0xa1, # Append log record with one topics.
|
||||||
|
Log2,
|
||||||
TIMESTAMP, # 66
|
Log3,
|
||||||
|
Log4 = 0xa4, # Append log record with four topics.
|
||||||
NUMBER, # 67
|
|
||||||
|
|
||||||
DIFFICULTY, # 68
|
|
||||||
|
|
||||||
GASLIMIT, # 69
|
|
||||||
|
|
||||||
POP = 0x50, # 80
|
|
||||||
|
|
||||||
MLOAD, # 81
|
|
||||||
MSTORE, # 82
|
|
||||||
MSTORE8 # 83
|
|
||||||
|
|
||||||
SLOAD, # 84
|
|
||||||
SSTORE, # 85
|
|
||||||
|
|
||||||
JUMP, # 86
|
|
||||||
JUMPI, # 87
|
|
||||||
|
|
||||||
PC, # 88
|
|
||||||
|
|
||||||
MSIZE, # 89
|
|
||||||
|
|
||||||
GAS, # 90
|
|
||||||
|
|
||||||
JUMPDEST, # 91
|
|
||||||
|
|
||||||
PUSH1 = 0x60, # 96
|
|
||||||
PUSH2, # 97
|
|
||||||
PUSH3, # 98
|
|
||||||
PUSH4, # 99
|
|
||||||
PUSH5, # 100
|
|
||||||
PUSH6, # 101
|
|
||||||
PUSH7, # 102
|
|
||||||
PUSH8, # 103
|
|
||||||
PUSH9, # 104
|
|
||||||
PUSH10, # 105
|
|
||||||
PUSH11, # 106
|
|
||||||
PUSH12, # 107
|
|
||||||
PUSH13, # 108
|
|
||||||
PUSH14, # 109
|
|
||||||
PUSH15, # 110
|
|
||||||
PUSH16, # 111
|
|
||||||
PUSH17, # 112
|
|
||||||
PUSH18, # 113
|
|
||||||
PUSH19, # 114
|
|
||||||
PUSH20, # 115
|
|
||||||
PUSH21, # 116
|
|
||||||
PUSH22, # 117
|
|
||||||
PUSH23, # 118
|
|
||||||
PUSH24, # 119
|
|
||||||
PUSH25, # 120
|
|
||||||
PUSH26, # 121
|
|
||||||
PUSH27, # 122
|
|
||||||
PUSH28, # 123
|
|
||||||
PUSH29, # 124
|
|
||||||
PUSH30, # 125
|
|
||||||
PUSH31, # 126
|
|
||||||
PUSH32, # 127
|
|
||||||
DUP1, # 128
|
|
||||||
DUP2, # 129
|
|
||||||
DUP3, # 130
|
|
||||||
DUP4, # 131
|
|
||||||
DUP5, # 132
|
|
||||||
DUP6, # 133
|
|
||||||
DUP7, # 134
|
|
||||||
DUP8, # 135
|
|
||||||
DUP9, # 136
|
|
||||||
DUP10, # 137
|
|
||||||
DUP11, # 138
|
|
||||||
DUP12, # 139
|
|
||||||
DUP13, # 140
|
|
||||||
DUP14, # 141
|
|
||||||
DUP15, # 142
|
|
||||||
DUP16, # 143
|
|
||||||
SWAP1, # 144
|
|
||||||
SWAP2, # 145
|
|
||||||
SWAP3, # 146
|
|
||||||
SWAP4, # 147
|
|
||||||
SWAP5, # 148
|
|
||||||
SWAP6, # 149
|
|
||||||
SWAP7, # 150
|
|
||||||
SWAP8, # 151
|
|
||||||
SWAP9, # 152
|
|
||||||
SWAP10, # 153
|
|
||||||
SWAP11, # 154
|
|
||||||
SWAP12, # 155
|
|
||||||
SWAP13, # 156
|
|
||||||
SWAP14, # 157
|
|
||||||
SWAP15, # 158
|
|
||||||
SWAP16, # 159
|
|
||||||
LOG0, # 160
|
|
||||||
LOG1, # 161
|
|
||||||
LOG2, # 162
|
|
||||||
LOG3, # 163
|
|
||||||
LOG4, # 164
|
|
||||||
CREATE = 0xf0, # 240
|
|
||||||
CALL, # 241
|
|
||||||
CALLCODE, # 242
|
|
||||||
RETURN, # 243
|
|
||||||
DELEGATECALL, # 244
|
|
||||||
STATICCALL = 0xfa,# 250
|
|
||||||
REVERT = 0xfd, # 253
|
|
||||||
SELFDESTRUCT = 0xff,# 255
|
|
||||||
INVALID # invalid
|
|
||||||
|
|
||||||
|
# f0s: System operations
|
||||||
|
Create = 0xf0, # Create a new account with associated code.
|
||||||
|
Call = 0xf1, # Message-call into an account.
|
||||||
|
CallCode = 0xf2, # Message-call into this account with an alternative account's code.
|
||||||
|
Return = 0xf3, # Halt execution returning output data.
|
||||||
|
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.
|
||||||
|
Revert = 0xfd, # Halt execution reverting state changes but returning data and remaining gas.
|
||||||
|
Invalid = 0xfe, # Designated invalid instruction.
|
||||||
|
SelfDestruct = 0xff # Halt execution and register account for later deletion.
|
||||||
|
126
nimbus/utils/macros_gen_opcodes.nim
Normal file
126
nimbus/utils/macros_gen_opcodes.nim
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
# Licensed under either of
|
||||||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
import macros, strformat, strutils
|
||||||
|
|
||||||
|
# Due to https://github.com/nim-lang/Nim/issues/8007, we can't
|
||||||
|
# use compile-time Tables of object variants, so instead, we use
|
||||||
|
# const arrays to map Op --> gas prices.
|
||||||
|
# Arrays require an enum without hole. This macro will fill the holes
|
||||||
|
# in the enum.
|
||||||
|
|
||||||
|
# This has an added benefits that we can use computed gotos (direct threaded interpreter)
|
||||||
|
# instead of call or subroutine threading to dispatch opcode.
|
||||||
|
# see: https://github.com/nim-lang/Nim/issues/7699 (computed gotos, bad codegen with enum with holes)
|
||||||
|
# see: https://github.com/status-im/nimbus/wiki/Interpreter-optimization-resources
|
||||||
|
# for interpreter dispatch strategies
|
||||||
|
|
||||||
|
macro fill_enum_holes*(body: untyped): untyped =
|
||||||
|
## Fill the holes of an enum
|
||||||
|
## For example
|
||||||
|
## type Foo = enum
|
||||||
|
## A = 0x00,
|
||||||
|
## B = 0x10
|
||||||
|
|
||||||
|
# Sanity checks
|
||||||
|
#
|
||||||
|
# StmtList
|
||||||
|
# TypeSection
|
||||||
|
# TypeDef
|
||||||
|
# PragmaExpr
|
||||||
|
# Postfix
|
||||||
|
# Ident "*"
|
||||||
|
# Ident "Op"
|
||||||
|
# Pragma
|
||||||
|
# Ident "pure"
|
||||||
|
# Empty
|
||||||
|
# EnumTy
|
||||||
|
# Empty
|
||||||
|
# EnumFieldDef
|
||||||
|
# Ident "Stop"
|
||||||
|
# IntLit 0
|
||||||
|
# EnumFieldDef
|
||||||
|
# Ident "Add"
|
||||||
|
# IntLit 1
|
||||||
|
body[0].expectKind(nnkTypeSection)
|
||||||
|
body[0][0][2].expectKind(nnkEnumTy)
|
||||||
|
|
||||||
|
let opcodes = body[0][0][2]
|
||||||
|
|
||||||
|
# We will iterate over all the opcodes
|
||||||
|
# check if the i-th value is declared, if not add a no-op
|
||||||
|
# and accumulate that in a "dense opcodes" declaration
|
||||||
|
|
||||||
|
var
|
||||||
|
opcode = 0
|
||||||
|
holes_idx = 1
|
||||||
|
dense_opcs = nnkEnumTy.newTree()
|
||||||
|
dense_opcs.add newEmptyNode()
|
||||||
|
|
||||||
|
# Iterate on the enum with holes
|
||||||
|
while holes_idx < opcodes.len:
|
||||||
|
let curr_ident = opcodes[holes_idx]
|
||||||
|
|
||||||
|
if curr_ident.kind in {nnkIdent, nnkEmpty} or
|
||||||
|
(curr_ident.kind == nnkEnumFieldDef and
|
||||||
|
curr_ident[1].intVal == opcode):
|
||||||
|
|
||||||
|
dense_opcs.add curr_ident
|
||||||
|
inc holes_idx
|
||||||
|
else:
|
||||||
|
dense_opcs.add newIdentNode(&"Nop0x{opcode.toHex(2)}")
|
||||||
|
|
||||||
|
inc opcode
|
||||||
|
|
||||||
|
result = body
|
||||||
|
result[0][0][2] = dense_opcs
|
||||||
|
|
||||||
|
macro fill_enum_table_holes*(enumTy: typedesc[enum], nop_filler, body: untyped): untyped =
|
||||||
|
## Fill the holes of table mapping for enum with a default value
|
||||||
|
##
|
||||||
|
## For example for enum
|
||||||
|
## type Foo = enum
|
||||||
|
## A = 0x00,
|
||||||
|
## B = 0x01,
|
||||||
|
## C = 0x02
|
||||||
|
## let foo = fill_enum_table_holes(Foo, 999):
|
||||||
|
## [A: 10, C: 20]
|
||||||
|
##
|
||||||
|
## will result into `[A: 10, B: 999, C: 20]`
|
||||||
|
|
||||||
|
# Sanity checks - body
|
||||||
|
# StmtList
|
||||||
|
# Bracket
|
||||||
|
# ExprColonExpr
|
||||||
|
# Ident "Stop"
|
||||||
|
# Command
|
||||||
|
# Ident "fixed"
|
||||||
|
# Ident "GasZero"
|
||||||
|
# ExprColonExpr
|
||||||
|
# Ident "Add"
|
||||||
|
# Command
|
||||||
|
# Ident "fixed"
|
||||||
|
# Ident "GasVeryLow"
|
||||||
|
body[0].expectKind(nnkBracket)
|
||||||
|
|
||||||
|
let
|
||||||
|
enumImpl = enumTy.getType[1]
|
||||||
|
opctable = body[0]
|
||||||
|
|
||||||
|
result = nnkBracket.newTree()
|
||||||
|
var
|
||||||
|
opcode = 1 # enumImpl[0] is an empty node
|
||||||
|
body_idx = 0
|
||||||
|
|
||||||
|
while opcode < enumImpl.len:
|
||||||
|
opctable[body_idx].expectKind(nnkExprColonExpr)
|
||||||
|
if eqIdent(enumImpl[opcode], opctable[body_idx][0]):
|
||||||
|
result.add opctable[body_idx]
|
||||||
|
inc body_idx
|
||||||
|
else:
|
||||||
|
result.add nop_filler
|
||||||
|
inc opcode
|
@ -9,8 +9,6 @@ import stint, constants, strformat, strutils, sequtils, endians, macros, utils /
|
|||||||
|
|
||||||
# some methods based on py-evm utils/numeric
|
# some methods based on py-evm utils/numeric
|
||||||
|
|
||||||
# TODO improve
|
|
||||||
|
|
||||||
proc intToBigEndian*(value: UInt256): Bytes {.deprecated.} =
|
proc intToBigEndian*(value: UInt256): Bytes {.deprecated.} =
|
||||||
result = newSeq[byte](32)
|
result = newSeq[byte](32)
|
||||||
result[0 .. ^1] = value.toByteArrayBE()
|
result[0 .. ^1] = value.toByteArrayBE()
|
||||||
@ -21,21 +19,9 @@ proc bigEndianToInt*(value: openarray[byte]): UInt256 =
|
|||||||
else:
|
else:
|
||||||
readUintBE[256](padLeft(@value, 32, 0.byte))
|
readUintBE[256](padLeft(@value, 32, 0.byte))
|
||||||
|
|
||||||
#echo intToBigEndian("32482610168005790164680892356840817100452003984372336767666156211029086934369".u256)
|
|
||||||
|
|
||||||
# proc bitLength*(value: UInt256): int =
|
|
||||||
# 255 - value.countLeadingZeroBits
|
|
||||||
|
|
||||||
proc log256*(value: UInt256): Natural =
|
proc log256*(value: UInt256): Natural =
|
||||||
(255 - value.countLeadingZeroBits) div 8 # Compilers optimize to `shr 3`
|
(255 - value.countLeadingZeroBits) div 8 # Compilers optimize to `shr 3`
|
||||||
|
|
||||||
# proc ceil8*(value: int): int =
|
|
||||||
# let remainder = value mod 8
|
|
||||||
# if remainder == 0:
|
|
||||||
# value
|
|
||||||
# else:
|
|
||||||
# value + 8 - remainder
|
|
||||||
|
|
||||||
proc unsignedToSigned*(value: UInt256): Int256 =
|
proc unsignedToSigned*(value: UInt256): Int256 =
|
||||||
0.i256
|
0.i256
|
||||||
# TODO Remove stub (used in quasiBoolean for signed comparison)
|
# TODO Remove stub (used in quasiBoolean for signed comparison)
|
||||||
@ -54,17 +40,16 @@ proc pseudoSignedToUnsigned*(value: UInt256): UInt256 =
|
|||||||
if value > INT_256_MAX_AS_UINT256:
|
if value > INT_256_MAX_AS_UINT256:
|
||||||
result += INT_256_MAX_AS_UINT256
|
result += INT_256_MAX_AS_UINT256
|
||||||
|
|
||||||
# it's deasible to map nameXX methods like that (originally decorator)
|
func ceil32*(value: Natural): Natural {.inline.}=
|
||||||
macro ceilXX(ceiling: static[int]): untyped =
|
# Round input to the nearest bigger multiple of 32
|
||||||
var name = ident(&"ceil{ceiling}")
|
|
||||||
result = quote:
|
|
||||||
proc `name`*(value: Natural): Natural =
|
|
||||||
var remainder = value mod `ceiling`
|
|
||||||
if remainder == 0:
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
return value + `ceiling` - remainder
|
|
||||||
|
|
||||||
|
result = value
|
||||||
|
|
||||||
ceilXX(32)
|
let remainder = result and 31 # equivalent to modulo 32
|
||||||
ceilXX(8)
|
if remainder != 0:
|
||||||
|
return value + 32 - remainder
|
||||||
|
|
||||||
|
func wordCount*(length: Natural): Natural {.inline.}=
|
||||||
|
# Returns the number of EVM words corresponding to a specific size.
|
||||||
|
# EVM words is rounded up
|
||||||
|
length.ceil32 shr 5 # equivalent to `div 32` (32 = 2^5)
|
||||||
|
@ -6,40 +6,549 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
stint, ../../vm_types
|
stint, eth_common, math,
|
||||||
|
../../utils/macros_gen_opcodes,
|
||||||
|
../../opcode_values,
|
||||||
|
../../utils_numeric
|
||||||
|
|
||||||
# TODO: Make that computation at compile-time.
|
# Gas Fee Schedule
|
||||||
# Go-Ethereum uses pure uint64 for gas computation
|
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf
|
||||||
const BaseGasCosts*: GasCosts = [
|
type
|
||||||
|
GasFeeKind = enum
|
||||||
|
GasZero, # Nothing paid for operations of the set Wzero.
|
||||||
|
GasBase, # Amount of gas to pay for operations of the set Wbase.
|
||||||
|
GasVeryLow, # Amount of gas to pay for operations of the set Wverylow.
|
||||||
|
GasLow, # Amount of gas to pay for operations of the set Wlow.
|
||||||
|
GasMid, # Amount of gas to pay for operations of the set Wmid.
|
||||||
|
GasHigh, # Amount of gas to pay for operations of the set Whigh.
|
||||||
|
GasExtCode, # Amount of gas to pay for operations of the set Wextcode.
|
||||||
|
GasBalance, # Amount of gas to pay for a BALANCE operation.
|
||||||
|
GasSload, # Paid for a SLOAD operation.
|
||||||
|
GasJumpDest, # Paid for a JUMPDEST operation.
|
||||||
|
GasSset, # Paid for an SSTORE operation when the storage value is set to non-zero from zero.
|
||||||
|
GasSreset, # Paid for an SSTORE operation when the storage value’s zeroness remains unchanged or is set to zero.
|
||||||
|
RefundSclear, # Refund given (added into refund counter) when the storage value is set to zero from non-zero.
|
||||||
|
RefundSelfDestruct, # Refund given (added into refund counter) for self-destructing an account.
|
||||||
|
GasSelfDestruct, # Amount of gas to pay for a SELFDESTRUCT operation.
|
||||||
|
GasCreate, # Paid for a CREATE operation.
|
||||||
|
GasCodeDeposit, # Paid per byte for a CREATE operation to succeed in placing code into state.
|
||||||
|
GasCall, # Paid for a CALL operation.
|
||||||
|
GasCallValue, # Paid for a non-zero value transfer as part of the CALL operation.
|
||||||
|
GasCallStipend, # A stipend for the called contract subtracted from Gcallvalue for a non-zero value transfer.
|
||||||
|
GasNewAccount, # Paid for a CALL or SELFDESTRUCT operation which creates an account.
|
||||||
|
GasExp, # Partial payment for an EXP operation.
|
||||||
|
GasExpByte, # Partial payment when multiplied by ⌈log256(exponent)⌉ for the EXP operation.
|
||||||
|
GasMemory, # Paid for every additional word when expanding memory.
|
||||||
|
GasTXCreate, # Paid by all contract-creating transactions after the Homestead transition.
|
||||||
|
GasTXDataZero, # Paid for every zero byte of data or code for a transaction.
|
||||||
|
GasTXDataNonZero, # Paid for every non-zero byte of data or code for a transaction.
|
||||||
|
GasTransaction, # Paid for every transaction.
|
||||||
|
GasLog, # Partial payment for a LOG operation.
|
||||||
|
GasLogData, # Paid for each byte in a LOG operation’s data.
|
||||||
|
GasLogTopic, # Paid for each topic of a LOG operation.
|
||||||
|
GasSha3, # Paid for each SHA3 operation.
|
||||||
|
GasSha3Word, # Paid for each word (rounded up) for input data to a SHA3 operation.
|
||||||
|
GasCopy, # Partial payment for COPY operations, multiplied by words copied, rounded up.
|
||||||
|
GasBlockhash # Payment for BLOCKHASH operation.
|
||||||
|
# GasQuadDivisor # The quadratic coefficient of the input sizes of the exponentiation-over-modulo precompiled contract.
|
||||||
|
|
||||||
|
GasFeeSchedule = array[GasFeeKind, GasInt]
|
||||||
|
|
||||||
|
GasParams* = object
|
||||||
|
# Yellow Paper, Appendix H - https://ethereum.github.io/yellowpaper/paper.pdf
|
||||||
|
# GasCost is a function of (σ, μ):
|
||||||
|
# - σ is the full system state
|
||||||
|
# - μ is the machine state
|
||||||
|
# In practice, we often require the following from
|
||||||
|
# - σ: an account address
|
||||||
|
# - μ: a value popped from the stack or its size.
|
||||||
|
|
||||||
|
case kind*: Op
|
||||||
|
of Sstore:
|
||||||
|
s_isStorageEmpty*: bool
|
||||||
|
of Call:
|
||||||
|
c_isNewAccount*: bool
|
||||||
|
c_gasBalance*: GasInt
|
||||||
|
c_contractGas*: Gasint
|
||||||
|
c_activeMemSize*: Natural
|
||||||
|
c_memRequested*: Natural
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
GasCostKind* = enum
|
||||||
|
GckInvalidOp,
|
||||||
|
GckFixed,
|
||||||
|
GckDynamic,
|
||||||
|
GckMemExpansion,
|
||||||
|
GckComplex
|
||||||
|
|
||||||
|
GasResult = tuple[gasCost, gasRefund: GasInt]
|
||||||
|
|
||||||
|
GasCost = object
|
||||||
|
case kind*: GasCostKind
|
||||||
|
of GckInvalidOp:
|
||||||
|
discard
|
||||||
|
of GckFixed:
|
||||||
|
cost*: GasInt
|
||||||
|
of GckDynamic:
|
||||||
|
d_handler*: proc(value: Uint256): GasInt {.nimcall.}
|
||||||
|
of GckMemExpansion:
|
||||||
|
m_handler*: proc(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.}
|
||||||
|
of GckComplex:
|
||||||
|
c_handler*: proc(value: Uint256, gasParams: GasParams): GasResult {.nimcall.}
|
||||||
|
# We use gasCost/gasRefund for:
|
||||||
|
# - Properly log and order cost and refund (for Sstore especially)
|
||||||
|
# - Allow to use unsigned integer in the future
|
||||||
|
# - CALL instruction requires passing the child message gas (Ccallgas in yellow paper)
|
||||||
|
|
||||||
|
GasCosts* = array[Op, GasCost]
|
||||||
|
|
||||||
|
template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyped) =
|
||||||
|
|
||||||
|
## Generate the gas cost for each forks and store them in a const
|
||||||
|
## named `ResultGasCostsName`
|
||||||
|
|
||||||
|
# ############### Helper functions ##############################
|
||||||
|
|
||||||
|
func `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize: Natural): GasInt {.inline.} =
|
||||||
|
# Input: size (in bytes)
|
||||||
|
|
||||||
|
# Yellow Paper:
|
||||||
|
# Memory expansion cost
|
||||||
|
# = Cmem(μ′i) − Cmem(μi)
|
||||||
|
# μi is memory size before opcode execution
|
||||||
|
# μ'i is the memory size after opcode execution
|
||||||
|
|
||||||
|
# Cmem(a) ≡ Gmemory · a + a² / 512
|
||||||
|
let
|
||||||
|
prev_words = activeMemSize.wordCount
|
||||||
|
prev_cost = prev_words * static(FeeSchedule[GasMemory]) +
|
||||||
|
(prev_words ^ 2) shr 9 # div 512
|
||||||
|
|
||||||
|
new_words = requestedMemSize.wordCount
|
||||||
|
new_cost = new_words * static(FeeSchedule[GasMemory]) +
|
||||||
|
(new_words ^ 2) shr 9 # div 512
|
||||||
|
|
||||||
|
# TODO: add logging
|
||||||
|
result = new_cost - prev_cost
|
||||||
|
|
||||||
|
func `prefix all_but_one_64th`(gas: GasInt): GasInt {.inline.} =
|
||||||
|
## Computes all but 1/64th
|
||||||
|
## L(n) ≡ n − ⌊n/64⌋ - (floored(n/64))
|
||||||
|
# Introduced in EIP-150 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
||||||
|
# TODO: deactivate it pre-EIP150
|
||||||
|
|
||||||
|
# Note: The all-but-one-64th calculation should occur after the memory expansion fee is taken
|
||||||
|
# https://github.com/ethereum/yellowpaper/pull/442
|
||||||
|
|
||||||
|
result = gas - (gas shr 6)
|
||||||
|
|
||||||
|
# ############### Opcode gas functions ##############################
|
||||||
|
|
||||||
|
func `prefix gasExp`(value: Uint256): GasInt {.nimcall.} =
|
||||||
|
## Value is the exponent
|
||||||
|
|
||||||
|
result = static FeeSchedule[GasExp]
|
||||||
|
if not value.isZero:
|
||||||
|
result += static(FeeSchedule[GasExpByte]) * (1 + log256(value))
|
||||||
|
|
||||||
|
func `prefix gasSha3`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
|
||||||
|
result = `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
result += static(FeeSchedule[GasSha3]) +
|
||||||
|
static(FeeSchedule[GasSha3Word]) * requestedMemSize.wordCount
|
||||||
|
|
||||||
|
func `prefix gasCopy`(value: Uint256): GasInt {.nimcall.} =
|
||||||
|
## Value is the size of the input to the CallDataCopy/CodeCopy/ReturnDataCopy function
|
||||||
|
|
||||||
|
result = static(FeeSchedule[GasVeryLow]) +
|
||||||
|
static(FeeSchedule[GasCopy]) * value.toInt.wordCount
|
||||||
|
|
||||||
|
func `prefix gasExtCodeCopy`(value: Uint256): GasInt {.nimcall.} =
|
||||||
|
## Value is the size of the input to the CallDataCopy/CodeCopy/ReturnDataCopy function
|
||||||
|
|
||||||
|
result = static(FeeSchedule[GasVeryLow]) +
|
||||||
|
static(FeeSchedule[GasCopy]) * value.toInt.wordCount
|
||||||
|
|
||||||
|
func `prefix gasLoadStore`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
result = static(FeeSchedule[GasVeryLow])
|
||||||
|
result += `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
|
||||||
|
func `prefix gasSstore`(value: Uint256, gasParams: Gasparams): GasResult {.nimcall.} =
|
||||||
|
## Value is word to save
|
||||||
|
|
||||||
|
# workaround for static evaluation not working for if expression
|
||||||
|
const
|
||||||
|
gSet = FeeSchedule[GasSset]
|
||||||
|
gSreset = FeeSchedule[GasSreset]
|
||||||
|
|
||||||
|
# Gas cost - literal translation of Yellow Paper
|
||||||
|
result.gasCost = if value.isZero.not xor gasParams.s_isStorageEmpty:
|
||||||
|
gSet
|
||||||
|
else:
|
||||||
|
gSreset
|
||||||
|
|
||||||
|
# Refund
|
||||||
|
if value.isZero xor gasParams.s_isStorageEmpty:
|
||||||
|
result.gasRefund = static(FeeSchedule[RefundSclear])
|
||||||
|
|
||||||
|
func `prefix gasLog0`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
result = `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
|
||||||
|
result += static(FeeSchedule[GasLog]) +
|
||||||
|
static(FeeSchedule[GasLogData]) * (requestedMemSize - activeMemSize)
|
||||||
|
|
||||||
|
func `prefix gasLog1`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
result = `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
|
||||||
|
result += static(FeeSchedule[GasLog]) +
|
||||||
|
static(FeeSchedule[GasLogData]) * (requestedMemSize - activeMemSize) +
|
||||||
|
static(FeeSchedule[GasLogTopic])
|
||||||
|
|
||||||
|
func `prefix gasLog2`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
result = `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
|
||||||
|
result += static(FeeSchedule[GasLog]) +
|
||||||
|
static(FeeSchedule[GasLogData]) * (requestedMemSize - activeMemSize) +
|
||||||
|
static(2 * FeeSchedule[GasLogTopic])
|
||||||
|
|
||||||
|
func `prefix gasLog3`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
result = `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
|
||||||
|
result = static(FeeSchedule[GasLog]) +
|
||||||
|
static(FeeSchedule[GasLogData]) * (requestedMemSize - activeMemSize) +
|
||||||
|
static(3 * FeeSchedule[GasLogTopic])
|
||||||
|
|
||||||
|
func `prefix gasLog4`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
result = `prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
|
||||||
|
result = static(FeeSchedule[GasLog]) +
|
||||||
|
static(FeeSchedule[GasLogData]) * (requestedMemSize - activeMemSize) +
|
||||||
|
static(4 * FeeSchedule[GasLogTopic])
|
||||||
|
|
||||||
|
func `prefix gasCall`(value: Uint256, gasParams: Gasparams): GasResult {.nimcall.} =
|
||||||
|
|
||||||
|
# From the Yellow Paper, going through the equation from bottom to top
|
||||||
|
# https://ethereum.github.io/yellowpaper/paper.pdf#appendix.H
|
||||||
|
#
|
||||||
|
# More readable info on the subtleties wiki page: https://github.com/ethereum/wiki/wiki/Subtleties#other-operations
|
||||||
|
# CALL has a multi-part gas cost:
|
||||||
|
#
|
||||||
|
# - 700 base
|
||||||
|
# - 9000 additional if the value is nonzero
|
||||||
|
# - 25000 additional if the destination account does not yet exist
|
||||||
|
#
|
||||||
|
# The child message of a nonzero-value CALL operation (NOT the top-level message arising from a transaction!)
|
||||||
|
# gains an additional 2300 gas on top of the gas supplied by the calling account;
|
||||||
|
# this stipend can be considered to be paid out of the 9000 mandatory additional fee for nonzero-value calls.
|
||||||
|
# This ensures that a call recipient will always have enough gas to log that it received funds.
|
||||||
|
#
|
||||||
|
# EIP150 goes over computation: https://github.com/ethereum/eips/issues/150
|
||||||
|
#
|
||||||
|
# The discussion for the draft EIP-5, which proposes to change the CALL opcode also goes over
|
||||||
|
# the current implementation - https://github.com/ethereum/EIPs/issues/8
|
||||||
|
|
||||||
|
|
||||||
|
# First we have to take into account the costs of memory expansion:
|
||||||
|
# Note there is a "bug" in the Ethereum Yellow Paper
|
||||||
|
# - https://github.com/ethereum/yellowpaper/issues/325
|
||||||
|
# μg already includes memory expansion costs but it is not
|
||||||
|
# plainly explained n the CALL opcode details
|
||||||
|
|
||||||
|
# i.e. Cmem(μ′i) − Cmem(μi)
|
||||||
|
# Yellow Paper: μ′i ≡ M(M(μi,μs[3],μs[4]),μs[5],μs[6])
|
||||||
|
# M is the memory expansion function
|
||||||
|
# μ′i is passed through gasParams.memRequested
|
||||||
|
# TODO:
|
||||||
|
# - Py-EVM has costs for both input and output memory expansion
|
||||||
|
# https://github.com/ethereum/py-evm/blob/eed0bfe4499b394ee58113408e487e7d35ab88d6/evm/vm/logic/call.py#L56-L57
|
||||||
|
# - Parity only for the largest expansion
|
||||||
|
# https://github.com/paritytech/parity/blob/af1088ef61323f171915555023d8e993aaaed755/ethcore/evm/src/interpreter/gasometer.rs#L192-L195
|
||||||
|
# - Go-Ethereum only has one cost
|
||||||
|
# https://github.com/ethereum/go-ethereum/blob/13af27641829f61d1e6b383e37aab6caae22f2c1/core/vm/gas_table.go#L334
|
||||||
|
# ⚠⚠ Py-EVM seems wrong if memory is needed for both in and out.
|
||||||
|
result.gasCost = `prefix gasMemoryExpansion`(
|
||||||
|
gasParams.c_activeMemSize,
|
||||||
|
gasParams.c_memRequested
|
||||||
|
)
|
||||||
|
|
||||||
|
# Cnew_account - TODO - pre-EIP158 zero-value call consumed 25000 gas
|
||||||
|
# https://github.com/ethereum/eips/issues/158
|
||||||
|
if gasParams.c_isNewAccount and not value.isZero:
|
||||||
|
result.gasCost += static(FeeSchedule[GasNewAccount])
|
||||||
|
|
||||||
|
# Cxfer
|
||||||
|
if not value.isZero:
|
||||||
|
result.gasCost += static(FeeSchedule[GasCallValue])
|
||||||
|
|
||||||
|
# Cextra
|
||||||
|
result.gasCost += static(FeeSchedule[GasCall])
|
||||||
|
let cextra = result.gasCost
|
||||||
|
|
||||||
|
# Cgascap
|
||||||
|
result.gasCost = if gasParams.c_gasBalance >= result.gasCost:
|
||||||
|
min(
|
||||||
|
`prefix all_but_one_64th`(gasParams.c_gasBalance - result.gasCost),
|
||||||
|
gasParams.c_contract_gas
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
gasParams.c_contract_gas
|
||||||
|
|
||||||
|
# Ccallgas - Gas sent to the child message
|
||||||
|
result.gasRefund = result.gasCost
|
||||||
|
if not value.isZero:
|
||||||
|
result.gasRefund += static(FeeSchedule[GasCallStipend])
|
||||||
|
|
||||||
|
# Ccall
|
||||||
|
result.gasCost += cextra
|
||||||
|
|
||||||
|
func `prefix gasHalt`(activeMemSize, requestedMemSize: Natural): GasInt {.nimcall.} =
|
||||||
|
`prefix gasMemoryExpansion`(activeMemSize, requestedMemSize)
|
||||||
|
|
||||||
|
func `prefix gasSelfDestruct`(value: Uint256, gasParams: Gasparams): GasResult {.nimcall.} =
|
||||||
|
# TODO
|
||||||
|
discard
|
||||||
|
|
||||||
|
# ###################################################################################################
|
||||||
|
|
||||||
|
# TODO - change this `let` into `const` - pending: https://github.com/nim-lang/Nim/issues/8015
|
||||||
|
let `ResultGasCostsName`*{.inject.}: GasCosts = block:
|
||||||
|
# We use a block expression to avoid name redefinition conflicts
|
||||||
|
# with "fixed" and "dynamic"
|
||||||
|
|
||||||
|
# Syntactic sugar
|
||||||
|
func fixed(gasFeeKind: static[GasFeeKind]): GasCost =
|
||||||
|
GasCost(kind: GckFixed, cost: static(FeeSchedule[gasFeeKind]))
|
||||||
|
|
||||||
|
func dynamic(handler: proc(value: Uint256): GasInt {.nimcall.}): GasCost =
|
||||||
|
GasCost(kind: GckDynamic, d_handler: handler)
|
||||||
|
|
||||||
|
func memExpansion(handler: proc(activeMemSize, memExpansion: Natural): GasInt {.nimcall.}): GasCost =
|
||||||
|
GasCost(kind: GckMemExpansion, m_handler: handler)
|
||||||
|
|
||||||
|
func complex(handler: proc(value: Uint256, gasParams: GasParams): GasResult {.nimcall.}): GasCost =
|
||||||
|
GasCost(kind: GckComplex, c_handler: handler)
|
||||||
|
|
||||||
|
# Returned value
|
||||||
|
fill_enum_table_holes(Op, GasCost(kind: GckInvalidOp)):
|
||||||
|
[
|
||||||
|
# 0s: Stop and Arithmetic Operations
|
||||||
|
Stop: fixed GasZero,
|
||||||
|
Add: fixed GasVeryLow,
|
||||||
|
Mul: fixed GasLow,
|
||||||
|
Sub: fixed GasVeryLow,
|
||||||
|
Div: fixed GasLow,
|
||||||
|
Sdiv: fixed GasLow,
|
||||||
|
Mod: fixed GasLow,
|
||||||
|
Smod: fixed GasLow,
|
||||||
|
Addmod: fixed GasMid,
|
||||||
|
Mulmod: fixed GasMid,
|
||||||
|
Exp: dynamic `prefix gasExp`,
|
||||||
|
SignExtend: fixed GasLow,
|
||||||
|
|
||||||
|
# 10s: Comparison & Bitwise Logic Operations
|
||||||
|
Lt: fixed GasVeryLow,
|
||||||
|
Gt: fixed GasVeryLow,
|
||||||
|
Slt: fixed GasVeryLow,
|
||||||
|
Sgt: fixed GasVeryLow,
|
||||||
|
Eq: fixed GasVeryLow,
|
||||||
|
IsZero: fixed GasVeryLow,
|
||||||
|
And: fixed GasVeryLow,
|
||||||
|
Or: fixed GasVeryLow,
|
||||||
|
Xor: fixed GasVeryLow,
|
||||||
|
Not: fixed GasVeryLow,
|
||||||
|
Byte: fixed GasVeryLow,
|
||||||
|
|
||||||
|
# 20s: SHA3
|
||||||
|
Sha3: memExpansion `prefix gasSha3`,
|
||||||
|
|
||||||
|
# 30s: Environmental Information
|
||||||
|
Address: fixed GasBase,
|
||||||
|
Balance: fixed GasBalance,
|
||||||
|
Origin: fixed GasBase,
|
||||||
|
Caller: fixed GasBase,
|
||||||
|
CallValue: fixed GasBase,
|
||||||
|
CallDataLoad: fixed GasVeryLow,
|
||||||
|
CallDataSize: fixed GasBase,
|
||||||
|
CallDataCopy: dynamic `prefix gasCopy`,
|
||||||
|
CodeSize: fixed GasBase,
|
||||||
|
CodeCopy: dynamic `prefix gasCopy`,
|
||||||
|
GasPrice: fixed GasBase,
|
||||||
|
ExtCodeSize: fixed GasExtcode,
|
||||||
|
ExtCodeCopy: dynamic `prefix gasExtCodeCopy`,
|
||||||
|
ReturnDataSize: fixed GasBase,
|
||||||
|
ReturnDataCopy: dynamic `prefix gasCopy`,
|
||||||
|
|
||||||
|
# 40s: Block Information
|
||||||
|
Blockhash: fixed GasBlockhash,
|
||||||
|
Coinbase: fixed GasBase,
|
||||||
|
Timestamp: fixed GasBase,
|
||||||
|
Number: fixed GasBase,
|
||||||
|
Difficulty: fixed GasBase,
|
||||||
|
GasLimit: fixed GasBase,
|
||||||
|
|
||||||
|
# 50s: Stack, Memory, Storage and Flow Operations
|
||||||
|
Pop: fixed GasBase,
|
||||||
|
Mload: memExpansion `prefix gasLoadStore`,
|
||||||
|
Mstore: memExpansion `prefix gasLoadStore`,
|
||||||
|
Mstore8: memExpansion `prefix gasLoadStore`,
|
||||||
|
Sload: fixed GasSload,
|
||||||
|
Sstore: complex `prefix gasSstore`,
|
||||||
|
Jump: fixed GasMid,
|
||||||
|
JumpI: fixed GasHigh,
|
||||||
|
Pc: fixed GasBase,
|
||||||
|
Msize: fixed GasBase,
|
||||||
|
Gas: fixed GasBase,
|
||||||
|
JumpDest: fixed GasJumpDest,
|
||||||
|
|
||||||
|
# 60s & 70s: Push Operations
|
||||||
|
Push1: fixed GasVeryLow,
|
||||||
|
Push2: fixed GasVeryLow,
|
||||||
|
Push3: fixed GasVeryLow,
|
||||||
|
Push4: fixed GasVeryLow,
|
||||||
|
Push5: fixed GasVeryLow,
|
||||||
|
Push6: fixed GasVeryLow,
|
||||||
|
Push7: fixed GasVeryLow,
|
||||||
|
Push8: fixed GasVeryLow,
|
||||||
|
Push9: fixed GasVeryLow,
|
||||||
|
Push10: fixed GasVeryLow,
|
||||||
|
Push11: fixed GasVeryLow,
|
||||||
|
Push12: fixed GasVeryLow,
|
||||||
|
Push13: fixed GasVeryLow,
|
||||||
|
Push14: fixed GasVeryLow,
|
||||||
|
Push15: fixed GasVeryLow,
|
||||||
|
Push16: fixed GasVeryLow,
|
||||||
|
Push17: fixed GasVeryLow,
|
||||||
|
Push18: fixed GasVeryLow,
|
||||||
|
Push19: fixed GasVeryLow,
|
||||||
|
Push20: fixed GasVeryLow,
|
||||||
|
Push21: fixed GasVeryLow,
|
||||||
|
Push22: fixed GasVeryLow,
|
||||||
|
Push23: fixed GasVeryLow,
|
||||||
|
Push24: fixed GasVeryLow,
|
||||||
|
Push25: fixed GasVeryLow,
|
||||||
|
Push26: fixed GasVeryLow,
|
||||||
|
Push27: fixed GasVeryLow,
|
||||||
|
Push28: fixed GasVeryLow,
|
||||||
|
Push29: fixed GasVeryLow,
|
||||||
|
Push30: fixed GasVeryLow,
|
||||||
|
Push31: fixed GasVeryLow,
|
||||||
|
Push32: fixed GasVeryLow,
|
||||||
|
|
||||||
|
# 80s: Duplication Operations
|
||||||
|
Dup1: fixed GasVeryLow,
|
||||||
|
Dup2: fixed GasVeryLow,
|
||||||
|
Dup3: fixed GasVeryLow,
|
||||||
|
Dup4: fixed GasVeryLow,
|
||||||
|
Dup5: fixed GasVeryLow,
|
||||||
|
Dup6: fixed GasVeryLow,
|
||||||
|
Dup7: fixed GasVeryLow,
|
||||||
|
Dup8: fixed GasVeryLow,
|
||||||
|
Dup9: fixed GasVeryLow,
|
||||||
|
Dup10: fixed GasVeryLow,
|
||||||
|
Dup11: fixed GasVeryLow,
|
||||||
|
Dup12: fixed GasVeryLow,
|
||||||
|
Dup13: fixed GasVeryLow,
|
||||||
|
Dup14: fixed GasVeryLow,
|
||||||
|
Dup15: fixed GasVeryLow,
|
||||||
|
Dup16: fixed GasVeryLow,
|
||||||
|
|
||||||
|
# 90s: Exchange Operations
|
||||||
|
Swap1: fixed GasVeryLow,
|
||||||
|
Swap2: fixed GasVeryLow,
|
||||||
|
Swap3: fixed GasVeryLow,
|
||||||
|
Swap4: fixed GasVeryLow,
|
||||||
|
Swap5: fixed GasVeryLow,
|
||||||
|
Swap6: fixed GasVeryLow,
|
||||||
|
Swap7: fixed GasVeryLow,
|
||||||
|
Swap8: fixed GasVeryLow,
|
||||||
|
Swap9: fixed GasVeryLow,
|
||||||
|
Swap10: fixed GasVeryLow,
|
||||||
|
Swap11: fixed GasVeryLow,
|
||||||
|
Swap12: fixed GasVeryLow,
|
||||||
|
Swap13: fixed GasVeryLow,
|
||||||
|
Swap14: fixed GasVeryLow,
|
||||||
|
Swap15: fixed GasVeryLow,
|
||||||
|
Swap16: fixed GasVeryLow,
|
||||||
|
|
||||||
|
# a0s: Logging Operations
|
||||||
|
Log0: memExpansion `prefix gasLog0`,
|
||||||
|
Log1: memExpansion `prefix gasLog1`,
|
||||||
|
Log2: memExpansion `prefix gasLog2`,
|
||||||
|
Log3: memExpansion `prefix gasLog3`,
|
||||||
|
Log4: memExpansion `prefix gasLog4`,
|
||||||
|
|
||||||
|
# f0s: System operations
|
||||||
|
Create: fixed GasCreate,
|
||||||
|
Call: complex `prefix gasCall`,
|
||||||
|
CallCode: complex `prefix gasCall`,
|
||||||
|
Return: memExpansion `prefix gasHalt`,
|
||||||
|
DelegateCall: complex `prefix gasCall`,
|
||||||
|
StaticCall: complex `prefix gasCall`,
|
||||||
|
Op.Revert: memExpansion `prefix gasHalt`,
|
||||||
|
Invalid: fixed GasZero,
|
||||||
|
SelfDestruct: complex `prefix gasSelfDestruct`
|
||||||
|
]
|
||||||
|
|
||||||
|
# Generate the fork-specific gas costs tables
|
||||||
|
const
|
||||||
|
BaseGasFees: GasFeeSchedule = [
|
||||||
|
# Fee Schedule at for the initial Ethereum forks
|
||||||
GasZero: 0'i64,
|
GasZero: 0'i64,
|
||||||
GasBase: 2,
|
GasBase: 2,
|
||||||
GasVeryLow: 3,
|
GasVeryLow: 3,
|
||||||
GasLow: 5,
|
GasLow: 5,
|
||||||
GasMid: 8,
|
GasMid: 8,
|
||||||
GasHigh: 10,
|
GasHigh: 10,
|
||||||
|
GasExtCode: 20, # Changed to 700 in Tangerine (EIP150)
|
||||||
|
GasBalance: 20, # Changed to 400 in Tangerine (EIP150)
|
||||||
GasSload: 50, # Changed to 200 in Tangerine (EIP150)
|
GasSload: 50, # Changed to 200 in Tangerine (EIP150)
|
||||||
GasJumpDest: 1,
|
GasJumpDest: 1,
|
||||||
GasSset: 20_000,
|
GasSset: 20_000,
|
||||||
GasSreset: 5_000,
|
GasSreset: 5_000,
|
||||||
GasExtCode: 20,
|
RefundSclear: 15_000,
|
||||||
GasCoinbase: 20,
|
RefundSelfDestruct: 24_000,
|
||||||
GasSelfDestruct: 0, # Changed to 5000 in Tangerine (EIP150)
|
GasSelfDestruct: 0, # Changed to 5000 in Tangerine (EIP150)
|
||||||
GasInHandler: 0, # to be calculated in handler
|
GasCreate: 32000,
|
||||||
GasRefundSclear: 15000,
|
GasCodeDeposit: 200,
|
||||||
|
|
||||||
GasBalance: 20, # Changed to 400 in Tangerine (EIP150)
|
|
||||||
GasCall: 40, # Changed to 700 in Tangerine (EIP150)
|
GasCall: 40, # Changed to 700 in Tangerine (EIP150)
|
||||||
|
GasCallValue: 9000,
|
||||||
|
GasCallStipend: 2300,
|
||||||
|
GasNewAccount: 25_000,
|
||||||
GasExp: 10,
|
GasExp: 10,
|
||||||
GasSHA3: 30
|
GasExpByte: 10, # Changed to 50 in Spurious Dragon (EIP160)
|
||||||
]
|
GasMemory: 3,
|
||||||
|
GasTXCreate: 32000,
|
||||||
proc tangerineGasCosts(baseCosts: GasCosts): GasCosts =
|
GasTXDataZero: 4,
|
||||||
|
GasTXDataNonZero: 68,
|
||||||
|
GasTransaction: 21000,
|
||||||
|
GasLog: 375,
|
||||||
|
GasLogData: 8,
|
||||||
|
GasLogTopic: 375,
|
||||||
|
GasSha3: 30,
|
||||||
|
GasSha3Word: 6,
|
||||||
|
GasCopy: 3,
|
||||||
|
GasBlockhash: 20
|
||||||
|
# GasQuadDivisor: 100 # Unused, do not confuse with the quadratic coefficient 512 for memory expansion
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create the schedule for each forks
|
||||||
|
func tangerineGasFees(previous_fees: GasFeeSchedule): GasFeeSchedule =
|
||||||
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
||||||
result = baseCosts
|
result = previous_fees
|
||||||
result[GasSload] = 200
|
result[GasSload] = 200
|
||||||
result[GasSelfDestruct] = 5000
|
result[GasSelfDestruct] = 5000
|
||||||
result[GasBalance] = 400
|
result[GasBalance] = 400
|
||||||
result[GasCall] = 40
|
result[GasCall] = 40
|
||||||
|
|
||||||
const TangerineGasCosts* = BaseGasCosts.tangerineGasCosts
|
func spuriousGasFees(previous_fees: GasFeeSchedule): GasFeeSchedule =
|
||||||
|
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-160.md
|
||||||
|
result = previous_fees
|
||||||
|
result[GasExpByte] = 50
|
||||||
|
|
||||||
|
const
|
||||||
|
TangerineGasFees = BaseGasFees.tangerineGasFees
|
||||||
|
SpuriousGasFees = TangerineGasFees.spuriousGasFees
|
||||||
|
|
||||||
|
gasCosts(BaseGasFees, base, BaseGasCosts)
|
||||||
|
gasCosts(TangerineGasFees, tangerine, TangerineGasCosts)
|
||||||
|
@ -9,10 +9,10 @@ import
|
|||||||
tables,
|
tables,
|
||||||
constants, vm_state,
|
constants, vm_state,
|
||||||
opcode_values, stint, eth_common,
|
opcode_values, stint, eth_common,
|
||||||
vm / [code_stream, memory, stack],
|
vm / [code_stream, memory, stack, forks/gas_costs],
|
||||||
./logging
|
./logging
|
||||||
|
|
||||||
export GasInt
|
export GasInt, gas_costs
|
||||||
|
|
||||||
type
|
type
|
||||||
BaseComputation* = ref object of RootObj
|
BaseComputation* = ref object of RootObj
|
||||||
@ -30,9 +30,9 @@ type
|
|||||||
logEntries*: seq[(EthAddress, seq[UInt256], string)]
|
logEntries*: seq[(EthAddress, seq[UInt256], string)]
|
||||||
shouldEraseReturnData*: bool
|
shouldEraseReturnData*: bool
|
||||||
accountsToDelete*: Table[EthAddress, EthAddress]
|
accountsToDelete*: Table[EthAddress, EthAddress]
|
||||||
opcodes*: Table[Op, Opcode] # TODO array[Op, Opcode]
|
opcodes*: Table[Op, proc(computation: var BaseComputation){.nimcall.}]
|
||||||
precompiles*: Table[string, Opcode]
|
precompiles*: Table[string, Opcode]
|
||||||
gasCosts*: GasCosts # TODO separate opcode processing and gas computation
|
gasCosts*: GasCosts # TODO - avoid allocating memory for this const
|
||||||
|
|
||||||
Error* = ref object
|
Error* = ref object
|
||||||
info*: string
|
info*: string
|
||||||
@ -40,16 +40,10 @@ type
|
|||||||
erasesReturnData*: bool
|
erasesReturnData*: bool
|
||||||
|
|
||||||
Opcode* = ref object of RootObj
|
Opcode* = ref object of RootObj
|
||||||
|
# TODO can't use a stack-allocated object because
|
||||||
|
# "BaseComputation is not a concrete type"
|
||||||
|
# TODO: We can probably remove this.
|
||||||
kind*: Op
|
kind*: Op
|
||||||
#of VARIABLE_GAS_COST_OPS:
|
|
||||||
# gasCostHandler*: proc(computation: var BaseComputation): UInt256
|
|
||||||
## so, we could have special logic that separates all gas cost calculations
|
|
||||||
## from actual opcode execution
|
|
||||||
## that's what parity does:
|
|
||||||
## it uses the peek methods of the stack and calculates the cost
|
|
||||||
## then it actually pops/pushes stuff in exec
|
|
||||||
## I followed the py-evm approach which does that in opcode logic
|
|
||||||
gasCostKind*: GasCostKind
|
|
||||||
runLogic*: proc(computation: var BaseComputation)
|
runLogic*: proc(computation: var BaseComputation)
|
||||||
|
|
||||||
GasMeter* = ref object
|
GasMeter* = ref object
|
||||||
@ -58,30 +52,6 @@ type
|
|||||||
startGas*: GasInt
|
startGas*: GasInt
|
||||||
gasRemaining*: GasInt
|
gasRemaining*: GasInt
|
||||||
|
|
||||||
GasCostKind* = enum
|
|
||||||
GasZero
|
|
||||||
GasBase
|
|
||||||
GasVeryLow
|
|
||||||
GasLow
|
|
||||||
GasMid
|
|
||||||
GasHigh
|
|
||||||
GasSload
|
|
||||||
GasJumpDest
|
|
||||||
GasSset
|
|
||||||
GasSreset
|
|
||||||
GasExtCode
|
|
||||||
GasCoinbase
|
|
||||||
GasSelfDestruct
|
|
||||||
GasInHandler
|
|
||||||
GasRefundSclear
|
|
||||||
|
|
||||||
GasBalance
|
|
||||||
GasCall
|
|
||||||
GasExp
|
|
||||||
GasSHA3
|
|
||||||
|
|
||||||
GasCosts* = array[GasCostKind, GasInt]
|
|
||||||
|
|
||||||
Message* = ref object
|
Message* = ref object
|
||||||
# A message for VM computation
|
# A message for VM computation
|
||||||
|
|
||||||
|
@ -9,6 +9,6 @@ import ./test_code_stream,
|
|||||||
./test_gas_meter,
|
./test_gas_meter,
|
||||||
./test_memory,
|
./test_memory,
|
||||||
./test_stack,
|
./test_stack,
|
||||||
./test_opcode,
|
./test_opcode
|
||||||
./test_vm
|
# ./test_vm
|
||||||
# ./test_vm_json
|
# ./test_vm_json
|
||||||
|
@ -42,7 +42,7 @@ proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputat
|
|||||||
c.displayDecompiled()
|
c.displayDecompiled()
|
||||||
|
|
||||||
var computation = newBaseComputation(vm.state, message)
|
var computation = newBaseComputation(vm.state, message)
|
||||||
computation.opcodes = OPCODE_TABLE
|
computation.opcodes = OpLogic # TODO remove this need
|
||||||
computation.precompiles = initTable[string, Opcode]()
|
computation.precompiles = initTable[string, Opcode]()
|
||||||
|
|
||||||
computation = computation.applyComputation(vm.state, message)
|
computation = computation.applyComputation(vm.state, message)
|
||||||
|
@ -30,7 +30,7 @@ suite "VM":
|
|||||||
# check(not computation.isError)
|
# check(not computation.isError)
|
||||||
|
|
||||||
let
|
let
|
||||||
txGas = tx.gasPrice * constants.GAS_TX
|
# txGas = tx.gasPrice * constants.GAS_TX
|
||||||
state_db = vm.state.readOnlyStateDB
|
state_db = vm.state.readOnlyStateDB
|
||||||
b = vm.`block`
|
b = vm.`block`
|
||||||
|
|
||||||
|
@ -9,12 +9,14 @@ import
|
|||||||
unittest, strformat, strutils, sequtils, tables, stint, json, ospaths, times,
|
unittest, strformat, strutils, sequtils, tables, stint, json, ospaths, times,
|
||||||
./test_helpers,
|
./test_helpers,
|
||||||
../nimbus/[constants, errors, logging],
|
../nimbus/[constants, errors, logging],
|
||||||
../nimbus/[chain, vm_state, computation, opcode, vm_types, opcode_table],
|
../nimbus/[chain, vm_state, computation, opcode, vm_types],
|
||||||
../nimbus/utils/[header, padding],
|
../nimbus/utils/[header, padding],
|
||||||
../nimbus/vm/[gas_meter, message, code_stream, stack],
|
../nimbus/vm/[gas_meter, message, code_stream, stack],
|
||||||
../nimbus/vm/forks/vm_forks, ../nimbus/db/[db_chain, state_db, backends/memory_backend],
|
../nimbus/vm/forks/vm_forks, ../nimbus/db/[db_chain, state_db, backends/memory_backend],
|
||||||
eth_common
|
eth_common
|
||||||
|
|
||||||
|
from ../nimbus/opcode_table import OpLogic
|
||||||
|
|
||||||
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus)
|
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus)
|
||||||
|
|
||||||
suite "vm json tests":
|
suite "vm json tests":
|
||||||
@ -58,7 +60,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
|||||||
c.displayDecompiled()
|
c.displayDecompiled()
|
||||||
|
|
||||||
var computation = newBaseComputation(vm.state, message)
|
var computation = newBaseComputation(vm.state, message)
|
||||||
computation.opcodes = OPCODE_TABLE
|
computation.opcodes = OpLogic
|
||||||
computation.precompiles = initTable[string, Opcode]()
|
computation.precompiles = initTable[string, Opcode]()
|
||||||
|
|
||||||
computation = computation.applyComputation(vm.state, message)
|
computation = computation.applyComputation(vm.state, message)
|
||||||
@ -85,7 +87,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
|||||||
|
|
||||||
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt
|
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt
|
||||||
let actualGasRemaining = gasMeter.gasRemaining
|
let actualGasRemaining = gasMeter.gasRemaining
|
||||||
checkpoint(&"{actualGasRemaining} {expectedGasRemaining}")
|
checkpoint(&"Remaining: {actualGasRemaining} - Expected: {expectedGasRemaining}")
|
||||||
check(actualGasRemaining == expectedGasRemaining or
|
check(actualGasRemaining == expectedGasRemaining or
|
||||||
computation.code.hasSStore() and
|
computation.code.hasSStore() and
|
||||||
(actualGasRemaining > expectedGasRemaining and (actualGasRemaining - expectedGasRemaining) mod 15_000 == 0 or
|
(actualGasRemaining > expectedGasRemaining and (actualGasRemaining - expectedGasRemaining) mod 15_000 == 0 or
|
||||||
|
Loading…
x
Reference in New Issue
Block a user