Implemented most of the stubbed-out state handling instructions (#59)

Merge note: currently cannot compile due to `quasiBoolean` (#63). This will be solved by https://github.com/status-im/nimbus/pull/65
----

* Implemented most of the stubbed out state handling instructions

The code compiles, but still fails at the moment due to incorrect

initialization of the VM. Don't merge yet. More commits will be
pushed in the coming days.

* Fixed crash

* trie put and del are void now

* getBlockTransactionData and getReceipts

* Working code for extcodesize0.json

* fix origin.json

* fix calldatasize1

* fix calldataloadSizeTooHighPartial

* fix calldataloadSizeTooHigh

* more efficient PushX implementation

* fix and, or, xor
This commit is contained in:
zah 2018-07-05 15:41:01 +03:00 committed by Mamy Ratsimbazafy
parent 6f28d11866
commit 18b7bbb3b0
43 changed files with 518 additions and 393 deletions

View File

@ -115,7 +115,7 @@ VMTests
+ expPowerOf2_4.json OK + expPowerOf2_4.json OK
+ expPowerOf2_64.json OK + expPowerOf2_64.json OK
+ expPowerOf2_8.json OK + expPowerOf2_8.json OK
- expXY.json Fail + expXY.json OK
+ expXY_success.json OK + expXY_success.json OK
+ fibbonacci_unrolled.json OK + fibbonacci_unrolled.json OK
+ mod0.json OK + mod0.json OK
@ -150,12 +150,12 @@ VMTests
+ mulmoddivByZero2.json OK + mulmoddivByZero2.json OK
+ mulmoddivByZero3.json OK + mulmoddivByZero3.json OK
+ not1.json OK + not1.json OK
+ sdiv0.json OK - sdiv0.json Fail
+ sdiv1.json OK + sdiv1.json OK
+ sdiv2.json OK + sdiv2.json OK
+ sdiv3.json OK - sdiv3.json Fail
+ sdiv4.json OK - sdiv4.json Fail
+ sdiv5.json OK - sdiv5.json Fail
+ sdiv6.json OK + sdiv6.json OK
+ sdiv7.json OK + sdiv7.json OK
+ sdiv8.json OK + sdiv8.json OK
@ -164,8 +164,8 @@ VMTests
+ sdivByZero1.json OK + sdivByZero1.json OK
+ sdivByZero2.json OK + sdivByZero2.json OK
+ sdiv_dejavu.json OK + sdiv_dejavu.json OK
+ sdiv_i256min.json OK - sdiv_i256min.json Fail
+ sdiv_i256min2.json OK - sdiv_i256min2.json Fail
+ sdiv_i256min3.json OK + sdiv_i256min3.json OK
+ signextendInvalidByteNumber.json OK + signextendInvalidByteNumber.json OK
+ signextend_00.json OK + signextend_00.json OK
@ -180,9 +180,9 @@ VMTests
+ signextend_Overflow_dj42.json OK + 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 Fail
+ smod1.json OK - smod1.json Fail
+ smod2.json OK - smod2.json Fail
+ smod3.json OK + smod3.json OK
+ smod4.json OK + smod4.json OK
+ smod5.json OK + smod5.json OK
@ -190,7 +190,7 @@ VMTests
+ smod7.json OK + smod7.json OK
+ smod8_byZero.json OK + smod8_byZero.json OK
+ smod_i256min1.json OK + smod_i256min1.json OK
+ smod_i256min2.json OK - smod_i256min2.json Fail
+ stop.json OK + stop.json OK
+ sub0.json OK + sub0.json OK
+ sub1.json OK + sub1.json OK
@ -198,7 +198,7 @@ VMTests
+ sub3.json OK + sub3.json OK
+ sub4.json OK + sub4.json OK
``` ```
OK: 194/195 Fail: 1/195 Skip: 0/195 OK: 185/195 Fail: 10/195 Skip: 0/195
## vmBitwiseLogicOperation ## vmBitwiseLogicOperation
```diff ```diff
+ and0.json OK + and0.json OK
@ -267,7 +267,7 @@ OK: 60/60 Fail: 0/60 Skip: 0/60
```diff ```diff
+ blockhash257Block.json OK + blockhash257Block.json OK
+ blockhash258Block.json OK + blockhash258Block.json OK
+ blockhashInRange.json OK - blockhashInRange.json Fail
+ blockhashMyBlock.json OK + blockhashMyBlock.json OK
+ blockhashNotExistingBlock.json OK + blockhashNotExistingBlock.json OK
+ blockhashOutOfRange.json OK + blockhashOutOfRange.json OK
@ -278,21 +278,21 @@ OK: 60/60 Fail: 0/60 Skip: 0/60
+ number.json OK + number.json OK
+ timestamp.json OK + timestamp.json OK
``` ```
OK: 12/12 Fail: 0/12 Skip: 0/12 OK: 11/12 Fail: 1/12 Skip: 0/12
## vmEnvironmentalInfo ## vmEnvironmentalInfo
```diff ```diff
ExtCodeSizeAddressInputTooBigLeftMyAddress.json Skip + ExtCodeSizeAddressInputTooBigLeftMyAddress.json OK
ExtCodeSizeAddressInputTooBigRightMyAddress.json Skip + ExtCodeSizeAddressInputTooBigRightMyAddress.json OK
address0.json Skip + address0.json OK
address1.json Skip + address1.json OK
balance0.json Skip + balance0.json OK
balance01.json Skip - balance01.json Fail
balance1.json Skip - balance1.json Fail
balanceAddress2.json Skip + balanceAddress2.json OK
balanceAddressInputTooBig.json Skip + balanceAddressInputTooBig.json OK
balanceAddressInputTooBigLeftMyAddress.json Skip - balanceAddressInputTooBigLeftMyAddress.json Fail
balanceAddressInputTooBigRightMyAddress.json Skip balanceAddressInputTooBigRightMyAddress.json Skip
balanceCaller3.json Skip + balanceCaller3.json OK
calldatacopy0.json Skip calldatacopy0.json Skip
calldatacopy0_return.json Skip calldatacopy0_return.json Skip
calldatacopy1.json Skip calldatacopy1.json Skip
@ -307,41 +307,41 @@ OK: 12/12 Fail: 0/12 Skip: 0/12
calldatacopy_DataIndexTooHigh2_return.json Skip calldatacopy_DataIndexTooHigh2_return.json Skip
calldatacopy_DataIndexTooHigh_return.json Skip calldatacopy_DataIndexTooHigh_return.json Skip
calldatacopy_sec.json Skip calldatacopy_sec.json Skip
calldataload0.json Skip + calldataload0.json OK
calldataload1.json Skip + calldataload1.json OK
calldataload2.json Skip + calldataload2.json OK
calldataloadSizeTooHigh.json Skip + calldataloadSizeTooHigh.json OK
calldataloadSizeTooHighPartial.json Skip + calldataloadSizeTooHighPartial.json OK
calldataload_BigOffset.json Skip + calldataload_BigOffset.json OK
calldatasize0.json Skip + calldatasize0.json OK
calldatasize1.json Skip + calldatasize1.json OK
calldatasize2.json Skip + calldatasize2.json OK
caller.json Skip + caller.json OK
callvalue.json Skip + callvalue.json OK
codecopy0.json Skip - codecopy0.json Fail
codecopyZeroMemExpansion.json Skip + codecopyZeroMemExpansion.json OK
codecopy_DataIndexTooHigh.json Skip - codecopy_DataIndexTooHigh.json Fail
codesize.json Skip + codesize.json OK
env1.json Skip - env1.json Fail
extcodecopy0.json Skip - extcodecopy0.json Fail
extcodecopy0AddressTooBigLeft.json Skip - extcodecopy0AddressTooBigLeft.json Fail
extcodecopy0AddressTooBigRight.json Skip - extcodecopy0AddressTooBigRight.json Fail
extcodecopyZeroMemExpansion.json Skip - extcodecopyZeroMemExpansion.json Fail
extcodecopy_DataIndexTooHigh.json Skip - extcodecopy_DataIndexTooHigh.json Fail
extcodesize0.json Skip + extcodesize0.json OK
extcodesize1.json Skip + extcodesize1.json OK
extcodesizeUnderFlow.json Skip + extcodesizeUnderFlow.json OK
gasprice.json Skip + gasprice.json OK
origin.json Skip + origin.json OK
``` ```
OK: 0/52 Fail: 0/52 Skip: 52/52 OK: 26/52 Fail: 11/52 Skip: 15/52
## vmIOandFlowOperations ## vmIOandFlowOperations
```diff ```diff
+ BlockNumberDynamicJump0_AfterJumpdest.json OK + BlockNumberDynamicJump0_AfterJumpdest.json OK
+ BlockNumberDynamicJump0_AfterJumpdest3.json OK + BlockNumberDynamicJump0_AfterJumpdest3.json OK
+ BlockNumberDynamicJump0_foreverOutOfGas.json OK + BlockNumberDynamicJump0_foreverOutOfGas.json OK
- BlockNumberDynamicJump0_jumpdest0.json Fail + BlockNumberDynamicJump0_jumpdest0.json OK
- BlockNumberDynamicJump0_jumpdest2.json Fail + BlockNumberDynamicJump0_jumpdest2.json OK
+ BlockNumberDynamicJump0_withoutJumpdest.json OK + BlockNumberDynamicJump0_withoutJumpdest.json OK
+ BlockNumberDynamicJump1.json OK + BlockNumberDynamicJump1.json OK
+ BlockNumberDynamicJumpInsidePushWithJumpDest.json OK + BlockNumberDynamicJumpInsidePushWithJumpDest.json OK
@ -353,7 +353,7 @@ OK: 0/52 Fail: 0/52 Skip: 52/52
- BlockNumberDynamicJumpiOutsideBoundary.json Fail - BlockNumberDynamicJumpiOutsideBoundary.json Fail
+ BlockNumberDynamicJumpifInsidePushWithJumpDest.json OK + BlockNumberDynamicJumpifInsidePushWithJumpDest.json OK
+ BlockNumberDynamicJumpifInsidePushWithoutJumpDest.json OK + BlockNumberDynamicJumpifInsidePushWithoutJumpDest.json OK
- DyanmicJump0_outOfBoundary.json Fail + DyanmicJump0_outOfBoundary.json OK
+ DynamicJump0_AfterJumpdest.json OK + DynamicJump0_AfterJumpdest.json OK
+ DynamicJump0_AfterJumpdest3.json OK + DynamicJump0_AfterJumpdest3.json OK
+ DynamicJump0_foreverOutOfGas.json OK + DynamicJump0_foreverOutOfGas.json OK
@ -366,15 +366,15 @@ OK: 0/52 Fail: 0/52 Skip: 52/52
+ DynamicJumpInsidePushWithoutJumpDest.json OK + DynamicJumpInsidePushWithoutJumpDest.json OK
+ DynamicJumpJD_DependsOnJumps0.json OK + DynamicJumpJD_DependsOnJumps0.json OK
+ DynamicJumpJD_DependsOnJumps1.json OK + DynamicJumpJD_DependsOnJumps1.json OK
- DynamicJumpPathologicalTest0.json Fail + DynamicJumpPathologicalTest0.json OK
+ DynamicJumpPathologicalTest1.json OK + DynamicJumpPathologicalTest1.json OK
+ DynamicJumpPathologicalTest2.json OK + DynamicJumpPathologicalTest2.json OK
+ DynamicJumpPathologicalTest3.json OK + DynamicJumpPathologicalTest3.json OK
+ DynamicJumpStartWithJumpDest.json OK + DynamicJumpStartWithJumpDest.json OK
+ DynamicJump_value1.json OK - DynamicJump_value1.json Fail
+ DynamicJump_value2.json OK - DynamicJump_value2.json Fail
+ DynamicJump_value3.json OK - DynamicJump_value3.json Fail
+ DynamicJump_valueUnderflow.json OK - DynamicJump_valueUnderflow.json Fail
+ DynamicJumpi0.json OK + DynamicJumpi0.json OK
+ DynamicJumpi1.json OK + DynamicJumpi1.json OK
+ DynamicJumpi1_jumpdest.json OK + DynamicJumpi1_jumpdest.json OK
@ -382,22 +382,22 @@ OK: 0/52 Fail: 0/52 Skip: 52/52
- DynamicJumpiOutsideBoundary.json Fail - DynamicJumpiOutsideBoundary.json Fail
+ DynamicJumpifInsidePushWithJumpDest.json OK + DynamicJumpifInsidePushWithJumpDest.json OK
+ DynamicJumpifInsidePushWithoutJumpDest.json OK + DynamicJumpifInsidePushWithoutJumpDest.json OK
- JDfromStorageDynamicJump0_AfterJumpdest.json Fail + JDfromStorageDynamicJump0_AfterJumpdest.json OK
- JDfromStorageDynamicJump0_AfterJumpdest3.json Fail + JDfromStorageDynamicJump0_AfterJumpdest3.json OK
- JDfromStorageDynamicJump0_foreverOutOfGas.json Fail + JDfromStorageDynamicJump0_foreverOutOfGas.json OK
- JDfromStorageDynamicJump0_jumpdest0.json Fail - JDfromStorageDynamicJump0_jumpdest0.json Fail
- JDfromStorageDynamicJump0_jumpdest2.json Fail - JDfromStorageDynamicJump0_jumpdest2.json Fail
- JDfromStorageDynamicJump0_withoutJumpdest.json Fail + JDfromStorageDynamicJump0_withoutJumpdest.json OK
- JDfromStorageDynamicJump1.json Fail + JDfromStorageDynamicJump1.json OK
- JDfromStorageDynamicJumpInsidePushWithJumpDest.json Fail + JDfromStorageDynamicJumpInsidePushWithJumpDest.json OK
- JDfromStorageDynamicJumpInsidePushWithoutJumpDest.json Fail + JDfromStorageDynamicJumpInsidePushWithoutJumpDest.json OK
- JDfromStorageDynamicJumpi0.json Fail + JDfromStorageDynamicJumpi0.json OK
- JDfromStorageDynamicJumpi1.json Fail - JDfromStorageDynamicJumpi1.json Fail
- JDfromStorageDynamicJumpi1_jumpdest.json Fail + JDfromStorageDynamicJumpi1_jumpdest.json OK
- JDfromStorageDynamicJumpiAfterStop.json Fail - JDfromStorageDynamicJumpiAfterStop.json Fail
- JDfromStorageDynamicJumpiOutsideBoundary.json Fail + JDfromStorageDynamicJumpiOutsideBoundary.json OK
- JDfromStorageDynamicJumpifInsidePushWithJumpDest.json Fail + JDfromStorageDynamicJumpifInsidePushWithJumpDest.json OK
- JDfromStorageDynamicJumpifInsidePushWithoutJumpDest.json Fail + JDfromStorageDynamicJumpifInsidePushWithoutJumpDest.json OK
+ bad_indirect_jump1.json OK + bad_indirect_jump1.json OK
+ bad_indirect_jump2.json OK + bad_indirect_jump2.json OK
+ byte1.json OK + byte1.json OK
@ -461,9 +461,9 @@ OK: 0/52 Fail: 0/52 Skip: 52/52
+ mstore0.json OK + mstore0.json OK
+ mstore1.json OK + mstore1.json OK
+ mstore8MemExp.json OK + mstore8MemExp.json OK
+ mstore8WordToBigError.json OK - mstore8WordToBigError.json Fail
+ mstore8_0.json OK - mstore8_0.json Fail
+ mstore8_1.json OK - mstore8_1.json Fail
+ mstoreMemExp.json OK + mstoreMemExp.json OK
+ mstoreWordToBigError.json OK + mstoreWordToBigError.json OK
+ mstore_mload0.json OK + mstore_mload0.json OK
@ -475,15 +475,15 @@ OK: 0/52 Fail: 0/52 Skip: 52/52
- return2.json Fail - return2.json Fail
+ sha3MemExp.json OK + sha3MemExp.json OK
+ sstore_load_0.json OK + sstore_load_0.json OK
+ sstore_load_1.json OK - sstore_load_1.json Fail
- sstore_load_2.json Fail - sstore_load_2.json Fail
+ sstore_underflow.json OK + sstore_underflow.json OK
+ stack_loop.json OK - stack_loop.json Fail
+ stackjump1.json OK + stackjump1.json OK
+ swapAt52becameMstore.json OK + swapAt52becameMstore.json OK
+ when.json OK + when.json OK
``` ```
OK: 103/145 Fail: 41/145 Skip: 1/145 OK: 110/145 Fail: 34/145 Skip: 1/145
## vmLogTest ## vmLogTest
```diff ```diff
+ log0_emptyMem.json OK + log0_emptyMem.json OK
@ -614,7 +614,7 @@ OK: 0/18 Fail: 0/18 Skip: 18/18
+ push7.json OK + push7.json OK
+ push8.json OK + push8.json OK
+ push9.json OK + push9.json OK
+ swap1.json OK - swap1.json Fail
+ swap10.json OK + swap10.json OK
+ swap11.json OK + swap11.json OK
+ swap12.json OK + swap12.json OK
@ -631,9 +631,9 @@ OK: 0/18 Fail: 0/18 Skip: 18/18
+ swap7.json OK + swap7.json OK
+ swap8.json OK + swap8.json OK
+ swap9.json OK + swap9.json OK
+ swapjump1.json OK - swapjump1.json Fail
``` ```
OK: 74/74 Fail: 0/74 Skip: 0/74 OK: 72/74 Fail: 2/74 Skip: 0/74
## vmRandomTest ## vmRandomTest
```diff ```diff
201503102037PYTHON.json Skip 201503102037PYTHON.json Skip
@ -658,25 +658,25 @@ OK: 0/17 Fail: 0/17 Skip: 17/17
## vmSha3Test ## vmSha3Test
```diff ```diff
+ sha3_0.json OK + sha3_0.json OK
+ sha3_1.json OK - sha3_1.json Fail
+ sha3_2.json OK - sha3_2.json Fail
+ sha3_3.json OK + sha3_3.json OK
+ sha3_4.json OK + sha3_4.json OK
+ sha3_5.json OK + sha3_5.json OK
- sha3_6.json Fail - sha3_6.json Fail
- sha3_bigOffset.json Fail - sha3_bigOffset.json Fail
+ sha3_bigOffset2.json OK - sha3_bigOffset2.json Fail
- sha3_bigSize.json Fail - sha3_bigSize.json Fail
+ sha3_memSizeNoQuadraticCost31.json OK - sha3_memSizeNoQuadraticCost31.json Fail
+ sha3_memSizeQuadraticCost32.json OK - sha3_memSizeQuadraticCost32.json Fail
- sha3_memSizeQuadraticCost32_zeroSize.json Fail - sha3_memSizeQuadraticCost32_zeroSize.json Fail
+ sha3_memSizeQuadraticCost33.json OK - sha3_memSizeQuadraticCost33.json Fail
+ sha3_memSizeQuadraticCost63.json OK - sha3_memSizeQuadraticCost63.json Fail
+ sha3_memSizeQuadraticCost64.json OK - sha3_memSizeQuadraticCost64.json Fail
+ sha3_memSizeQuadraticCost64_2.json OK - sha3_memSizeQuadraticCost64_2.json Fail
+ sha3_memSizeQuadraticCost65.json OK - sha3_memSizeQuadraticCost65.json Fail
``` ```
OK: 14/18 Fail: 4/18 Skip: 0/18 OK: 4/18 Fail: 14/18 Skip: 0/18
## vmSystemOperations ## vmSystemOperations
```diff ```diff
ABAcalls0.json Skip ABAcalls0.json Skip

View File

@ -6,16 +6,18 @@
# 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, errors, stint, rlp, eth_common constants, errors, eth_common, rlp, eth_common/eth_types
type type
Account* = ref object Account* = object
nonce*: UInt256 nonce*: UInt256
balance*: UInt256 balance*: UInt256
storageRoot*: Hash256 storageRoot*: Hash256
codeHash*: Hash256 codeHash*: Hash256
rlpFields Account, nonce, balance
proc newAccount*(nonce: UInt256 = 0.u256, balance: UInt256 = 0.u256): Account = proc newAccount*(nonce: UInt256 = 0.u256, balance: UInt256 = 0.u256): Account =
Account(nonce: nonce, balance: balance) result.nonce = nonce
result.balance = balance
result.storageRoot = BLANK_ROOT_HASH
result.codeHash = EMPTY_SHA3

View File

@ -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
stint, eth_common, eth_common,
./logging, ./constants ./logging, ./constants
type type

View File

@ -1,6 +1,6 @@
import import
stint, math, strutils, utils/padding, eth_common math, strutils, utils/padding, eth_common
proc default(t: typedesc): t = discard proc default(t: typedesc): t = discard
@ -48,8 +48,8 @@ let
GENESIS_EXTRA_DATA* = "" GENESIS_EXTRA_DATA* = ""
GAS_LIMIT_MINIMUM* = 5000 GAS_LIMIT_MINIMUM* = 5000
EMPTYSHA3 = "\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p" BLANK_ROOT_HASH* = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest
BLANK_ROOT_HASH* = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest() EMPTY_SHA3* = "883f7328a6c30727a655daff17eba3a86049871bc7839a5b71e2bc26a99c4d4c".toDigest
GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.u256 GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.u256

View File

@ -5,13 +5,14 @@
# * 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 stint, tables, sequtils, algorithm, rlp, ranges, state_db, nimcrypto, import
../errors, ../block_types, ../utils/header, ../constants, eth_common, byteutils, tables, sequtils, algorithm,
./storage_types.nim, backends/memory_backend rlp, ranges, state_db, nimcrypto, eth_trie/[types, hexary], eth_common, byteutils,
../errors, ../block_types, ../utils/header, ../constants, ./storage_types.nim
type type
BaseChainDB* = ref object BaseChainDB* = ref object
db*: MemoryDB db*: TrieDatabaseRef
# TODO db*: JournalDB # TODO db*: JournalDB
KeyType = enum KeyType = enum
@ -22,13 +23,10 @@ type
blockNumber: BlockNumber blockNumber: BlockNumber
index: int index: int
proc newBaseChainDB*(db: MemoryDB): BaseChainDB = proc newBaseChainDB*(db: TrieDatabaseRef): BaseChainDB =
new(result) new(result)
result.db = db result.db = db
proc contains*(self: BaseChainDB; key: Hash256): bool =
return self.db.contains(genericHashKey(key))
proc `$`*(db: BaseChainDB): string = proc `$`*(db: BaseChainDB): string =
result = "BaseChainDB" result = "BaseChainDB"
@ -37,17 +35,17 @@ proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader =
## ##
## Raises BlockNotFound if it is not present in the db. ## Raises BlockNotFound if it is not present in the db.
try: try:
let blk = self.db.get(genericHashKey(blockHash)) let blk = self.db.get(genericHashKey(blockHash).toOpenArray).toRange
return decode(blk, BlockHeader) return decode(blk, BlockHeader)
except KeyError: except KeyError:
raise newException(BlockNotFound, "No block with hash " & blockHash.data.toHex) raise newException(BlockNotFound, "No block with hash " & blockHash.data.toHex)
proc getHash(self: BaseChainDB, key: DbKey): Hash256 {.inline.} = proc getHash(self: BaseChainDB, key: DbKey): Hash256 {.inline.} =
rlp.decode(self.db.get(key).toRange, Hash256) rlp.decode(self.db.get(key.toOpenArray).toRange, Hash256)
proc getCanonicalHead*(self: BaseChainDB): BlockHeader = proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
let k = canonicalHeadHashKey() let k = canonicalHeadHashKey()
if k notin self.db: if k.toOpenArray notin self.db:
raise newException(CanonicalHeadNotFound, raise newException(CanonicalHeadNotFound,
"No canonical head set for this chain") "No canonical head set for this chain")
return self.getBlockHeaderByHash(self.getHash(k)) return self.getBlockHeaderByHash(self.getHash(k))
@ -64,7 +62,7 @@ proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockH
self.getBlockHeaderByHash(self.lookupBlockHash(n)) self.getBlockHeaderByHash(self.lookupBlockHash(n))
proc getScore*(self: BaseChainDB; blockHash: Hash256): int = proc getScore*(self: BaseChainDB; blockHash: Hash256): int =
rlp.decode(self.db.get(blockHashToScoreKey(blockHash)).toRange, int) rlp.decode(self.db.get(blockHashToScoreKey(blockHash).toOpenArray).toRange, int)
iterator findNewAncestors(self: BaseChainDB; header: BlockHeader): BlockHeader = iterator findNewAncestors(self: BaseChainDB; header: BlockHeader): BlockHeader =
## Returns the chain leading up from the given header until the first ancestor it has in ## Returns the chain leading up from the given header until the first ancestor it has in
@ -86,7 +84,8 @@ iterator findNewAncestors(self: BaseChainDB; header: BlockHeader): BlockHeader =
h = self.getBlockHeaderByHash(h.parentHash) h = self.getBlockHeaderByHash(h.parentHash)
proc addBlockNumberToHashLookup(self: BaseChainDB; header: BlockHeader) = proc addBlockNumberToHashLookup(self: BaseChainDB; header: BlockHeader) =
self.db.set(blockNumberToHashKey(header.blockNumber), rlp.encode(header.hash)) self.db.put(blockNumberToHashKey(header.blockNumber).toOpenArray,
rlp.encode(header.hash).toOpenArray)
iterator getBlockTransactionHashes(self: BaseChainDB, blockHeader: BlockHeader): Hash256 = iterator getBlockTransactionHashes(self: BaseChainDB, blockHeader: BlockHeader): Hash256 =
## Returns an iterable of the transaction hashes from th block specified ## Returns an iterable of the transaction hashes from th block specified
@ -101,7 +100,7 @@ iterator getBlockTransactionHashes(self: BaseChainDB, blockHeader: BlockHeader):
proc removeTransactionFromCanonicalChain(self: BaseChainDB, transactionHash: Hash256) {.inline.} = proc removeTransactionFromCanonicalChain(self: BaseChainDB, transactionHash: Hash256) {.inline.} =
## Removes the transaction specified by the given hash from the canonical chain. ## Removes the transaction specified by the given hash from the canonical chain.
self.db.delete(transactionHashToBlockKey(transactionHash)) self.db.del(transactionHashToBlockKey(transactionHash).toOpenArray)
proc setAsCanonicalChainHead(self: BaseChainDB; headerHash: Hash256): seq[BlockHeader] = proc setAsCanonicalChainHead(self: BaseChainDB; headerHash: Hash256): seq[BlockHeader] =
## Sets the header as the canonical chain HEAD. ## Sets the header as the canonical chain HEAD.
@ -125,36 +124,36 @@ proc setAsCanonicalChainHead(self: BaseChainDB; headerHash: Hash256): seq[BlockH
for h in newCanonicalHeaders: for h in newCanonicalHeaders:
self.addBlockNumberToHashLookup(h) self.addBlockNumberToHashLookup(h)
self.db.set(canonicalHeadHashKey(), rlp.encode(header.hash)) self.db.put(canonicalHeadHashKey().toOpenArray, rlp.encode(header.hash).toOpenArray)
return newCanonicalHeaders return newCanonicalHeaders
proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool = proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool =
## Returns True if the header with the given block hash is in our DB. ## Returns True if the header with the given block hash is in our DB.
self.contains(blockHash) self.db.contains(blockHash.data)
iterator getBlockTransactionData(self: BaseChainDB, transactionRoot: Hash256): BytesRange = iterator getBlockTransactionData(self: BaseChainDB, transactionRoot: Hash256): BytesRange =
doAssert(false, "TODO: Implement me") var transactionDb = initHexaryTrie(self.db, transactionRoot)
# var transactionDb = HexaryTrie(self.db, transactionRoot) var transactionIdx = 0
# var transactionIdx = 0 while true:
# while true: let transactionKey = rlp.encode(transactionIdx).toRange
# var transactionKey = rlp.encode(transactionIdx) if transactionKey in transactionDb:
# if transactionKey in transactionDb: yield transactionDb.get(transactionKey)
# var transactionData = transactionDb[transactionKey] else:
# yield transactionDb[transactionKey] break
# else: inc transactionIdx
# break
# inc transactionIdx
iterator getReceipts*(self: BaseChainDB; header: BlockHeader; receiptClass: typedesc): Receipt =
# iterator getReceipts*(self: BaseChainDB; header: BlockHeader; receiptClass: typedesc): Receipt = var receiptDb = initHexaryTrie(self.db, header.receiptRoot)
# var receiptDb = HexaryTrie() var receiptIdx = 0
# for receiptIdx in itertools.count(): while true:
# var receiptKey = rlp.encode(receiptIdx) let receiptKey = rlp.encode(receiptIdx).toRange
# if receiptKey in receiptDb: if receiptKey in receiptDb:
# var receiptData = receiptDb[receiptKey] let receiptData = receiptDb.get(receiptKey)
# yield rlp.decode(receiptData) yield rlp.decode(receiptData, receiptClass)
# else: else:
# break break
inc receiptIdx
iterator getBlockTransactions(self: BaseChainDB; transactionRoot: Hash256; iterator getBlockTransactions(self: BaseChainDB; transactionRoot: Hash256;
transactionClass: typedesc): transactionClass = transactionClass: typedesc): transactionClass =
@ -166,10 +165,12 @@ proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader
if not isGenesis and not self.headerExists(header.parentHash): if not isGenesis and not self.headerExists(header.parentHash):
raise newException(ParentNotFound, "Cannot persist block header " & raise newException(ParentNotFound, "Cannot persist block header " &
$header.hash & " with unknown parent " & $header.parentHash) $header.hash & " with unknown parent " & $header.parentHash)
self.db.set(genericHashKey(header.hash), rlp.encode(header)) self.db.put(genericHashKey(header.hash).toOpenArray, rlp.encode(header).toOpenArray)
let score = if isGenesis: header.difficulty let score = if isGenesis: header.difficulty
else: self.getScore(header.parentHash).u256 + header.difficulty else: self.getScore(header.parentHash).u256 + header.difficulty
self.db.set(blockHashToScoreKey(header.hash), rlp.encode(score)) self.db.put(blockHashToScoreKey(header.hash).toOpenArray, rlp.encode(score).toOpenArray)
var headScore: int var headScore: int
try: try:
headScore = self.getScore(self.getCanonicalHead().hash) headScore = self.getScore(self.getCanonicalHead().hash)
@ -179,18 +180,17 @@ proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader
if score > headScore.u256: if score > headScore.u256:
result = self.setAsCanonicalChainHead(header.hash) result = self.setAsCanonicalChainHead(header.hash)
proc addTransactionToCanonicalChain(self: BaseChainDB, txHash: Hash256, proc addTransactionToCanonicalChain(self: BaseChainDB, txHash: Hash256,
blockHeader: BlockHeader, index: int) = blockHeader: BlockHeader, index: int) =
let k: TransactionKey = (blockHeader.blockNumber, index) let k: TransactionKey = (blockHeader.blockNumber, index)
self.db.set(transactionHashToBlockKey(txHash), rlp.encode(k)) self.db.put(transactionHashToBlockKey(txHash).toOpenArray, rlp.encode(k).toOpenArray)
proc persistUncles*(self: BaseChainDB, uncles: openarray[BlockHeader]): Hash256 = proc persistUncles*(self: BaseChainDB, uncles: openarray[BlockHeader]): Hash256 =
## Persists the list of uncles to the database. ## Persists the list of uncles to the database.
## Returns the uncles hash. ## Returns the uncles hash.
let enc = rlp.encode(uncles) let enc = rlp.encode(uncles)
result = keccak256.digest(enc.toOpenArray()) result = keccak256.digest(enc.toOpenArray())
self.db.set(genericHashKey(result), enc) self.db.put(genericHashKey(result).toOpenArray, enc.toOpenArray)
proc persistBlockToDb*(self: BaseChainDB; blk: Block) = proc persistBlockToDb*(self: BaseChainDB; blk: Block) =
## Persist the given block's header and uncles. ## Persist the given block's header and uncles.
@ -227,6 +227,5 @@ proc persistBlockToDb*(self: BaseChainDB; blk: Block) =
# proc clear*(self: BaseChainDB): void = # proc clear*(self: BaseChainDB): void =
# self.db.clear() # self.db.clear()
method getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB = proc getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB =
# TODO result = newAccountStateDB(self.db, stateRoot)
result = newAccountStateDB(initTable[string, string]())

View File

@ -6,33 +6,44 @@
# 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
eth_common, tables, nimcrypto, stint, rlp, sequtils, tables,
eth_common, nimcrypto, rlp, eth_trie/[hexary, memdb],
../constants, ../errors, ../validation, ../account, ../logging ../constants, ../errors, ../validation, ../account, ../logging
type type
AccountStateDB* = ref object AccountStateDB* = ref object
db*: Table[string, BytesRange] trie: HexaryTrie
rootHash*: Hash256 # TODO trie
proc newAccountStateDB*(db: Table[string, string], readOnly: bool = false): AccountStateDB = proc rootHash*(accountDb: AccountStateDB): KeccakHash =
result = AccountStateDB(db: initTable[string, BytesRange]()) accountDb.trie.rootHash
# proc `rootHash=`*(db: var AccountStateDB, value: string) =
# TODO: self.Trie.rootHash = value
proc newAccountStateDB*(backingStore: TrieDatabaseRef,
root: KeccakHash, readOnly: bool = false): AccountStateDB =
result.new()
result.trie = initHexaryTrie(backingStore, root)
proc logger*(db: AccountStateDB): Logger = proc logger*(db: AccountStateDB): Logger =
logging.getLogger("db.State") logging.getLogger("db.State")
template createRangeFromAddress(address: EthAddress): ByteRange =
## XXX: The name of this proc is intentionally long, because it
## performs a memory allocation and data copying that may be eliminated
## in the future. Avoid renaming it to something similar as `toRange`, so
## it can remain searchable in the code.
toRange(@address)
proc getAccount(db: AccountStateDB, address: EthAddress): Account = proc getAccount(db: AccountStateDB, address: EthAddress): Account =
# let rlpAccount = db.trie[address] let recordFound = db.trie.get(createRangeFromAddress address)
# if not rlpAccount.isNil: if recordFound.len > 0:
# account = rlp.decode[Account](rlpAccount) result = rlp.decode(recordFound, Account)
# account.mutable = true else:
# else: result = newAccount()
# account = newAccount()
result = newAccount() # TODO
proc setAccount(db: AccountStateDB, address: EthAddress, account: Account) = proc setAccount(db: AccountStateDB, address: EthAddress, account: Account) =
# db.trie[address] = rlp.encode[Account](account) db.trie.put createRangeFromAddress(address), rlp.encode(account)
discard # TODO
proc getCodeHash*(db: AccountStateDB, address: EthAddress): Hash256 = proc getCodeHash*(db: AccountStateDB, address: EthAddress): Hash256 =
validateCanonicalAddress(address, title="Storage Address") validateCanonicalAddress(address, title="Storage Address")
@ -46,51 +57,55 @@ proc getBalance*(db: AccountStateDB, address: EthAddress): UInt256 =
proc setBalance*(db: var AccountStateDB, address: EthAddress, balance: UInt256) = proc setBalance*(db: var AccountStateDB, address: EthAddress, balance: UInt256) =
validateCanonicalAddress(address, title="Storage Address") validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address) var account = db.getAccount(address)
account.balance = balance account.balance = balance
db.setAccount(address, account) db.setAccount(address, account)
proc deltaBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) = proc deltaBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) =
db.setBalance(address, db.getBalance(address) + delta) db.setBalance(address, db.getBalance(address) + delta)
template createTrieKeyFromSlot(slot: UInt256): ByteRange =
# XXX: This is too expensive. Similar to `createRangeFromAddress`
# Converts a number to hex big-endian representation including
# prefix and leading zeros:
@(keccak256.digest(slot.toByteArrayBE).data).toRange
# Original py-evm code:
# pad32(int_to_big_endian(slot))
proc setStorage*(db: var AccountStateDB, address: EthAddress, slot: UInt256, value: UInt256) = proc setStorage*(db: var AccountStateDB,
address: EthAddress,
slot: UInt256, value: UInt256) =
#validateGte(value, 0, title="Storage Value") #validateGte(value, 0, title="Storage Value")
#validateGte(slot, 0, title="Storage Slot") #validateGte(slot, 0, title="Storage Slot")
validateCanonicalAddress(address, title="Storage Address") validateCanonicalAddress(address, title="Storage Address")
# TODO var account = db.getAccount(address)
# let account = db.getAccount(address) var accountTrie = initHexaryTrie(db.trie.db, account.storageRoot)
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot)) let slotAsKey = createTrieKeyFromSlot slot
let slotAsKey = "0x" & slot.dumpHex # Convert to a number to hex big-endian representation including prefix and leading zeros
var storage = db.db
# TODO fix
if value > 0: if value > 0:
let encodedValue = rlp.encode value.toByteArrayBE let encodedValue = rlp.encode value
storage[slotAsKey] = encodedValue accountTrie.put(slotAsKey, encodedValue)
else: else:
storage.del(slotAsKey) accountTrie.del(slotAsKey)
#storage[slotAsKey] = value
# account.storageRoot = storage.rootHash
# db.setAccount(address, account)
proc getStorage*(db: var AccountStateDB, address: EthAddress, slot: UInt256): (UInt256, bool) = account.storageRoot = accountTrie.rootHash
db.setAccount(address, account)
proc getStorage*(db: AccountStateDB, address: EthAddress, slot: UInt256): (UInt256, bool) =
validateCanonicalAddress(address, title="Storage Address") validateCanonicalAddress(address, title="Storage Address")
#validateGte(slot, 0, title="Storage Slot") #validateGte(slot, 0, title="Storage Slot")
# TODO let
# make it more correct account = db.getAccount(address)
# for now, we just use a table slotAsKey = createTrieKeyFromSlot slot
accountTrie = initHexaryTrie(db.trie.db, account.storageRoot)
# let account = db.GetAccount(address) let
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot)) foundRecord = accountTrie.get(slotAsKey)
let slotAsKey = "0x" & slot.dumpHex # Convert to a number to hex big-endian representation including prefix and leading zeros if foundRecord.len > 0:
var storage = db.db result = (rlp.decode(foundRecord, UInt256), true)
if storage.hasKey(slotAsKey):
let byteRange = storage[slotAsKey]
result = (readUintBE[256](byteRange.toOpenArray), true)
else: else:
result = (0.u256, false) result = (0.u256, false)
@ -109,24 +124,21 @@ proc getNonce*(db: AccountStateDB, address: EthAddress): UInt256 =
let account = db.getAccount(address) let account = db.getAccount(address)
return account.nonce return account.nonce
proc setCode*(db: var AccountStateDB, address: EthAddress, code: string) = proc toByteRange_Unnecessary*(h: KeccakHash): ByteRange =
## XXX: Another proc used to mark unnecessary conversions it the code
var s = @(h.data)
return s.toRange
proc setCode*(db: var AccountStateDB, address: EthAddress, code: ByteRange) =
validateCanonicalAddress(address, title="Storage Address") validateCanonicalAddress(address, title="Storage Address")
var account = db.getAccount(address) var account = db.getAccount(address)
account.codeHash = keccak256.digest code.toOpenArray
account.codeHash = keccak256.digest code # XXX: this uses the journaldb in py-evm
#db.db[account.codeHash] = code db.trie.put(account.codeHash.toByteRange_Unnecessary, code)
db.setAccount(address, account) db.setAccount(address, account)
proc getCode*(db: var AccountStateDB, address: EthAddress): string = proc getCode*(db: AccountStateDB, address: EthAddress): ByteRange =
let codeHash = db.getCodeHash(address) let codeHash = db.getCodeHash(address)
#if db.db.hasKey(codeHash): result = db.trie.get(codeHash.toByteRange_Unnecessary)
# result = db.db[codeHash]
#else:
result = ""
# proc rootHash*(db: AccountStateDB): string =
# TODO return self.Trie.rootHash
# proc `rootHash=`*(db: var AccountStateDB, value: string) =
# TODO: self.Trie.rootHash = value

