mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-23 09:18:29 +00:00
First part of port
Translate with py2nim Work on improving the input, use more nim-ish stuff
This commit is contained in:
parent
44f6fa5945
commit
8ce5fa1773
136
src/constants.nim
Normal file
136
src/constants.nim
Normal file
@ -0,0 +1,136 @@
|
||||
|
||||
import
|
||||
math, strutils, tables #, eth_utils
|
||||
|
||||
type
|
||||
TypeHint* {.pure.} = enum UInt256, Bytes, Any
|
||||
|
||||
Int256* = distinct int # TODO
|
||||
|
||||
proc `>`*(a: Int256, b: Int256): bool =
|
||||
a.int > b.int
|
||||
|
||||
proc `<`*(a: Int256, b: Int256): bool =
|
||||
a.int < b.int
|
||||
|
||||
proc `$`*(a: Int256): string =
|
||||
$(a.int)
|
||||
|
||||
# proc `-`*(a: Int256, b: Int256): Int256 {.borrow.}
|
||||
|
||||
# proc `+`*(a: Int256, b: Int256): Int256 {.borrow.}
|
||||
|
||||
# proc `-=`*(a: var Int256, b: Int256) {.borrow.}
|
||||
|
||||
# proc `+=`*(a: var Int256, b: Int256) {.borrow.}
|
||||
|
||||
proc `-`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int - b.int).Int256
|
||||
|
||||
proc `+`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int + b.int).Int256
|
||||
|
||||
proc `-=`*(a: var Int256, b: Int256) =
|
||||
a = (a - b).Int256
|
||||
|
||||
proc `+=`*(a: var Int256, b: Int256) =
|
||||
a = (a + b).Int256
|
||||
|
||||
proc repeat(b: cstring, count: int): cstring =
|
||||
# TODO: faster
|
||||
var s = $b
|
||||
result = cstring(repeat(s, count))
|
||||
|
||||
const
|
||||
# UINT256MAX = 2 ^ 256 - 1
|
||||
# UINT256CEILING = 2 ^ 256
|
||||
# UINT255MAX = 2 ^ 255 - 1
|
||||
# UINT255CEILING = 2 ^ 255
|
||||
NULLBYTE = cstring"\\x00"
|
||||
EMPTYWORD = repeat(NULLBYTE, 32)
|
||||
# UINT160CEILING = 2 ^ 160
|
||||
CREATE_CONTRACT_ADDRESS* = cstring""
|
||||
ZEROADDRESS = repeat(cstring"\\x00", 20)
|
||||
ZEROHASH32 = repeat(cstring"\\x00", 20)
|
||||
STACKDEPTHLIMIT = 1024
|
||||
GASNULL = 0
|
||||
GASZERO = 0
|
||||
GASBASE = 2
|
||||
GASVERYLOW = 3
|
||||
GASLOW = 5
|
||||
GASMID = 8
|
||||
GASHIGH = 10
|
||||
GASEXTCODE = 20
|
||||
GASBALANCE = 20
|
||||
GASSLOAD = 50
|
||||
GASJUMPDEST = 1
|
||||
GASSSET = 20000
|
||||
GASSRESET = 5000
|
||||
REFUNDSCLEAR = 15000
|
||||
GASSELFDESTRUCT = 0
|
||||
GASSELFDESTRUCTNEWACCOUNT = 25000
|
||||
GASCREATE = 32000
|
||||
GASCALL = 40
|
||||
GASCALLVALUE = 9000
|
||||
GASCALLSTIPEND = 2300
|
||||
GASNEWACCOUNT = 25000
|
||||
GASEXP = 10
|
||||
GASEXPBYTE = 10
|
||||
GASMEMORY = 3
|
||||
GASTXCREATE = 32000
|
||||
GASTXDATAZERO = 4
|
||||
GASTXDATANONZERO = 68
|
||||
GASTX = 21000
|
||||
GASLOG = 375
|
||||
GASLOGDATA = 8
|
||||
GASLOGTOPIC = 375
|
||||
GASSHA3 = 30
|
||||
GASSHA3WORD = 6
|
||||
GASCOPY = 3
|
||||
GASBLOCKHASH = 20
|
||||
GASCODEDEPOSIT = 200
|
||||
GASMEMORYQUADRATICDENOMINATOR = 512
|
||||
GASSHA256 = 60
|
||||
GASSHA256WORD = 12
|
||||
GASRIPEMD160 = 600
|
||||
GASRIPEMD160WORD = 120
|
||||
GASIDENTITY = 15
|
||||
GASIDENTITYWORD = 3
|
||||
GASECRECOVER = 3000
|
||||
GASECADD = 500
|
||||
GASECMUL = 40000
|
||||
GASECPAIRINGBASE = 100000
|
||||
GASECPAIRINGPERPOINT = 80000
|
||||
GASLIMITEMADENOMINATOR = 1024
|
||||
GASLIMITADJUSTMENTFACTOR = 1024
|
||||
GASLIMITMINIMUM = 5000
|
||||
# GASLIMITMAXIMUM = 2 ^ 63 - 1
|
||||
GASLIMITUSAGEADJUSTMENTNUMERATOR = 3
|
||||
GASLIMITUSAGEADJUSTMENTDENOMINATOR = 2
|
||||
DIFFICULTYADJUSTMENTDENOMINATOR = 2048
|
||||
DIFFICULTYMINIMUM = 131072
|
||||
BOMBEXPONENTIALPERIOD = 100000
|
||||
# BOMBEXPONENTIALFREEPERIODS = 2
|
||||
# BLOCKREWARD = 5 * denoms.ether
|
||||
UNCLEDEPTHPENALTYFACTOR = 8
|
||||
MAXUNCLEDEPTH = 6
|
||||
MAXUNCLES = 2
|
||||
# SECPK1P = 2 ^ 256 - 2 ^ 32 - 977
|
||||
SECPK1N = 0
|
||||
SECPK1A = 0
|
||||
SECPK1B = 7
|
||||
SECPK1Gx = 0
|
||||
SECPK1Gy = 0
|
||||
SECPK1G = (SECPK1Gx, SECPK1Gy)
|
||||
EMPTYUNCLEHASH = cstring"\\x1d\\xccM\\xe8\\xde\\xc7]z\\xab\\x85\\xb5g\\xb6\\xcc\\xd4\\x1a\\xd3\\x12E\\x1b\\x94\\x8at\\x13\\xf0\\xa1B\\xfd@\\xd4\\x93G"
|
||||
GENESISBLOCKNUMBER = 0
|
||||
GENESISDIFFICULTY = 131072
|
||||
GENESISGASLIMIT = 3141592
|
||||
GENESISPARENTHASH = ZEROHASH32
|
||||
GENESISCOINBASE = ZEROADDRESS
|
||||
GENESISNONCE = cstring"\\x00\\x00\\x00\\x00\\x00\\x00\\x00B"
|
||||
GENESISMIXHASH = ZEROHASH32
|
||||
EMPTYSHA3 = cstring"\\xc5\\xd2F\\x01\\x86\\xf7#<\\x92~}\\xb2\\xdc\\xc7\\x03\\xc0\\xe5\\x00\\xb6S\\xca\\x82';{\\xfa\\xd8\\x04]\\x85\\xa4p"
|
||||
BLANKROOTHASH = cstring"V\\xe8\\x1f\\x17\\x1b\\xccU\\xa6\\xff\\x83E\\xe6\\x92\\xc0\\xf8n[H\\xe0\\x1b\\x99l\\xad\\xc0\\x01b/\\xb5\\xe3c\\xb4!"
|
||||
GASMODEXPQUADRATICDENOMINATOR = 20
|
||||
MAXPREVHEADERDEPTH = 256
|
71
src/errors.nim
Normal file
71
src/errors.nim
Normal file
@ -0,0 +1,71 @@
|
||||
type
|
||||
EVMError* = object of Exception
|
||||
## Base error class for all evm errors.
|
||||
|
||||
VMNotFound* = object of EVMError
|
||||
## No VM available for the provided block number.
|
||||
|
||||
BlockNotFound* = object of EVMError
|
||||
## The block with the given number/hash does not exist.
|
||||
|
||||
ParentNotFound* = object of EVMError
|
||||
## The parent of a given block does not exist.
|
||||
|
||||
CanonicalHeadNotFound* = object of EVMError
|
||||
## The chain has no canonical head.
|
||||
|
||||
ValidationError* = object of EVMError
|
||||
## Error to signal something does not pass a validation check.
|
||||
|
||||
Halt* = object of EVMError
|
||||
## Raised by opcode function to halt vm execution.
|
||||
|
||||
VMError* = object of EVMError
|
||||
## Class of errors which can be raised during VM execution.
|
||||
erasesReturnData*: bool
|
||||
burnsGas*: bool
|
||||
|
||||
OutOfGas* = object of VMError
|
||||
## Error signaling that VM execution has run out of gas.
|
||||
|
||||
InsufficientStack* = object of VMError
|
||||
## Error signaling that the stack is empty.
|
||||
|
||||
FullStack* = object of VMError
|
||||
## Error signaling that the stack is full.
|
||||
|
||||
InvalidJumpDestination* = object of VMError
|
||||
## Error signaling that the jump destination for a JUMPDEST operation is invalid.
|
||||
|
||||
InvalidInstruction* = object of VMError
|
||||
## Error signaling that an opcode is invalid.
|
||||
|
||||
InsufficientFunds* = object of VMError
|
||||
## Error signaling that an account has insufficient funds to transfer the
|
||||
## requested value.
|
||||
|
||||
StackDepthLimit* = object of VMError
|
||||
## Error signaling that the call stack has exceeded it's maximum allowed depth.
|
||||
|
||||
ContractCreationCollision* = object of VMError
|
||||
## Error signaling that there was an address collision during contract creation.
|
||||
|
||||
Revert* = object of VMError
|
||||
## Error used by the REVERT opcode
|
||||
|
||||
WriteProtection* = object of VMError
|
||||
## Error raised if an attempt to modify the state database is made while
|
||||
## operating inside of a STATICCALL context.
|
||||
|
||||
OutOfBoundsRead* = object of VMError
|
||||
## Error raised to indicate an attempt was made to read data beyond the
|
||||
## boundaries of the buffer (such as with RETURNDATACOPY)
|
||||
|
||||
proc makeVMError*(): VMError =
|
||||
result.burnsGas = true
|
||||
result.erasesReturnData = true
|
||||
|
||||
proc makeRevert*(): Revert =
|
||||
result.burnsGas = false
|
||||
result.erasesReturnData = false
|
||||
|
BIN
src/gdb.txt
Normal file
BIN
src/gdb.txt
Normal file
Binary file not shown.
1
src/line.csv
Normal file
1
src/line.csv
Normal file
@ -0,0 +1 @@
|
||||
|
|
14
src/logging.nim
Normal file
14
src/logging.nim
Normal file
@ -0,0 +1,14 @@
|
||||
import strformat
|
||||
|
||||
type
|
||||
Logger* = object
|
||||
name*: string
|
||||
|
||||
proc log*(l: Logger, msg: string) =
|
||||
echo fmt"#{l.name}: {msg}"
|
||||
|
||||
proc trace*(l: Logger, msg: string) =
|
||||
echo fmt"#{l.name}: {msg}"
|
||||
|
||||
proc getLogger*(name: string): Logger =
|
||||
result = Logger(name: name)
|
1
src/nim.cfg
Normal file
1
src/nim.cfg
Normal file
@ -0,0 +1 @@
|
||||
# --warning[XDeclaredButNotUsed]:off
|
135
src/opcode_values.nim
Normal file
135
src/opcode_values.nim
Normal file
@ -0,0 +1,135 @@
|
||||
const
|
||||
STOP* = 0
|
||||
ADD* = 1
|
||||
MUL* = 2
|
||||
SUB* = 3
|
||||
DIV* = 4
|
||||
SDIV* = 5
|
||||
MOD* = 6
|
||||
SMOD* = 7
|
||||
ADDMOD* = 8
|
||||
MULMOD* = 9
|
||||
EXP* = 10
|
||||
SIGNEXTEND* = 11
|
||||
LT* = 16
|
||||
GT* = 17
|
||||
SLT* = 18
|
||||
SGT* = 19
|
||||
EQ* = 20
|
||||
ISZERO* = 21
|
||||
AND* = 22
|
||||
OR* = 23
|
||||
XOR* = 24
|
||||
NOT* = 25
|
||||
BYTE* = 26
|
||||
SHA3* = 32
|
||||
ADDRESS* = 48
|
||||
BALANCE* = 49
|
||||
ORIGIN* = 50
|
||||
CALLER* = 51
|
||||
CALLVALUE* = 52
|
||||
CALLDATALOAD* = 53
|
||||
CALLDATASIZE* = 54
|
||||
CALLDATACOPY* = 55
|
||||
CODESIZE* = 56
|
||||
CODECOPY* = 57
|
||||
GASPRICE* = 58
|
||||
EXTCODESIZE* = 59
|
||||
EXTCODECOPY* = 60
|
||||
RETURNDATASIZE* = 61
|
||||
RETURNDATACOPY* = 62
|
||||
BLOCKHASH* = 64
|
||||
COINBASE* = 65
|
||||
TIMESTAMP* = 66
|
||||
NUMBER* = 67
|
||||
DIFFICULTY* = 68
|
||||
GASLIMIT* = 69
|
||||
POP* = 80
|
||||
MLOAD* = 81
|
||||
MSTORE* = 82
|
||||
MSTORE8 = 83
|
||||
SLOAD* = 84
|
||||
SSTORE* = 85
|
||||
JUMP* = 86
|
||||
JUMPI* = 87
|
||||
PC* = 88
|
||||
MSIZE* = 89
|
||||
GAS* = 90
|
||||
JUMPDEST* = 91
|
||||
PUSH1* = 96.byte
|
||||
PUSH2* = 97.byte
|
||||
PUSH3* = 98.byte
|
||||
PUSH4* = 99.byte
|
||||
PUSH5* = 100.byte
|
||||
PUSH6* = 101.byte
|
||||
PUSH7* = 102.byte
|
||||
PUSH8* = 103.byte
|
||||
PUSH9* = 104.byte
|
||||
PUSH10* = 105.byte
|
||||
PUSH11* = 106.byte
|
||||
PUSH12* = 107.byte
|
||||
PUSH13* = 108.byte
|
||||
PUSH14* = 109.byte
|
||||
PUSH15* = 110.byte
|
||||
PUSH16* = 111.byte
|
||||
PUSH17* = 112.byte
|
||||
PUSH18* = 113.byte
|
||||
PUSH19* = 114.byte
|
||||
PUSH20* = 115.byte
|
||||
PUSH21* = 116.byte
|
||||
PUSH22* = 117.byte
|
||||
PUSH23* = 118.byte
|
||||
PUSH24* = 119.byte
|
||||
PUSH25* = 120.byte
|
||||
PUSH26* = 121.byte
|
||||
PUSH27* = 122.byte
|
||||
PUSH28* = 123.byte
|
||||
PUSH29* = 124.byte
|
||||
PUSH30* = 125.byte
|
||||
PUSH31* = 126.byte
|
||||
PUSH32* = 127.byte
|
||||
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* = 240
|
||||
CALL* = 241
|
||||
CALLCODE* = 242
|
||||
RETURN* = 243
|
||||
DELEGATECALL* = 244
|
||||
STATICCALL* = 250
|
||||
REVERT* = 253
|
||||
SELFDESTRUCT* = 255
|
5
src/utils_numeric.nim
Normal file
5
src/utils_numeric.nim
Normal file
@ -0,0 +1,5 @@
|
||||
proc intToBigEndian*(value: int): cstring =
|
||||
result = cstring""
|
||||
|
||||
proc bigEndianToInt*(value: cstring): int =
|
||||
result = 0
|
18
src/validation.nim
Normal file
18
src/validation.nim
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
import
|
||||
strformat,
|
||||
errors, constants
|
||||
|
||||
proc validateCanonicalAddress*(value: cstring, title: string = "Value") =
|
||||
if len(value) != 20:
|
||||
raise newException(ValidationError,
|
||||
fmt"{title} {value} is not a valid canonical address")
|
||||
|
||||
|
||||
|
||||
|
||||
proc validateGte*(value: Int256, minimum: int, title: string = "Value") =
|
||||
if value < minimum.Int256:
|
||||
raise newException(ValidationError,
|
||||
fmt"{title} {value} is not greater than or equal to {minimum}")
|
90
src/vm/code_stream.nim
Normal file
90
src/vm/code_stream.nim
Normal file
@ -0,0 +1,90 @@
|
||||
import
|
||||
strformat, strutils, sequtils, sets,
|
||||
../logging, ../constants, ../opcode_values
|
||||
|
||||
# I don't see why would we wrap our in memory stream in something like BytesIO
|
||||
|
||||
type
|
||||
CodeStream* = ref object
|
||||
bytes: seq[byte]
|
||||
depthProcessed: int
|
||||
invalidPositions: HashSet[int]
|
||||
pc*: int
|
||||
logger: Logger
|
||||
|
||||
proc `$`*(b: byte): string =
|
||||
$(b.int)
|
||||
|
||||
proc newCodeStream*(codeBytes: cstring): CodeStream =
|
||||
new(result)
|
||||
result.bytes = codeBytes.mapIt(it.byte)
|
||||
result.pc = 0
|
||||
result.invalidPositions = initSet[int]()
|
||||
result.depthProcessed = 0
|
||||
result.logger = logging.getLogger("evm.vm.CodeStream")
|
||||
|
||||
proc read*(c: var CodeStream, size: int): seq[byte] =
|
||||
result = c.bytes[c.pc .. c.pc + size - 1]
|
||||
c.pc += size
|
||||
|
||||
proc len*(c: CodeStream): int =
|
||||
len(c.bytes)
|
||||
|
||||
proc next*(c: var CodeStream): byte =
|
||||
var nextOpcode = c.read(1)
|
||||
if nextOpcode[0] != 0x0.byte:
|
||||
return nextOpcode[0]
|
||||
else:
|
||||
return opcode_values.STOP
|
||||
|
||||
|
||||
iterator items*(c: var CodeStream): byte =
|
||||
var nextOpcode = c.next()
|
||||
while nextOpcode != opcode_values.STOP:
|
||||
yield nextOpcode
|
||||
nextOpcode = c.next()
|
||||
|
||||
proc `[]`*(c: CodeStream, offset: int): byte =
|
||||
c.bytes[offset]
|
||||
|
||||
proc peek*(c: var CodeStream): byte =
|
||||
var currentPc = c.pc
|
||||
result = c.next()
|
||||
c.pc = currentPc
|
||||
|
||||
proc updatePc*(c: var CodeStream, value: int) =
|
||||
c.pc = min(value, len(c))
|
||||
|
||||
template seek*(c: var CodeStream, pc: int, handler: untyped): untyped =
|
||||
var anchorPc = pc
|
||||
`c`.pc = pc
|
||||
try:
|
||||
var c = `c` {.inject.}
|
||||
`handler`
|
||||
finally:
|
||||
`c`.pc = anchorPc
|
||||
|
||||
proc isValidOpcode*(c: var CodeStream, position: int): bool =
|
||||
if position >= len(c):
|
||||
return false
|
||||
if position in c.invalidPositions:
|
||||
return false
|
||||
if position <= c.depthProcessed:
|
||||
return true
|
||||
else:
|
||||
var i = c.depthProcessed
|
||||
while i <= position:
|
||||
var opcode = c[i]
|
||||
if opcode >= opcode_values.PUSH1 and opcode <= opcode_values.PUSH32:
|
||||
var leftBound = (i + 1)
|
||||
var rightBound = leftBound + (opcode.int - 95)
|
||||
for z in leftBound ..< rightBound:
|
||||
c.invalidPositions.incl(z)
|
||||
i = rightBound
|
||||
else:
|
||||
c.depthProcessed = i
|
||||
i += 1
|
||||
if position in c.invalidPositions:
|
||||
return false
|
||||
else:
|
||||
return true
|
40
src/vm/gas_meter.nim
Normal file
40
src/vm/gas_meter.nim
Normal file
@ -0,0 +1,40 @@
|
||||
import
|
||||
strformat,
|
||||
../logging, ../errors, ../constants
|
||||
|
||||
type
|
||||
GasMeter* = ref object
|
||||
logger*: Logger
|
||||
gasRefunded*: Int256
|
||||
startGas*: Int256
|
||||
gasRemaining*: Int256
|
||||
|
||||
proc newGasMeter*(startGas: Int256): GasMeter =
|
||||
new(result)
|
||||
result.startGas = startGas
|
||||
result.gasRemaining = result.startGas
|
||||
result.gasRefunded = 0.Int256
|
||||
|
||||
proc consumeGas*(gasMeter: var GasMeter; amount: Int256; reason: string) =
|
||||
if amount < 0.Int256:
|
||||
raise newException(ValidationError, "Gas consumption amount must be positive")
|
||||
if amount > gasMeter.gasRemaining:
|
||||
raise newException(OutOfGas,
|
||||
%"Out of gas: Needed {amount} - Remaining {gasMeter.gasRemaining} - Reason: {reason}")
|
||||
gasMeter.gasRemaining -= amount
|
||||
gasMeter.logger.trace(
|
||||
%"GAS CONSUMPTION: {gasMeter.gasRemaining + amount} - {amount} -> {gasMeter.gasRemaining} ({reason})")
|
||||
|
||||
proc returnGas*(gasMeter: var GasMeter; amount: Int256) =
|
||||
if amount < 0.Int256:
|
||||
raise newException(ValidationError, "Gas return amount must be positive")
|
||||
gasMeter.gasRemaining += amount
|
||||
gasMeter.logger.trace(
|
||||
%"GAS RETURNED: {gasMeter.gasRemaining - amount} + {amount} -> {gasMeter.gasRemaining}")
|
||||
|
||||
proc refundGas*(gasMeter: var GasMeter; amount: Int256) =
|
||||
if amount < 0.Int256:
|
||||
raise newException(ValidationError, "Gas refund amount must be positive")
|
||||
gasMeter.gasRefunded += amount
|
||||
gasMeter.logger.trace(
|
||||
%"GAS REFUND: {gasMeter.gasRemaining - amount} + {amount} -> {gasMeter.gasRefunded}")
|
116
src/vm/message.nim
Normal file
116
src/vm/message.nim
Normal file
@ -0,0 +1,116 @@
|
||||
import
|
||||
../logging, ../constants, ../validation
|
||||
|
||||
type
|
||||
Message* = ref object
|
||||
# A message for VM computation
|
||||
|
||||
# depth = None
|
||||
|
||||
# code = None
|
||||
# codeAddress = None
|
||||
|
||||
# createAddress = None
|
||||
|
||||
# shouldTransferValue = None
|
||||
# isStatic = None
|
||||
|
||||
# logger = logging.getLogger("evm.vm.message.Message")
|
||||
|
||||
gas*: Int256
|
||||
gasPrice*: Int256
|
||||
to*: cstring
|
||||
sender*: cstring
|
||||
value*: Int256
|
||||
data*: cstring
|
||||
code*: cstring
|
||||
internalOrigin: cstring
|
||||
internalCodeAddress: cstring
|
||||
depth*: Int256
|
||||
internalStorageAddress: cstring
|
||||
shouldTransferValue*: bool
|
||||
isStatic*: bool
|
||||
|
||||
proc `origin=`*(message: var Message, value: cstring) =
|
||||
message.internalOrigin = value
|
||||
|
||||
proc `codeAddress=`*(message: var Message, value: cstring) =
|
||||
message.internalCodeAddress = value
|
||||
|
||||
proc `storageAddress=`*(message: var Message, value: cstring) =
|
||||
message.internalStorageAddress = value
|
||||
|
||||
proc newMessage*(
|
||||
gas: Int256,
|
||||
gasPrice: Int256,
|
||||
to: cstring,
|
||||
sender: cstring,
|
||||
value: Int256,
|
||||
data: cstring,
|
||||
code: cstring,
|
||||
origin: cstring = nil,
|
||||
depth: Int256 = 0.Int256,
|
||||
createAddress: cstring = nil,
|
||||
codeAddress: cstring = nil,
|
||||
shouldTransferValue: bool = true,
|
||||
isStatic: bool = false): Message =
|
||||
|
||||
new(result)
|
||||
result.gas = gas
|
||||
result.gasPrice = gasPrice
|
||||
|
||||
if to != CREATE_CONTRACT_ADDRESS:
|
||||
validateCanonicalAddress(to, title="Message.to")
|
||||
result.to = to
|
||||
|
||||
validateCanonicalAddress(sender, title="Message.sender")
|
||||
result.sender = sender
|
||||
|
||||
result.value = value
|
||||
|
||||
result.data = data
|
||||
|
||||
if not origin.isNil:
|
||||
validateCanonicalAddress(origin, title="Message.origin")
|
||||
result.internalOrigin = origin
|
||||
|
||||
validateGte(depth, minimum=0, title="Message.depth")
|
||||
result.depth = depth
|
||||
|
||||
result.code = code
|
||||
|
||||
if not createAddress.isNil:
|
||||
validateCanonicalAddress(createAddress, title="Message.storage_address")
|
||||
result.storageAddress = createAddress
|
||||
|
||||
if not codeAddress.isNil:
|
||||
validateCanonicalAddress(codeAddress, title="Message.code_address")
|
||||
result.codeAddress = codeAddress
|
||||
|
||||
result.shouldTransferValue = shouldTransferValue
|
||||
|
||||
result.isStatic = isStatic
|
||||
|
||||
proc origin*(message: Message): cstring =
|
||||
if not message.internalOrigin.isNil:
|
||||
message.internalOrigin
|
||||
else:
|
||||
message.sender
|
||||
|
||||
proc isOrigin*(message: Message): bool =
|
||||
message.sender == message.origin
|
||||
|
||||
proc codeAddress*(message: Message): cstring =
|
||||
if not message.internalCodeAddress.isNil:
|
||||
message.internalCodeAddress
|
||||
else:
|
||||
message.to
|
||||
|
||||
proc `storageAddress`*(message: Message): cstring =
|
||||
if not message.internalStorageAddress.isNil:
|
||||
message.internalStorageAddress
|
||||
else:
|
||||
message.to
|
||||
|
||||
proc isCreate(message: Message): bool =
|
||||
message.to == CREATE_CONTRACT_ADDRESS
|
123
src/vm/stack.nim
Normal file
123
src/vm/stack.nim
Normal file
@ -0,0 +1,123 @@
|
||||
|
||||
import
|
||||
strformat,
|
||||
value, ../errors, ../validation, ../utils_numeric, ../constants, ../logging
|
||||
|
||||
type
|
||||
|
||||
Stack* = ref object of RootObj
|
||||
## VM Stack
|
||||
logger*: Logger
|
||||
values*: seq[Value]
|
||||
|
||||
template ensureStackLimit: untyped =
|
||||
if len(stack.values) > 1023:
|
||||
raise newException(FullStack, "Stack limit reached")
|
||||
|
||||
method len*(stack: Stack): int =
|
||||
len(stack.values)
|
||||
|
||||
method push*(stack: var Stack; value: Value) =
|
||||
## Push an item onto the stack
|
||||
ensureStackLimit()
|
||||
|
||||
stack.values.add(value)
|
||||
|
||||
method push*(stack: var Stack; value: int) =
|
||||
## Push an integer onto the stack
|
||||
ensureStackLimit()
|
||||
|
||||
stack.values.add(Value(kind: VInt, i: value))
|
||||
|
||||
method push*(stack: var Stack; value: cstring) =
|
||||
## Push a binary onto the stack
|
||||
ensureStackLimit()
|
||||
|
||||
stack.values.add(Value(kind: VBinary, b: value))
|
||||
|
||||
method internalPop(stack: var Stack; numItems: int): seq[Value] =
|
||||
if len(stack) < numItems:
|
||||
result = @[]
|
||||
else:
|
||||
result = stack.values[^numItems .. ^1]
|
||||
stack.values = stack.values[0 ..< ^numItems]
|
||||
|
||||
template toType(i: int, _: typedesc[int]): int =
|
||||
i
|
||||
|
||||
template toType(i: int, _: typedesc[cstring]): cstring =
|
||||
intToBigEndian(i)
|
||||
|
||||
template toType(b: cstring, _: typedesc[int]): int =
|
||||
bigEndianToInt(b)
|
||||
|
||||
template toType(b: cstring, _: typedesc[cstring]): cstring =
|
||||
b
|
||||
|
||||
method internalPop(stack: var Stack; numItems: int, T: typedesc): seq[T] =
|
||||
result = @[]
|
||||
if len(stack) < numItems:
|
||||
return
|
||||
|
||||
for z in 0 ..< numItems:
|
||||
var value = stack.values.pop()
|
||||
case value.kind:
|
||||
of VInt:
|
||||
result.add(toType(value.i, T))
|
||||
of VBinary:
|
||||
result.add(toType(value.b, T))
|
||||
|
||||
template ensurePop(elements: untyped, a: untyped): untyped =
|
||||
if len(`elements`) < `a`:
|
||||
raise newException(InsufficientStack, "No stack items")
|
||||
|
||||
method pop*(stack: var Stack): Value =
|
||||
## Pop an item off the stack
|
||||
var elements = stack.internalPop(1)
|
||||
ensurePop(elements, 1)
|
||||
result = elements[0]
|
||||
|
||||
method pop*(stack: var Stack; numItems: int): seq[Value] =
|
||||
## Pop many items off the stack
|
||||
result = stack.internalPop(numItems)
|
||||
ensurePop(result, numItems)
|
||||
|
||||
method popInt*(stack: var Stack): int =
|
||||
var elements = stack.internalPop(1, int)
|
||||
ensurePop(elements, 1)
|
||||
result = elements[0]
|
||||
|
||||
method popInt*(stack: var Stack; numItems: int): seq[int] =
|
||||
result = stack.internalPop(numItems, int)
|
||||
ensurePop(result, numItems)
|
||||
|
||||
method popBinary*(stack: var Stack): cstring =
|
||||
var elements = stack.internalPop(1, cstring)
|
||||
ensurePop(elements, 1)
|
||||
result = elements[0]
|
||||
|
||||
method popBinary*(stack: var Stack; numItems: int): seq[cstring] =
|
||||
result = stack.internalPop(numItems, cstring)
|
||||
ensurePop(result, numItems)
|
||||
|
||||
proc makeStack*(): Stack =
|
||||
# result.logger = logging.getLogger("evm.vm.stack.Stack")
|
||||
result.values = @[]
|
||||
|
||||
method swap*(stack: var Stack; position: int) =
|
||||
## Perform a SWAP operation on the stack
|
||||
var idx = position + 1
|
||||
if idx < len(stack) + 1:
|
||||
(stack.values[^1], stack.values[^idx]) = (stack.values[^idx], stack.values[^1])
|
||||
else:
|
||||
raise newException(InsufficientStack,
|
||||
%"Insufficient stack items for SWAP{position}")
|
||||
|
||||
method dup*(stack: var Stack; position: int) =
|
||||
## Perform a DUP operation on the stack
|
||||
if position < len(stack) + 1:
|
||||
stack.push(stack.values[^position])
|
||||
else:
|
||||
raise newException(InsufficientStack,
|
||||
%"Insufficient stack items for DUP{position}")
|
||||
|
10
src/vm/value.nim
Normal file
10
src/vm/value.nim
Normal file
@ -0,0 +1,10 @@
|
||||
type
|
||||
ValueKind* = enum VInt, VBinary
|
||||
|
||||
Value* = ref object
|
||||
case kind*: ValueKind:
|
||||
of VInt:
|
||||
i*: int
|
||||
of VBinary:
|
||||
b*: cstring
|
||||
|
Loading…
x
Reference in New Issue
Block a user