View File

@ -11,8 +11,8 @@ type
DbKey* = object DbKey* = object
# The first byte stores the key type. The rest are key-specific values # The first byte stores the key type. The rest are key-specific values
data: array[33, byte] data*: array[33, byte]
dataEndPos: uint8 # the last populated position in the data dataEndPos*: uint8 # the last populated position in the data
StorageError* = object of Exception StorageError* = object of Exception

View File

@ -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
constants, stint, errors, eth_common constants, errors, eth_common
type type
BaseTransaction* = ref object BaseTransaction* = ref object

View File

@ -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 eth_common, ../constants, stint, strformat, times, ../validation, rlp import eth_common, ../constants, strformat, times, ../validation, rlp
export BlockHeader export BlockHeader

View File

@ -7,7 +7,7 @@
import import
strformat, strformat,
errors, constants, stint, eth_common errors, constants, eth_common
template validateCanonicalAddress*(value: EthAddress, title: string = "Value") = template validateCanonicalAddress*(value: EthAddress, title: string = "Value") =
# TODO: This needs to be removed # TODO: This needs to be removed

View File

@ -7,6 +7,7 @@
import import
strformat, strutils, sequtils, parseutils, sets, macros, strformat, strutils, sequtils, parseutils, sets, macros,
eth_common,
../logging, ../constants, ./interpreter/opcode_values ../logging, ../constants, ./interpreter/opcode_values
type type
@ -51,6 +52,17 @@ proc read*(c: var CodeStream, size: int): seq[byte] =
result = @[] result = @[]
c.pc = c.bytes.len c.pc = c.bytes.len
proc readVmWord*(c: var CodeStream, n: int): UInt256 =
## Reads `n` bytes bytes from the code stream and pads
## the remaining bytes with zeros.
let result_bytes = cast[ptr array[32, byte]](addr result)
let last = min(c.pc + n, c.bytes.len)
let toWrite = last - c.pc
for i in 0 ..< toWrite : result_bytes[i] = c.bytes[last - i - 1]
for j in toWrite ..< 32: result_bytes[j] = 0
c.pc = last
proc len*(c: CodeStream): int = proc len*(c: CodeStream): int =
len(c.bytes) len(c.bytes)
@ -78,16 +90,15 @@ proc peek*(c: var CodeStream): Op =
proc updatePc*(c: var CodeStream, value: int) = proc updatePc*(c: var CodeStream, value: int) =
c.pc = min(value, len(c)) c.pc = min(value, len(c))
macro seek*(c: var CodeStream, pc: int, handler: untyped): untyped = when false:
let c2 = ident("c") template seek*(cs: var CodeStream, pc: int, handler: untyped): untyped =
result = quote: var anchorPc = cs.pc
var anchorPc = `c`.pc cs.pc = pc
`c`.pc = `pc`
try: try:
var `c2` = `c` var c {.inject.} = cs
`handler` handler
finally: finally:
`c`.pc = anchorPc cs.pc = anchorPc
proc isValidOpcode*(c: var CodeStream, position: int): bool = proc isValidOpcode*(c: var CodeStream, position: int): bool =
if position >= len(c): if position >= len(c):

View File

@ -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
strformat, strutils, sequtils, macros, stint, terminal, math, eth_common, byteutils, tables, strformat, strutils, sequtils, macros, terminal, math, tables,
eth_common, byteutils,
../constants, ../errors, ../validation, ../vm_state, ../logging, ../vm_types, ../constants, ../errors, ../validation, ../vm_state, ../logging, ../vm_types,
./interpreter/[opcode_values,gas_meter, gas_costs], ./interpreter/[opcode_values,gas_meter, gas_costs],
./code_stream, ./memory, ./message, ./stack, ./code_stream, ./memory, ./message, ./stack,

View File

@ -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
strformat, stint, strformat,
eth_common/eth_types,
../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header ../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header
proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransaction) = proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransaction) =

View File

@ -6,13 +6,12 @@
# 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, eth_common/eth_types, eth_common/eth_types,
../../../logging, ../../../constants, ../../../errors, ../../../logging, ../../../constants, ../../../errors,
../../../block_types, ../../../block_types,
../../../vm/[base, stack], ../../../db/db_chain, ../../../utils/header, ../../../vm/[base, stack], ../../../db/db_chain, ../../../utils/header,
./frontier_block, ./frontier_vm_state, ./frontier_validation ./frontier_block, ./frontier_vm_state, ./frontier_validation
type type
FrontierVM* = ref object of VM FrontierVM* = ref object of VM
@ -34,4 +33,6 @@ proc newFrontierVM*(header: BlockHeader, chainDB: BaseChainDB): FrontierVM =
result.chainDB = chainDB result.chainDB = chainDB
result.isStateless = true result.isStateless = true
result.state = newFrontierVMState() result.state = newFrontierVMState()
result.state.chaindb = result.chainDB
result.state.blockHeader = header
result.`block` = makeFrontierBlock(header, @[]) result.`block` = makeFrontierBlock(header, @[])

View File

@ -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
strformat, stint, strformat,
eth_common/eth_types,
../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header ../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header
proc validateTangerineTransaction*(vmState: BaseVmState, transaction: BaseTransaction) = proc validateTangerineTransaction*(vmState: BaseVmState, transaction: BaseTransaction) =

View File

@ -6,8 +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
eth_common/eth_types,
../../../logging, ../../../constants, ../../../errors, ../../../logging, ../../../constants, ../../../errors,
stint,
../../../block_types, ../../../block_types,
../../../vm/[base, stack], ../../../db/db_chain, ../../../utils/header, ../../../vm/[base, stack], ../../../db/db_chain, ../../../utils/header,
./tangerine_block, ./tangerine_vm_state, ./tangerine_validation ./tangerine_block, ./tangerine_vm_state, ./tangerine_validation
@ -34,4 +34,6 @@ proc newTangerineVM*(header: BlockHeader, chainDB: BaseChainDB): TangerineVM =
result.chainDB = chainDB result.chainDB = chainDB
result.isStateless = true result.isStateless = true
result.state = newTangerineVMState() result.state = newTangerineVMState()
result.state.chaindb = result.chainDB
result.state.blockHeader = header
result.`block` = makeTangerineBlock(header, @[]) result.`block` = makeTangerineBlock(header, @[])

View File

@ -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
stint, math, eth_common, # GasInt math, eth_common/eth_types,
../utils/[macros_gen_opcodes, utils_numeric], ../utils/[macros_gen_opcodes, utils_numeric],
./opcode_values ./opcode_values

View File

@ -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
tables, stint, tables, eth_common/eth_types,
../../vm_types, ./opcode_values, ../../vm_types, ./opcode_values,
opcodes_impl/[arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops] opcodes_impl/[arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops]
@ -51,8 +51,8 @@ const
CallDataLoad: callDataLoad, CallDataLoad: callDataLoad,
CallDataSize: callDataSize, CallDataSize: callDataSize,
CallDataCopy: callDataCopy, CallDataCopy: callDataCopy,
CodeSize: codesize, CodeSize: codeSize,
CodeCopy: codecopy, CodeCopy: codeCopy,
GasPrice: gasPrice, # TODO this wasn't used previously GasPrice: gasPrice, # TODO this wasn't used previously
ExtCodeSize: extCodeSize, ExtCodeSize: extCodeSize,
ExtCodeCopy: extCodeCopy, ExtCodeCopy: extCodeCopy,

View File

@ -6,7 +6,9 @@
# 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
helpers, stint, strutils, ./impl_std_import strutils,
eth_common/eth_types,
helpers, ./impl_std_import
proc add*(computation: var BaseComputation) = proc add*(computation: var BaseComputation) =
# Addition # Addition

View File

@ -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/stack, ../vm_state, stint eth_common/eth_types,
../constants, ../computation, ../vm/stack, ../vm_state
proc blockhash*(computation: var BaseComputation) = proc blockhash*(computation: var BaseComputation) =
var blockNumber = computation.stack.popInt() var blockNumber = computation.stack.popInt()

View File

@ -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, eth_common, stint, strformat, eth_common,
# ./impl_std_import # Cannot do that due to recursive dependencies # ./impl_std_import # Cannot do that due to recursive dependencies
# .../vm/interpreter/opcodes_impl/impl_std_import.nim imports .../vm/computation.nim # .../vm/interpreter/opcodes_impl/impl_std_import.nim imports .../vm/computation.nim
# .../vm/computation.nim imports .../vm/interpreter/opcodes_impl/call.nim # .../vm/computation.nim imports .../vm/interpreter/opcodes_impl/call.nim

View File

@ -18,11 +18,17 @@ quasiBoolean(sgt, `>`, signed=true) # Signed Greater Comparison
quasiBoolean(eq, `==`) # Equality quasiBoolean(eq, `==`) # Equality
quasiBoolean(andOp, `and`, nonzero=true) # Bitwise And proc andOp*(computation: var BaseComputation) =
let (lhs, rhs) = computation.stack.popInt(2)
computation.stack.push(lhs and rhs)
quasiBoolean(orOp, `or`, nonzero=true) # Bitwise Or proc orOp*(computation: var BaseComputation) =
let (lhs, rhs) = computation.stack.popInt(2)
computation.stack.push(lhs or rhs)
quasiBoolean(xorOp, `xor`, nonzero=true) # Bitwise XOr proc xorOp*(computation: var BaseComputation) =
let (lhs, rhs) = computation.stack.popInt(2)
computation.stack.push(lhs xor rhs)
# TODO use isZero from Stint # TODO use isZero from Stint
proc iszero*(computation: var BaseComputation) = proc iszero*(computation: var BaseComputation) =

View File

@ -7,14 +7,14 @@
import import
strformat, strformat,
./impl_std_import ranges/typedranges,
./impl_std_import,
../../../db/state_db
proc balance*(computation: var BaseComputation) = proc balance*(computation: var BaseComputation) =
let address = computation.stack.popAddress() let address = computation.stack.popAddress()
var balance: Int256 let balance = computation.vmState.readOnlyStateDB.getBalance(address)
# TODO computation.vmState.stateDB(read_only=True): computation.stack.push balance
# balance = db.getBalance(address)
# computation.stack.push(balance)
proc origin*(computation: var BaseComputation) = proc origin*(computation: var BaseComputation) =
computation.stack.push(computation.msg.origin) computation.stack.push(computation.msg.origin)
@ -25,18 +25,53 @@ proc address*(computation: var BaseComputation) =
proc caller*(computation: var BaseComputation) = proc caller*(computation: var BaseComputation) =
computation.stack.push(computation.msg.sender) computation.stack.push(computation.msg.sender)
proc callValue*(computation: var BaseComputation) = proc callValue*(computation: var BaseComputation) =
computation.stack.push(computation.msg.value) computation.stack.push(computation.msg.value)
proc writePaddedResult(mem: var Memory,
data: openarray[byte],
memPos, dataPos, len: Natural,
paddingValue = 0.byte) =
mem.extend(memPos, len)
let dataEndPosition = dataPos + len - 1
if dataEndPosition < data.len:
mem.write(memPos, data[dataPos .. dataEndPosition])
else:
var presentElements = data.len - dataPos
if presentElements > 0:
mem.write(memPos, data.toOpenArray(dataPos, data.len - 1))
else:
presentElements = 0
mem.writePaddingBytes(memPos + presentElements,
len - presentElements,
paddingValue)
proc callDataLoad*(computation: var BaseComputation) = proc callDataLoad*(computation: var BaseComputation) =
# Load call data into memory # Load call data into memory
let startPosition = computation.stack.popInt.toInt let origDataPos = computation.stack.popInt
let value = computation.msg.data[startPosition ..< startPosition + 32] if origDataPos >= computation.msg.data.len:
let paddedValue = padRight(value, 32, 0.byte) computation.stack.push(0)
let normalizedValue = paddedValue.lStrip(0.byte) return
computation.stack.push(normalizedValue)
let
dataPos = origDataPos.toInt
dataEndPosition = dataPos + 31
if dataEndPosition < computation.msg.data.len:
computation.stack.push(computation.msg.data[dataPos .. dataEndPosition])
else:
var bytes: array[32, byte]
var presentBytes = min(computation.msg.data.len - dataPos, 32)
if presentBytes > 0:
copyMem(addr bytes[0], addr computation.msg.data[dataPos], presentBytes)
else:
presentBytes = 0
for i in presentBytes ..< 32: bytes[i] = 0
computation.stack.push(bytes)
proc callDataSize*(computation: var BaseComputation) = proc callDataSize*(computation: var BaseComputation) =
let size = computation.msg.data.len.u256 let size = computation.msg.data.len.u256
@ -52,19 +87,15 @@ proc callDataCopy*(computation: var BaseComputation) =
reason="CALLDATACOPY fee") reason="CALLDATACOPY fee")
let (memPos, callPos, len) = (memStartPosition.toInt, calldataStartPosition.toInt, size.toInt) let (memPos, callPos, len) = (memStartPosition.toInt, calldataStartPosition.toInt, size.toInt)
computation.memory.extend(memPos, len)
let value = computation.msg.data[callPos ..< callPos + len] computation.memory.writePaddedResult(computation.msg.data,
let paddedValue = padRight(value, len, 0.byte) memPos, callPos, len)
computation.memory.write(memPos, paddedValue)
proc codeSize*(computation: var BaseComputation) =
proc codesize*(computation: var BaseComputation) =
let size = computation.code.len.u256 let size = computation.code.len.u256
computation.stack.push(size) computation.stack.push(size)
proc codeCopy*(computation: var BaseComputation) =
proc codecopy*(computation: var BaseComputation) =
let (memStartPosition, let (memStartPosition,
codeStartPosition, codeStartPosition,
size) = computation.stack.popInt(3) size) = computation.stack.popInt(3)
@ -74,26 +105,16 @@ proc codecopy*(computation: var BaseComputation) =
reason="CODECOPY: word gas cost") 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.memory.extend(memPos, len)
# TODO computation.memory.writePaddedResult(computation.code.bytes, memPos, codePos, len)
# with computation.code.seek(code_start_position):
# code_bytes = computation.code.read(size)
# padded_code_bytes = pad_right(code_bytes, size, b'\x00')
# computation.memory.write(mem_start_position, size, padded_code_bytes)
proc gasPrice*(computation: var BaseComputation) =
proc gasprice*(computation: var BaseComputation) =
computation.stack.push(computation.msg.gasPrice.u256) computation.stack.push(computation.msg.gasPrice.u256)
proc extCodeSize*(computation: var BaseComputation) = proc extCodeSize*(computation: var BaseComputation) =
let account = computation.stack.popAddress() let account = computation.stack.popAddress()
# TODO let codeSize = computation.vmState.readOnlyStateDB.getCode(account).len
# with computation.vm_state.state_db(read_only=True) as state_db: computation.stack.push uint(codeSize)
# code_size = len(state_db.get_code(account))
# computation.stack.push(code_size)
proc extCodeCopy*(computation: var BaseComputation) = proc extCodeCopy*(computation: var BaseComputation) =
let account = computation.stack.popAddress() let account = computation.stack.popAddress()
@ -105,15 +126,9 @@ proc extCodeCopy*(computation: var BaseComputation) =
) )
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt) let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
computation.memory.extend(memPos, len) let codeBytes = computation.vmState.readOnlyStateDB.getCode(account)
computation.memory.writePaddedResult(codeBytes.toOpenArray, memPos, codePos, len)
# TODO:
# with computation.vm_state.state_db(read_only=True) as state_db:
# code = state_db.get_code(account)
# code_bytes = code[code_start_position:code_start_position + size]
# padded_code_bytes = pad_right(code_bytes, size, b'\x00')
# computation.memory.write(mem_start_position, size, padded_code_bytes)
proc returnDataSize*(computation: var BaseComputation) = proc returnDataSize*(computation: var BaseComputation) =
let size = computation.returnData.len.u256 let size = computation.returnData.len.u256

View File

@ -5,7 +5,9 @@
# * 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 macros, stint import
macros,
eth_common/eth_types
template pushRes*: untyped = template pushRes*: untyped =
computation.stack.push(res) computation.stack.push(res)

View File

@ -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
stint, stint/lenient_stint, eth_common/eth_types, stint/lenient_stint,
../../../constants, ../../../vm_state, ../../../vm_types, ../../../vm_types, ../../../constants, ../../../vm_state, ../../../vm_types, ../../../vm_types,
../../../errors, ../../../logging, ../../../utils/padding, ../../../utils/bytes, ../../../errors, ../../../logging, ../../../utils/padding, ../../../utils/bytes,
../../stack, ../../computation, ../../stack, ../../memory, ../../message, ../../stack, ../../computation, ../../stack, ../../memory, ../../message,
@ -14,7 +14,7 @@ import
../opcode_values, ../gas_meter, ../gas_costs ../opcode_values, ../gas_meter, ../gas_costs
export export
stint, lenient_stint, eth_types, lenient_stint,
constants, vm_state, vm_types, vm_types, constants, vm_state, vm_types, vm_types,
errors, logging, padding, bytes, errors, logging, padding, bytes,
stack, computation, stack, memory, message, stack, computation, stack, memory, message,

View File

@ -23,14 +23,7 @@ macro pushXX(size: static[int]): untyped =
let name = ident(&"push{size}") let name = ident(&"push{size}")
result = quote: result = quote:
proc `name`*(`computation`: var BaseComputation) = proc `name`*(`computation`: var BaseComputation) =
let `value` = `computation`.code.read(`size`) `computation`.stack.push `computation`.code.readVmWord(`size`)
let stripped = `value`.toString.strip(0.char)
if stripped.len == 0:
`computation`.stack.push(0.u256)
else:
let paddedValue = `value`.padRight(`size`, 0.byte)
`computation`.stack.push(paddedValue)
pushXX(1) pushXX(1)
pushXX(2) pushXX(2)

View File

@ -10,7 +10,6 @@ import
../../../utils/header, ../../../utils/header,
../../../db/[db_chain, state_db] ../../../db/[db_chain, state_db]
{.this: computation.} {.this: computation.}
{.experimental.} {.experimental.}
@ -20,32 +19,26 @@ using
proc sstore*(computation) = proc sstore*(computation) =
let (slot, value) = stack.popInt(2) let (slot, value) = stack.popInt(2)
var currentValue = 0.u256 var (currentValue, existing) = computation.vmState.readOnlyStateDB.getStorage(computation.msg.storageAddress, slot)
var existing = false
computation.vmState.db(readOnly=false):
(currentValue, existing) = db.getStorage(computation.msg.storageAddress, slot)
let let
gasParam = GasParams(kind: Op.Sstore, s_isStorageEmpty: not existing) gasParam = GasParams(kind: Op.Sstore, s_isStorageEmpty: not existing)
(gasCost, gasRefund) = computation.gasCosts[Sstore].c_handler(currentValue, gasParam) (gasCost, gasRefund) = computation.gasCosts[Sstore].c_handler(currentValue, gasParam)
computation.gasMeter.consumeGas(gasCost, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} ({currentValue})") computation.gasMeter.consumeGas(gasCost, &"SSTORE: {computation.msg.storageAddress}[{slot}] -> {value} ({currentValue})")
if gasRefund > 0: if gasRefund > 0:
computation.gasMeter.refundGas(gasRefund) computation.gasMeter.refundGas(gasRefund)
computation.vmState.db(readOnly=false): computation.vmState.mutateStateDB:
db.setStorage(computation.msg.storageAddress, slot, value) db.setStorage(computation.msg.storageAddress, slot, value)
proc sload*(computation) = proc sload*(computation) =
let slot = stack.popInt() let slot = stack.popInt()
var value = 2.u256 let (value, found) = computation.vmState.readOnlyStateDB.getStorage(computation.msg.storageAddress, slot)
if found:
computation.stack.push value
else:
# XXX: raise exception?
discard
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# value = state_db.get_storage(
# address=computation.msg.storage_address,
# slot=slot,
# )
computation.stack.push(value)

View File

@ -8,7 +8,7 @@
import import
strformat, strformat,
./call, ./impl_std_import, ./call, ./impl_std_import,
stint, byteutils, eth_common byteutils, eth_common
{.this: computation.} {.this: computation.}
{.experimental.} {.experimental.}
@ -32,7 +32,7 @@ method runLogic*(create: Create, computation) =
let (pos, len) = (startPosition.toInt, size.toInt) let (pos, len) = (startPosition.toInt, size.toInt)
computation.memory.extend(pos, len) computation.memory.extend(pos, len)
# TODO: with # TODO: with ZZZZ
# with computation.vm_state.state_db(read_only=True) as state_db: # with computation.vm_state.state_db(read_only=True) as state_db:
# insufficient_funds = state_db.get_balance( # insufficient_funds = state_db.get_balance(
# computation.msg.storage_address) < value # computation.msg.storage_address) < value
@ -94,7 +94,7 @@ method runLogic*(create: CreateByzantium, computation) =
proc selfdestructEIP150(computation) = proc selfdestructEIP150(computation) =
let beneficiary = stack.popAddress() let beneficiary = stack.popAddress()
# TODO: with # TODO: with ZZZZ
# with computation.vm_state.state_db(read_only=True) as state_db: # with computation.vm_state.state_db(read_only=True) as state_db:
# if not state_db.account_exists(beneficiary): # if not state_db.account_exists(beneficiary):
# computation.gas_meter.consume_gas( # computation.gas_meter.consume_gas(
@ -105,7 +105,7 @@ proc selfdestructEIP150(computation) =
proc selfdestructEIP161(computation) = proc selfdestructEIP161(computation) =
let beneficiary = stack.popAddress() let beneficiary = stack.popAddress()
# TODO: with # TODO: with ZZZZ
# with computation.vm_state.state_db(read_only=True) as state_db: # with computation.vm_state.state_db(read_only=True) as state_db:
# is_dead = ( # is_dead = (
# not state_db.account_exists(beneficiary) or # not state_db.account_exists(beneficiary) or
@ -119,7 +119,7 @@ proc selfdestructEIP161(computation) =
# _selfdestruct(computation, beneficiary) # _selfdestruct(computation, beneficiary)
proc selfdestruct(computation; beneficiary: EthAddress) = proc selfdestruct(computation; beneficiary: EthAddress) =
discard # TODO: with discard # TODO: with ZZZZ
# with computation.vm_state.state_db() as state_db: # with computation.vm_state.state_db() as state_db:
# local_balance = state_db.get_balance(computation.msg.storage_address) # local_balance = state_db.get_balance(computation.msg.storage_address)
# beneficiary_balance = state_db.get_balance(beneficiary) # beneficiary_balance = state_db.get_balance(beneficiary)

View File

@ -6,12 +6,12 @@
# 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
eth_common/eth_types,
../../db/db_chain, ../../constants, ../../db/db_chain, ../../constants,
../../utils/header, ../../utils/header,
../base, ../base,
../forks/f20150730_frontier/frontier_vm, ../forks/f20150730_frontier/frontier_vm,
../forks/f20161018_tangerine_whistle/tangerine_vm, ../forks/f20161018_tangerine_whistle/tangerine_vm
stint
# Note (mamy): refactoring is in progress (2018-05-23), this is redundant with # Note (mamy): refactoring is in progress (2018-05-23), this is redundant with
# - `Chain` in src/chain.nim, to be honest I don't understand the need of this abstraction at the moment # - `Chain` in src/chain.nim, to be honest I don't understand the need of this abstraction at the moment

View File

@ -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
sequtils, stint, sequtils,
eth_common/eth_types,
../constants, ../errors, ../logging, ../validation, ../utils/bytes, ../constants, ../errors, ../logging, ../validation, ../utils/bytes,
./utils/utils_numeric ./utils/utils_numeric
@ -24,10 +25,10 @@ proc len*(memory: Memory): int =
result = memory.bytes.len result = memory.bytes.len
proc extend*(memory: var Memory; startPosition: Natural; size: Natural) = proc extend*(memory: var Memory; startPos: Natural; size: Natural) =
if size == 0: if size == 0:
return return
var newSize = ceil32(startPosition + size) var newSize = ceil32(startPos + size)
if newSize <= len(memory): if newSize <= len(memory):
return return
var sizeToExtend = newSize - len(memory) var sizeToExtend = newSize - len(memory)
@ -37,27 +38,35 @@ proc newMemory*(size: Natural): Memory =
result = newMemory() result = newMemory()
result.extend(0, size) result.extend(0, size)
proc read*(memory: var Memory, startPosition: Natural, size: Natural): seq[byte] = proc read*(memory: var Memory, startPos: Natural, size: Natural): seq[byte] =
result = memory.bytes[startPosition ..< (startPosition + size)] result = memory.bytes[startPos ..< (startPos + size)]
proc write*(memory: var Memory, startPosition: Natural, value: openarray[byte]) = proc write*(memory: var Memory, startPos: Natural, value: openarray[byte]) =
let size = value.len let size = value.len
if size == 0: if size == 0:
return return
#echo size #echo size
#echo startPosition #echo startPos
#validateGte(startPosition, 0) #validateGte(startPos, 0)
#validateGte(size, 0) #validateGte(size, 0)
validateLte(startPosition + size, memory.len) validateLte(startPos + size, memory.len)
let index = memory.len let index = memory.len
if memory.len < startPosition + size: if memory.len < startPos + size:
memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPosition + size))) # TODO: better logarithmic scaling? memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPos + size))) # TODO: better logarithmic scaling?
for z, b in value: for z, b in value:
memory.bytes[z + startPosition] = b memory.bytes[z + startPos] = b
template write*(memory: var Memory, startPosition: Natural, size: Natural, value: cstring) = proc writePaddingBytes*(memory: var Memory,
memory.write(startPosition, value.toBytes) startPos, numberOfBytes: Natural,
paddingValue = 0.byte) =
let endPos = startPos + numberOfBytes
assert endPos < memory.len
for i in startPos ..< endPos:
memory.bytes[i] = paddingValue
template write*(memory: var Memory, startPos: Natural, size: Natural, value: cstring) =
memory.write(startPos, value.toBytes)
# TODO ~ O(n^3): # TODO ~ O(n^3):
# - there is a string allocation with $ (?) # - there is a string allocation with $ (?)
# - then a conversion to seq (= new allocation) with toBytes # - then a conversion to seq (= new allocation) with toBytes

View File

@ -6,8 +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
stint, eth_common, eth_common,
../logging, ../constants, ../validation, ../vm_types ../logging, ../constants, ../validation, ../vm_types
proc `origin=`*(message: var Message, value: EthAddress) = proc `origin=`*(message: var Message, value: EthAddress) =
message.internalOrigin = value message.internalOrigin = value
@ -59,7 +59,10 @@ proc newMessage*(
result.data = data result.data = data
result.internalOrigin = options.origin if options.origin != ZERO_ADDRESS:
result.internalOrigin = options.origin
else:
result.internalOrigin = sender
validateGte(options.depth, minimum=0, title="Message.depth") validateGte(options.depth, minimum=0, title="Message.depth")
result.depth = options.depth result.depth = options.depth

View File

@ -7,7 +7,7 @@
import import
strformat, strutils, sequtils, macros, rlp, eth_common, nimcrypto, strformat, strutils, sequtils, macros, rlp, eth_common, nimcrypto,
../errors, ../validation, ./utils/utils_numeric, ../constants, stint, ../logging, .. / utils / bytes ../errors, ../validation, ./utils/utils_numeric, ../constants, ../logging, .. / utils / bytes
type type
Stack* = ref object of RootObj Stack* = ref object of RootObj
@ -29,12 +29,12 @@ proc toStackElement(v: EthAddress, elem: var StackElement) {.inline.} = elem = b
proc toStackElement(v: MDigest, elem: var StackElement) {.inline.} = elem = readUintBE[256](v.data) proc toStackElement(v: MDigest, elem: var StackElement) {.inline.} = elem = readUintBE[256](v.data)
proc fromStackElement(elem: StackElement, v: var UInt256) {.inline.} = v = elem proc fromStackElement(elem: StackElement, v: var UInt256) {.inline.} = v = elem
proc fromStackElement(elem: StackElement, v: var EthAddress) {.inline.} = v[0 .. ^1] = elem.toByteArrayBE().toOpenArray(0, 19) proc fromStackElement(elem: StackElement, v: var EthAddress) {.inline.} = v[0 .. ^1] = elem.toByteArrayBE().toOpenArray(12, 31)
proc fromStackElement(elem: StackElement, v: var Hash256) {.inline.} = v.data = elem.toByteArrayBE() proc fromStackElement(elem: StackElement, v: var Hash256) {.inline.} = v.data = elem.toByteArrayBE()
proc toStackElement(v: seq[byte], elem: var StackElement) {.inline.} = proc toStackElement(v: openarray[byte], elem: var StackElement) {.inline.} =
# TODO: This needs to go # TODO: This needs to go
validateStackItem(v) # validateStackItem(v)
elem = bigEndianToInt(v) elem = bigEndianToInt(v)
proc pushAux[T](stack: var Stack, value: T) = proc pushAux[T](stack: var Stack, value: T) =
@ -45,7 +45,7 @@ proc pushAux[T](stack: var Stack, value: T) =
proc push*(stack: var Stack, value: uint | UInt256 | EthAddress | Hash256) {.inline.} = proc push*(stack: var Stack, value: uint | UInt256 | EthAddress | Hash256) {.inline.} =
pushAux(stack, value) pushAux(stack, value)
proc push*(stack: var Stack, value: seq[byte]) {.inline.} = proc push*(stack: var Stack, value: openarray[byte]) {.inline.} =
# TODO: This needs to go... # TODO: This needs to go...
pushAux(stack, value) pushAux(stack, value)

View File

@ -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
stint, strformat, strutils, sequtils, endians, macros, rlp, strformat, strutils, sequtils, endians, macros,
eth_common/eth_types, rlp,
../../constants, ../../utils/padding ../../constants, ../../utils/padding
# some methods based on py-evm utils/numeric # some methods based on py-evm utils/numeric

View File

@ -7,7 +7,7 @@
import import
macros, strformat, tables, macros, strformat, tables,
stint, eth_common, eth_common,
./logging, ./constants, ./errors, ./transaction, ./db/[db_chain, state_db], ./logging, ./constants, ./errors, ./transaction, ./db/[db_chain, state_db],
./utils/header ./utils/header
@ -77,31 +77,46 @@ method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256
var header = vmState.prevHeaders[idx] var header = vmState.prevHeaders[idx]
result = header.hash result = header.hash
macro db*(vmState: untyped, readOnly: untyped, handler: untyped): untyped = when false:
# vm.state.db: # this was an older version of `mutateStateDB`, kept here for reference
# setupStateDB(fixture{"pre"}, stateDb) # until `mutateStateDB` is fully implemented.
# code = db.getCode(fixture{"exec"}{"address"}.getStr) macro db*(vmState: untyped, readOnly: bool, handler: untyped): untyped =
let db = ident("db") # vm.state.db:
result = quote: # setupStateDB(fixture{"pre"}, stateDb)
block: # code = db.getCode(fixture{"exec"}{"address"}.getStr)
var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`) let db = ident("db")
`handler` result = quote:
if `readOnly`: block:
# This acts as a secondary check that no mutation took place for var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`)
# read_only databases. `handler`
assert `db`.rootHash == `vmState`.blockHeader.stateRoot if `readOnly`:
elif `vmState`.blockHeader.stateRoot != `db`.rootHash: # This acts as a secondary check that no mutation took place for
`vmState`.blockHeader.stateRoot = `db`.rootHash # read_only databases.
assert `db`.rootHash == `vmState`.blockHeader.stateRoot
elif `vmState`.blockHeader.stateRoot != `db`.rootHash:
`vmState`.blockHeader.stateRoot = `db`.rootHash
# TODO # TODO
# `vmState`.accessLogs.reads.update(`db`.db.accessLogs.reads) # `vmState`.accessLogs.reads.update(`db`.db.accessLogs.reads)
# `vmState`.accessLogs.writes.update(`db`.db.accessLogs.writes) # `vmState`.accessLogs.writes.update(`db`.db.accessLogs.writes)
# remove the reference to the underlying `db` object to ensure that no # remove the reference to the underlying `db` object to ensure that no
# further modifications can occur using the `State` object after # further modifications can occur using the `State` object after
# leaving the context. # leaving the context.
# TODO `db`.db = nil # TODO `db`.db = nil
# state._trie = None # state._trie = None
template mutateStateDB*(vmState: BaseVMState, body: untyped) =
# This should provide more clever change handling in the future
block:
let initialStateRoot = vmState.blockHeader.stateRoot
var db {.inject.} = vmState.chaindb.getStateDB(initialStateRoot, false)
body
let finalStateRoot = db.rootHash
if finalStateRoot != initialStateRoot:
vmState.blockHeader.stateRoot = finalStateRoot
proc readOnlyStateDB*(vmState: BaseVMState): AccountStateDB {.inline.}= proc readOnlyStateDB*(vmState: BaseVMState): AccountStateDB {.inline.}=
vmState.chaindb.getStateDb(Hash256(), readOnly = true) vmState.chaindb.getStateDb(vmState.blockHeader.stateRoot, readOnly = true)

View File

@ -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
tables, stint, eth_common, tables,
eth_common,
./constants, ./vm_state, ./logging, ./constants, ./vm_state, ./logging,
./vm/[memory, stack, code_stream], ./vm/[memory, stack, code_stream],
./vm/interpreter/[gas_costs, opcode_values] # TODO - will be hidden at a lower layer ./vm/interpreter/[gas_costs, opcode_values] # TODO - will be hidden at a lower layer

View File

@ -5,10 +5,13 @@
# * 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 ./test_code_stream, when false:
./test_gas_meter, import ./test_code_stream,
./test_memory, ./test_gas_meter,
./test_stack, ./test_memory,
./test_opcode, ./test_stack,
./test_storage_backends ./test_opcode,
# ./test_vm_json ./test_storage_backends
when true:
import ./test_vm_json

View File

@ -5,9 +5,10 @@
# * 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 unittest, macros, strformat, import
stint, unittest, macros, strformat,
../nimbus/[vm_types, errors, logging, vm/interpreter] eth_common/eth_types,
../nimbus/[vm_types, errors, logging, vm/interpreter]
# TODO: quicktest # TODO: quicktest
# PS: parametrize can be easily immitated, but still quicktests would be even more useful # PS: parametrize can be easily immitated, but still quicktests would be even more useful

View File

@ -7,7 +7,7 @@
import import
os, macros, json, strformat, strutils, parseutils, ospaths, tables, os, macros, json, strformat, strutils, parseutils, ospaths, tables,
stint, byteutils, eth_common, eth_keys, byteutils, eth_common, eth_keys, ranges/typedranges,
../nimbus/utils/[address, padding], ../nimbus/utils/[address, padding],
../nimbus/[vm_state, constants], ../nimbus/[vm_state, constants],
../nimbus/db/[db_chain, state_db], ../nimbus/db/[db_chain, state_db],
@ -19,10 +19,15 @@ type
proc validTest*(folder: string, name: string): bool = proc validTest*(folder: string, name: string): bool =
# tests we want to skip or which segfault will be skipped here # tests we want to skip or which segfault will be skipped here
# TODO fix # TODO fix
#if true:
# return "or0" in name
#if true:
# return folder == "vmEnvironmentalInfo"
result = "calldatacopy" notin name and result = "calldatacopy" notin name and
"balanceAddressInputTooBigRightMyAddress." notin name and "balanceAddressInputTooBigRightMyAddress." notin name and
"callstatelessToReturn1" notin name and "callstatelessToReturn1" notin name and
folder notin @["vmRandomTest", "vmSystemOperations", "vmPerformance", "vmEnvironmentalInfo"] folder notin @["vmRandomTest", "vmSystemOperations", "vmPerformance"]
#result = name == "exp2.json" #result = name == "exp2.json"
macro jsonTest*(s: static[string], handler: untyped): untyped = macro jsonTest*(s: static[string], handler: untyped): untyped =
@ -76,22 +81,48 @@ macro jsonTest*(s: static[string], handler: untyped): untyped =
raw.add("OK: " & $okCount & "/" & $sum & " Fail: " & $failCount & "/" & $sum & " Skip: " & $skipCount & "/" & $sum & "\n") raw.add("OK: " & $okCount & "/" & $sum & " Fail: " & $failCount & "/" & $sum & " Skip: " & $skipCount & "/" & $sum & "\n")
writeFile(`s` & ".md", raw) writeFile(`s` & ".md", raw)
proc accountFromHex(s: string): EthAddress = hexToByteArray(s, result) proc ethAddressFromHex(s: string): EthAddress = hexToByteArray(s, result)
proc setupStateDB*(desiredState: JsonNode, stateDB: var AccountStateDB) = proc setupStateDB*(wantedState: JsonNode, stateDB: var AccountStateDB) =
for ac, accountData in desiredState: for ac, accountData in wantedState:
let account = accountFromHex(ac) let account = ethAddressFromHex(ac)
for slot, value in accountData{"storage"}: for slot, value in accountData{"storage"}:
stateDB.setStorage(account, slot.parseInt.u256, value.getInt.u256) stateDB.setStorage(account, slot.parseHexInt.u256, value.getInt.u256)
let nonce = accountData{"nonce"}.getInt.u256 let nonce = accountData{"nonce"}.getInt.u256
let code = accountData{"code"}.getStr let code = hexToSeqByte(accountData{"code"}.getStr).toRange
let balance = accountData{"balance"}.getInt.u256 let balance = accountData{"balance"}.getInt.u256
stateDB.setNonce(account, nonce) stateDB.setNonce(account, nonce)
stateDB.setCode(account, code) stateDB.setCode(account, code)
stateDB.setBalance(account, balance) stateDB.setBalance(account, balance)
proc verifyStateDB*(wantedState: JsonNode, stateDB: AccountStateDB) =
for ac, accountData in wantedState:
let account = ethAddressFromHex(ac)
for slot, value in accountData{"storage"}:
let
slotId = slot.parseHexInt.u256
wantedValue = UInt256.fromHex value.getStr
let (actualValue, found) = stateDB.getStorage(account, slotId)
# echo "FOUND ", found
# echo "ACTUAL VALUE ", actualValue.toHex
doAssert found and actualValue == wantedValue
let
wantedCode = hexToSeqByte(accountData{"code"}.getStr).toRange
wantedBalance = accountData{"balance"}.getInt.u256
wantedNonce = accountData{"nonce"}.getInt.u256
actualCode = stateDB.getCode(account)
actualBalance = stateDB.getBalance(account)
actualNonce = stateDB.getNonce(account)
doAssert wantedCode == actualCode
doAssert wantedBalance == actualBalance
doAssert wantedNonce == actualNonce
proc getHexadecimalInt*(j: JsonNode): int = proc getHexadecimalInt*(j: JsonNode): int =
discard parseHex(j.getStr, result) discard parseHex(j.getStr, result)

View File

@ -5,9 +5,10 @@
# * 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 unittest, sequtils, import
stint, unittest, sequtils,
../nimbus/[constants, errors, vm/memory] eth_common/eth_types,
../nimbus/[constants, errors, vm/memory]
proc memory32: Memory = proc memory32: Memory =
result = newMemory() result = newMemory()

View File

@ -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
unittest, stint, tables, parseutils, unittest, tables, parseutils,
eth_trie/[types, memdb], eth_common/eth_types,
../nimbus/[constants, vm_types, logging], ../nimbus/[constants, vm_types, logging],
../nimbus/vm/interpreter, ../nimbus/vm/interpreter,
../nimbus/utils/header, ../nimbus/utils/header,
@ -17,7 +18,8 @@ from eth_common import GasInt
proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputation = proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputation =
let header = BlockHeader(blockNumber: blockNum) let header = BlockHeader(blockNumber: blockNum)
var vm = newNimbusVM(header, newBaseChainDB(newMemoryDB())) var memDb = newMemDB()
var vm = newNimbusVM(header, newBaseChainDB(trieDB memDb))
# coinbase: "", # coinbase: "",
# difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.u256, # difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.u256,
# blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.u256, # blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.u256,

View File

@ -5,8 +5,10 @@
# * 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 unittest, stint, import
../nimbus/[constants, errors, vm/interpreter, utils/bytes] unittest,
eth_common/eth_types,
../nimbus/[constants, errors, vm/interpreter, utils/bytes]
template testPush(value: untyped, expected: untyped): untyped = template testPush(value: untyped, expected: untyped): untyped =

View File

@ -1,6 +1,6 @@
import import
unittest, macros, unittest, macros,
stint, nimcrypto/[keccak, hash], ranges, eth_common/eth_types, nimcrypto/[keccak, hash], ranges, eth_common/eth_types,
../nimbus/db/[storage_types], ../nimbus/db/[storage_types],
../nimbus/db/backends/[sqlite_backend, rocksdb_backend] ../nimbus/db/backends/[sqlite_backend, rocksdb_backend]

View File

@ -6,51 +6,64 @@
# 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
unittest, strformat, strutils, sequtils, tables, stint, json, ospaths, times, unittest, strformat, strutils, sequtils, tables, json, ospaths, times,
byteutils, ranges/typedranges, nimcrypto/[keccak, hash],
rlp, eth_trie/[types, memdb], eth_common,
./test_helpers, ./test_helpers,
../nimbus/[constants, errors, logging], ../nimbus/[constants, errors, logging],
../nimbus/[vm_state, vm_types], ../nimbus/[vm_state, vm_types],
../nimbus/utils/[header, padding], ../nimbus/utils/[header, padding],
../nimbus/vm/interpreter, ../nimbus/vm/interpreter,
../nimbus/db/[db_chain, state_db, backends/memory_backend], ../nimbus/db/[db_chain, state_db, backends/memory_backend]
eth_common
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus)
suite "vm json tests": suite "vm json tests":
jsonTest("VMTests", testFixture) jsonTest("VMTests", testFixture)
proc stringFromBytes(x: ByteRange): string =
result = newString(x.len)
for i in 0 ..< x.len:
result[i] = char(x[i])
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) = proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
var fixture: JsonNode var fixture: JsonNode
for label, child in fixtures: for label, child in fixtures:
fixture = child fixture = child
break break
let fenv = fixture["env"] let fenv = fixture["env"]
var emptyRlpHash = keccak256.digest(rlp.encode("").toOpenArray)
let header = BlockHeader( let header = BlockHeader(
coinbase: fenv{"currentCoinbase"}.getStr.parseAddress, coinbase: fenv{"currentCoinbase"}.getStr.parseAddress,
difficulty: fenv{"currentDifficulty"}.getHexadecimalInt.u256, difficulty: fenv{"currentDifficulty"}.getHexadecimalInt.u256,
blockNumber: fenv{"currentNumber"}.getHexadecimalInt.u256, blockNumber: fenv{"currentNumber"}.getHexadecimalInt.u256,
gasLimit: fenv{"currentGasLimit"}.getHexadecimalInt.GasInt, gasLimit: fenv{"currentGasLimit"}.getHexadecimalInt.GasInt,
timestamp: fenv{"currentTimestamp"}.getHexadecimalInt.int64.fromUnix timestamp: fenv{"currentTimestamp"}.getHexadecimalInt.int64.fromUnix,
stateRoot: emptyRlpHash
) )
var vm = newNimbusVM(header, newBaseChainDB(newMemoryDB())) var memDb = newMemDB()
var vm = newNimbusVM(header, newBaseChainDB(trieDB memDb))
let fexec = fixture["exec"] let fexec = fixture["exec"]
var code = "" var code = ""
vm.state.db(readOnly=false): vm.state.mutateStateDB:
setupStateDB(fixture{"pre"}, db) setupStateDB(fixture{"pre"}, db)
code = db.getCode(fexec{"address"}.getStr.parseAddress) let address = fexec{"address"}.getStr.parseAddress
code = stringFromBytes db.getCode(address)
code = fexec{"code"}.getStr code = fexec{"code"}.getStr
let toAddress = fexec{"address"}.getStr.parseAddress
let message = newMessage( let message = newMessage(
to = fexec{"address"}.getStr.parseAddress, to = toAddress,
sender = fexec{"caller"}.getStr.parseAddress, sender = fexec{"caller"}.getStr.parseAddress,
value = cast[uint](fexec{"value"}.getHexadecimalInt).u256, # Cast workaround for negative value value = cast[uint](fexec{"value"}.getHexadecimalInt).u256, # Cast workaround for negative value
data = fexec{"data"}.getStr.mapIt(it.byte), data = fexec{"data"}.getStr.hexToSeqByte,
code = code, code = code,
gas = fexec{"gas"}.getHexadecimalInt, gas = fexec{"gas"}.getHexadecimalInt,
gasPrice = fexec{"gasPrice"}.getHexadecimalInt, gasPrice = fexec{"gasPrice"}.getHexadecimalInt,
options = newMessageOptions(origin=fexec{"origin"}.getStr.parseAddress)) options = newMessageOptions(origin=fexec{"origin"}.getStr.parseAddress,
createAddress = toAddress))
#echo fixture{"exec"} #echo fixture{"exec"}
var c = newCodeStreamFromUnescaped(code) var c = newCodeStreamFromUnescaped(code)
@ -101,7 +114,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
for child in zip(computation.children, callCreates): for child in zip(computation.children, callCreates):
var (childComputation, createdCall) = child var (childComputation, createdCall) = child
let toAddress = createdCall{"destination"}.getStr.parseAddress let toAddress = createdCall{"destination"}.getStr.parseAddress
let data = createdCall{"data"}.getStr.mapIt(it.byte) let data = createdCall{"data"}.getStr.hexToSeqByte
let gasLimit = createdCall{"gasLimit"}.getHexadecimalInt let gasLimit = createdCall{"gasLimit"}.getHexadecimalInt
let value = createdCall{"value"}.getHexadecimalInt.u256 let value = createdCall{"value"}.getHexadecimalInt.u256
@ -110,10 +123,11 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
check(gasLimit == childComputation.msg.gas) check(gasLimit == childComputation.msg.gas)
check(value == childComputation.msg.value) check(value == childComputation.msg.value)
# TODO postState = fixture{"post"} # TODO postState = fixture{"post"}
if not fixture{"post"}.isNil:
verifyStateDb(fixture{"post"}, computation.vmState.readOnlyStateDB)
else: else:
# Error checks # Error checks
check(computation.isError) check(computation.isError)
# TODO postState = fixture{"pre"} # TODO postState = fixture{"pre"}
# TODO with vm.state.stateDb(readOnly=True) as stateDb:
# verifyStateDb(postState, stateDb)