diff --git a/VMTests.md b/VMTests.md index c1e92d7f5..a111e729b 100644 --- a/VMTests.md +++ b/VMTests.md @@ -115,7 +115,7 @@ VMTests + expPowerOf2_4.json OK + expPowerOf2_64.json OK + expPowerOf2_8.json OK -- expXY.json Fail ++ expXY.json OK + expXY_success.json OK + fibbonacci_unrolled.json OK + mod0.json OK @@ -150,12 +150,12 @@ VMTests + mulmoddivByZero2.json OK + mulmoddivByZero3.json OK + not1.json OK -+ sdiv0.json OK +- sdiv0.json Fail + sdiv1.json OK + sdiv2.json OK -+ sdiv3.json OK -+ sdiv4.json OK -+ sdiv5.json OK +- sdiv3.json Fail +- sdiv4.json Fail +- sdiv5.json Fail + sdiv6.json OK + sdiv7.json OK + sdiv8.json OK @@ -164,8 +164,8 @@ VMTests + sdivByZero1.json OK + sdivByZero2.json OK + sdiv_dejavu.json OK -+ sdiv_i256min.json OK -+ sdiv_i256min2.json OK +- sdiv_i256min.json Fail +- sdiv_i256min2.json Fail + sdiv_i256min3.json OK + signextendInvalidByteNumber.json OK + signextend_00.json OK @@ -180,9 +180,9 @@ VMTests + signextend_Overflow_dj42.json OK + signextend_bigBytePlus1.json OK + signextend_bitIsSet.json OK -+ smod0.json OK -+ smod1.json OK -+ smod2.json OK +- smod0.json Fail +- smod1.json Fail +- smod2.json Fail + smod3.json OK + smod4.json OK + smod5.json OK @@ -190,7 +190,7 @@ VMTests + smod7.json OK + smod8_byZero.json OK + smod_i256min1.json OK -+ smod_i256min2.json OK +- smod_i256min2.json Fail + stop.json OK + sub0.json OK + sub1.json OK @@ -198,7 +198,7 @@ VMTests + sub3.json OK + sub4.json OK ``` -OK: 194/195 Fail: 1/195 Skip: 0/195 +OK: 185/195 Fail: 10/195 Skip: 0/195 ## vmBitwiseLogicOperation ```diff + and0.json OK @@ -267,7 +267,7 @@ OK: 60/60 Fail: 0/60 Skip: 0/60 ```diff + blockhash257Block.json OK + blockhash258Block.json OK -+ blockhashInRange.json OK +- blockhashInRange.json Fail + blockhashMyBlock.json OK + blockhashNotExistingBlock.json OK + blockhashOutOfRange.json OK @@ -278,21 +278,21 @@ OK: 60/60 Fail: 0/60 Skip: 0/60 + number.json OK + timestamp.json OK ``` -OK: 12/12 Fail: 0/12 Skip: 0/12 +OK: 11/12 Fail: 1/12 Skip: 0/12 ## vmEnvironmentalInfo ```diff - ExtCodeSizeAddressInputTooBigLeftMyAddress.json Skip - ExtCodeSizeAddressInputTooBigRightMyAddress.json Skip - address0.json Skip - address1.json Skip - balance0.json Skip - balance01.json Skip - balance1.json Skip - balanceAddress2.json Skip - balanceAddressInputTooBig.json Skip - balanceAddressInputTooBigLeftMyAddress.json Skip ++ ExtCodeSizeAddressInputTooBigLeftMyAddress.json OK ++ ExtCodeSizeAddressInputTooBigRightMyAddress.json OK ++ address0.json OK ++ address1.json OK ++ balance0.json OK +- balance01.json Fail +- balance1.json Fail ++ balanceAddress2.json OK ++ balanceAddressInputTooBig.json OK +- balanceAddressInputTooBigLeftMyAddress.json Fail balanceAddressInputTooBigRightMyAddress.json Skip - balanceCaller3.json Skip ++ balanceCaller3.json OK calldatacopy0.json Skip calldatacopy0_return.json Skip calldatacopy1.json Skip @@ -307,41 +307,41 @@ OK: 12/12 Fail: 0/12 Skip: 0/12 calldatacopy_DataIndexTooHigh2_return.json Skip calldatacopy_DataIndexTooHigh_return.json Skip calldatacopy_sec.json Skip - calldataload0.json Skip - calldataload1.json Skip - calldataload2.json Skip - calldataloadSizeTooHigh.json Skip - calldataloadSizeTooHighPartial.json Skip - calldataload_BigOffset.json Skip - calldatasize0.json Skip - calldatasize1.json Skip - calldatasize2.json Skip - caller.json Skip - callvalue.json Skip - codecopy0.json Skip - codecopyZeroMemExpansion.json Skip - codecopy_DataIndexTooHigh.json Skip - codesize.json Skip - env1.json Skip - extcodecopy0.json Skip - extcodecopy0AddressTooBigLeft.json Skip - extcodecopy0AddressTooBigRight.json Skip - extcodecopyZeroMemExpansion.json Skip - extcodecopy_DataIndexTooHigh.json Skip - extcodesize0.json Skip - extcodesize1.json Skip - extcodesizeUnderFlow.json Skip - gasprice.json Skip - origin.json Skip ++ calldataload0.json OK ++ calldataload1.json OK ++ calldataload2.json OK ++ calldataloadSizeTooHigh.json OK ++ calldataloadSizeTooHighPartial.json OK ++ calldataload_BigOffset.json OK ++ calldatasize0.json OK ++ calldatasize1.json OK ++ calldatasize2.json OK ++ caller.json OK ++ callvalue.json OK +- codecopy0.json Fail ++ codecopyZeroMemExpansion.json OK +- codecopy_DataIndexTooHigh.json Fail ++ codesize.json OK +- env1.json Fail +- extcodecopy0.json Fail +- extcodecopy0AddressTooBigLeft.json Fail +- extcodecopy0AddressTooBigRight.json Fail +- extcodecopyZeroMemExpansion.json Fail +- extcodecopy_DataIndexTooHigh.json Fail ++ extcodesize0.json OK ++ extcodesize1.json OK ++ extcodesizeUnderFlow.json OK ++ gasprice.json OK ++ origin.json OK ``` -OK: 0/52 Fail: 0/52 Skip: 52/52 +OK: 26/52 Fail: 11/52 Skip: 15/52 ## vmIOandFlowOperations ```diff + BlockNumberDynamicJump0_AfterJumpdest.json OK + BlockNumberDynamicJump0_AfterJumpdest3.json OK + BlockNumberDynamicJump0_foreverOutOfGas.json OK -- BlockNumberDynamicJump0_jumpdest0.json Fail -- BlockNumberDynamicJump0_jumpdest2.json Fail ++ BlockNumberDynamicJump0_jumpdest0.json OK ++ BlockNumberDynamicJump0_jumpdest2.json OK + BlockNumberDynamicJump0_withoutJumpdest.json OK + BlockNumberDynamicJump1.json OK + BlockNumberDynamicJumpInsidePushWithJumpDest.json OK @@ -353,7 +353,7 @@ OK: 0/52 Fail: 0/52 Skip: 52/52 - BlockNumberDynamicJumpiOutsideBoundary.json Fail + BlockNumberDynamicJumpifInsidePushWithJumpDest.json OK + BlockNumberDynamicJumpifInsidePushWithoutJumpDest.json OK -- DyanmicJump0_outOfBoundary.json Fail ++ DyanmicJump0_outOfBoundary.json OK + DynamicJump0_AfterJumpdest.json OK + DynamicJump0_AfterJumpdest3.json OK + DynamicJump0_foreverOutOfGas.json OK @@ -366,15 +366,15 @@ OK: 0/52 Fail: 0/52 Skip: 52/52 + DynamicJumpInsidePushWithoutJumpDest.json OK + DynamicJumpJD_DependsOnJumps0.json OK + DynamicJumpJD_DependsOnJumps1.json OK -- DynamicJumpPathologicalTest0.json Fail ++ DynamicJumpPathologicalTest0.json OK + DynamicJumpPathologicalTest1.json OK + DynamicJumpPathologicalTest2.json OK + DynamicJumpPathologicalTest3.json OK + DynamicJumpStartWithJumpDest.json OK -+ DynamicJump_value1.json OK -+ DynamicJump_value2.json OK -+ DynamicJump_value3.json OK -+ DynamicJump_valueUnderflow.json OK +- DynamicJump_value1.json Fail +- DynamicJump_value2.json Fail +- DynamicJump_value3.json Fail +- DynamicJump_valueUnderflow.json Fail + DynamicJumpi0.json OK + DynamicJumpi1.json OK + DynamicJumpi1_jumpdest.json OK @@ -382,22 +382,22 @@ OK: 0/52 Fail: 0/52 Skip: 52/52 - DynamicJumpiOutsideBoundary.json Fail + DynamicJumpifInsidePushWithJumpDest.json OK + DynamicJumpifInsidePushWithoutJumpDest.json OK -- JDfromStorageDynamicJump0_AfterJumpdest.json Fail -- JDfromStorageDynamicJump0_AfterJumpdest3.json Fail -- JDfromStorageDynamicJump0_foreverOutOfGas.json Fail ++ JDfromStorageDynamicJump0_AfterJumpdest.json OK ++ JDfromStorageDynamicJump0_AfterJumpdest3.json OK ++ JDfromStorageDynamicJump0_foreverOutOfGas.json OK - JDfromStorageDynamicJump0_jumpdest0.json Fail - JDfromStorageDynamicJump0_jumpdest2.json Fail -- JDfromStorageDynamicJump0_withoutJumpdest.json Fail -- JDfromStorageDynamicJump1.json Fail -- JDfromStorageDynamicJumpInsidePushWithJumpDest.json Fail -- JDfromStorageDynamicJumpInsidePushWithoutJumpDest.json Fail -- JDfromStorageDynamicJumpi0.json Fail ++ JDfromStorageDynamicJump0_withoutJumpdest.json OK ++ JDfromStorageDynamicJump1.json OK ++ JDfromStorageDynamicJumpInsidePushWithJumpDest.json OK ++ JDfromStorageDynamicJumpInsidePushWithoutJumpDest.json OK ++ JDfromStorageDynamicJumpi0.json OK - JDfromStorageDynamicJumpi1.json Fail -- JDfromStorageDynamicJumpi1_jumpdest.json Fail ++ JDfromStorageDynamicJumpi1_jumpdest.json OK - JDfromStorageDynamicJumpiAfterStop.json Fail -- JDfromStorageDynamicJumpiOutsideBoundary.json Fail -- JDfromStorageDynamicJumpifInsidePushWithJumpDest.json Fail -- JDfromStorageDynamicJumpifInsidePushWithoutJumpDest.json Fail ++ JDfromStorageDynamicJumpiOutsideBoundary.json OK ++ JDfromStorageDynamicJumpifInsidePushWithJumpDest.json OK ++ JDfromStorageDynamicJumpifInsidePushWithoutJumpDest.json OK + bad_indirect_jump1.json OK + bad_indirect_jump2.json OK + byte1.json OK @@ -461,9 +461,9 @@ OK: 0/52 Fail: 0/52 Skip: 52/52 + mstore0.json OK + mstore1.json OK + mstore8MemExp.json OK -+ mstore8WordToBigError.json OK -+ mstore8_0.json OK -+ mstore8_1.json OK +- mstore8WordToBigError.json Fail +- mstore8_0.json Fail +- mstore8_1.json Fail + mstoreMemExp.json OK + mstoreWordToBigError.json OK + mstore_mload0.json OK @@ -475,15 +475,15 @@ OK: 0/52 Fail: 0/52 Skip: 52/52 - return2.json Fail + sha3MemExp.json OK + sstore_load_0.json OK -+ sstore_load_1.json OK +- sstore_load_1.json Fail - sstore_load_2.json Fail + sstore_underflow.json OK -+ stack_loop.json OK +- stack_loop.json Fail + stackjump1.json OK + swapAt52becameMstore.json OK + when.json OK ``` -OK: 103/145 Fail: 41/145 Skip: 1/145 +OK: 110/145 Fail: 34/145 Skip: 1/145 ## vmLogTest ```diff + log0_emptyMem.json OK @@ -614,7 +614,7 @@ OK: 0/18 Fail: 0/18 Skip: 18/18 + push7.json OK + push8.json OK + push9.json OK -+ swap1.json OK +- swap1.json Fail + swap10.json OK + swap11.json OK + swap12.json OK @@ -631,9 +631,9 @@ OK: 0/18 Fail: 0/18 Skip: 18/18 + swap7.json OK + swap8.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 ```diff 201503102037PYTHON.json Skip @@ -658,25 +658,25 @@ OK: 0/17 Fail: 0/17 Skip: 17/17 ## vmSha3Test ```diff + sha3_0.json OK -+ sha3_1.json OK -+ sha3_2.json OK +- sha3_1.json Fail +- sha3_2.json Fail + sha3_3.json OK + sha3_4.json OK + sha3_5.json OK - sha3_6.json Fail - sha3_bigOffset.json Fail -+ sha3_bigOffset2.json OK +- sha3_bigOffset2.json Fail - sha3_bigSize.json Fail -+ sha3_memSizeNoQuadraticCost31.json OK -+ sha3_memSizeQuadraticCost32.json OK +- sha3_memSizeNoQuadraticCost31.json Fail +- sha3_memSizeQuadraticCost32.json Fail - sha3_memSizeQuadraticCost32_zeroSize.json Fail -+ sha3_memSizeQuadraticCost33.json OK -+ sha3_memSizeQuadraticCost63.json OK -+ sha3_memSizeQuadraticCost64.json OK -+ sha3_memSizeQuadraticCost64_2.json OK -+ sha3_memSizeQuadraticCost65.json OK +- sha3_memSizeQuadraticCost33.json Fail +- sha3_memSizeQuadraticCost63.json Fail +- sha3_memSizeQuadraticCost64.json Fail +- sha3_memSizeQuadraticCost64_2.json Fail +- sha3_memSizeQuadraticCost65.json Fail ``` -OK: 14/18 Fail: 4/18 Skip: 0/18 +OK: 4/18 Fail: 14/18 Skip: 0/18 ## vmSystemOperations ```diff ABAcalls0.json Skip diff --git a/nimbus/account.nim b/nimbus/account.nim index 58e235690..19f76cdd0 100644 --- a/nimbus/account.nim +++ b/nimbus/account.nim @@ -6,16 +6,18 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - constants, errors, stint, rlp, eth_common + constants, errors, eth_common, rlp, eth_common/eth_types type - Account* = ref object + Account* = object nonce*: UInt256 balance*: UInt256 storageRoot*: Hash256 codeHash*: Hash256 -rlpFields Account, nonce, balance - 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 + diff --git a/nimbus/block_types.nim b/nimbus/block_types.nim index 8471a8294..4bc101ad9 100644 --- a/nimbus/block_types.nim +++ b/nimbus/block_types.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - stint, eth_common, + eth_common, ./logging, ./constants type diff --git a/nimbus/constants.nim b/nimbus/constants.nim index 3d95c63c2..3ad8d7a85 100644 --- a/nimbus/constants.nim +++ b/nimbus/constants.nim @@ -1,6 +1,6 @@ import - stint, math, strutils, utils/padding, eth_common + math, strutils, utils/padding, eth_common proc default(t: typedesc): t = discard @@ -48,8 +48,8 @@ let GENESIS_EXTRA_DATA* = "" 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 diff --git a/nimbus/db/db_chain.nim b/nimbus/db/db_chain.nim index f314afbd9..0ec580bdd 100644 --- a/nimbus/db/db_chain.nim +++ b/nimbus/db/db_chain.nim @@ -5,13 +5,14 @@ # * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) # at your option. This file may not be copied, modified, or distributed except according to those terms. -import stint, tables, sequtils, algorithm, rlp, ranges, state_db, nimcrypto, - ../errors, ../block_types, ../utils/header, ../constants, eth_common, byteutils, - ./storage_types.nim, backends/memory_backend +import + tables, sequtils, algorithm, + rlp, ranges, state_db, nimcrypto, eth_trie/[types, hexary], eth_common, byteutils, + ../errors, ../block_types, ../utils/header, ../constants, ./storage_types.nim type BaseChainDB* = ref object - db*: MemoryDB + db*: TrieDatabaseRef # TODO db*: JournalDB KeyType = enum @@ -22,13 +23,10 @@ type blockNumber: BlockNumber index: int -proc newBaseChainDB*(db: MemoryDB): BaseChainDB = +proc newBaseChainDB*(db: TrieDatabaseRef): BaseChainDB = new(result) result.db = db -proc contains*(self: BaseChainDB; key: Hash256): bool = - return self.db.contains(genericHashKey(key)) - proc `$`*(db: BaseChainDB): string = result = "BaseChainDB" @@ -37,17 +35,17 @@ proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader = ## ## Raises BlockNotFound if it is not present in the db. try: - let blk = self.db.get(genericHashKey(blockHash)) + let blk = self.db.get(genericHashKey(blockHash).toOpenArray).toRange return decode(blk, BlockHeader) except KeyError: raise newException(BlockNotFound, "No block with hash " & blockHash.data.toHex) 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 = let k = canonicalHeadHashKey() - if k notin self.db: + if k.toOpenArray notin self.db: raise newException(CanonicalHeadNotFound, "No canonical head set for this chain") return self.getBlockHeaderByHash(self.getHash(k)) @@ -64,7 +62,7 @@ proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockH self.getBlockHeaderByHash(self.lookupBlockHash(n)) 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 = ## 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) 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 = ## 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.} = ## 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] = ## Sets the header as the canonical chain HEAD. @@ -125,36 +124,36 @@ proc setAsCanonicalChainHead(self: BaseChainDB; headerHash: Hash256): seq[BlockH for h in newCanonicalHeaders: self.addBlockNumberToHashLookup(h) - self.db.set(canonicalHeadHashKey(), rlp.encode(header.hash)) + self.db.put(canonicalHeadHashKey().toOpenArray, rlp.encode(header.hash).toOpenArray) + return newCanonicalHeaders proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool = ## 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 = - doAssert(false, "TODO: Implement me") - # var transactionDb = HexaryTrie(self.db, transactionRoot) - # var transactionIdx = 0 - # while true: - # var transactionKey = rlp.encode(transactionIdx) - # if transactionKey in transactionDb: - # var transactionData = transactionDb[transactionKey] - # yield transactionDb[transactionKey] - # else: - # break - # inc transactionIdx + var transactionDb = initHexaryTrie(self.db, transactionRoot) + var transactionIdx = 0 + while true: + let transactionKey = rlp.encode(transactionIdx).toRange + if transactionKey in transactionDb: + yield transactionDb.get(transactionKey) + else: + break + inc transactionIdx - -# iterator getReceipts*(self: BaseChainDB; header: BlockHeader; receiptClass: typedesc): Receipt = -# var receiptDb = HexaryTrie() -# for receiptIdx in itertools.count(): -# var receiptKey = rlp.encode(receiptIdx) -# if receiptKey in receiptDb: -# var receiptData = receiptDb[receiptKey] -# yield rlp.decode(receiptData) -# else: -# break +iterator getReceipts*(self: BaseChainDB; header: BlockHeader; receiptClass: typedesc): Receipt = + var receiptDb = initHexaryTrie(self.db, header.receiptRoot) + var receiptIdx = 0 + while true: + let receiptKey = rlp.encode(receiptIdx).toRange + if receiptKey in receiptDb: + let receiptData = receiptDb.get(receiptKey) + yield rlp.decode(receiptData, receiptClass) + else: + break + inc receiptIdx iterator getBlockTransactions(self: BaseChainDB; transactionRoot: Hash256; transactionClass: typedesc): transactionClass = @@ -166,10 +165,12 @@ proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader if not isGenesis and not self.headerExists(header.parentHash): raise newException(ParentNotFound, "Cannot persist block header " & $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 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 try: headScore = self.getScore(self.getCanonicalHead().hash) @@ -179,18 +180,17 @@ proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader if score > headScore.u256: result = self.setAsCanonicalChainHead(header.hash) - proc addTransactionToCanonicalChain(self: BaseChainDB, txHash: Hash256, blockHeader: BlockHeader, index: int) = 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 = ## Persists the list of uncles to the database. ## Returns the uncles hash. let enc = rlp.encode(uncles) 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) = ## Persist the given block's header and uncles. @@ -227,6 +227,5 @@ proc persistBlockToDb*(self: BaseChainDB; blk: Block) = # proc clear*(self: BaseChainDB): void = # self.db.clear() -method getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB = - # TODO - result = newAccountStateDB(initTable[string, string]()) +proc getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB = + result = newAccountStateDB(self.db, stateRoot) diff --git a/nimbus/db/state_db.nim b/nimbus/db/state_db.nim index dfa673d79..2db99ea87 100644 --- a/nimbus/db/state_db.nim +++ b/nimbus/db/state_db.nim @@ -6,33 +6,44 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - eth_common, tables, nimcrypto, stint, rlp, + sequtils, tables, + eth_common, nimcrypto, rlp, eth_trie/[hexary, memdb], ../constants, ../errors, ../validation, ../account, ../logging type AccountStateDB* = ref object - db*: Table[string, BytesRange] - rootHash*: Hash256 # TODO trie + trie: HexaryTrie -proc newAccountStateDB*(db: Table[string, string], readOnly: bool = false): AccountStateDB = - result = AccountStateDB(db: initTable[string, BytesRange]()) +proc rootHash*(accountDb: AccountStateDB): KeccakHash = + 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 = 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 = - # let rlpAccount = db.trie[address] - # if not rlpAccount.isNil: - # account = rlp.decode[Account](rlpAccount) - # account.mutable = true - # else: - # account = newAccount() - result = newAccount() # TODO + let recordFound = db.trie.get(createRangeFromAddress address) + if recordFound.len > 0: + result = rlp.decode(recordFound, Account) + else: + result = newAccount() proc setAccount(db: AccountStateDB, address: EthAddress, account: Account) = - # db.trie[address] = rlp.encode[Account](account) - discard # TODO - + db.trie.put createRangeFromAddress(address), rlp.encode(account) proc getCodeHash*(db: AccountStateDB, address: EthAddress): Hash256 = 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) = validateCanonicalAddress(address, title="Storage Address") - let account = db.getAccount(address) + var account = db.getAccount(address) account.balance = balance db.setAccount(address, account) proc deltaBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) = 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(slot, 0, title="Storage Slot") validateCanonicalAddress(address, title="Storage Address") - # TODO - # let account = db.getAccount(address) - # var storage = HashTrie(HexaryTrie(self.db, account.storageRoot)) + var account = db.getAccount(address) + var accountTrie = initHexaryTrie(db.trie.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: - let encodedValue = rlp.encode value.toByteArrayBE - storage[slotAsKey] = encodedValue + let encodedValue = rlp.encode value + accountTrie.put(slotAsKey, encodedValue) else: - storage.del(slotAsKey) - #storage[slotAsKey] = value - # account.storageRoot = storage.rootHash - # db.setAccount(address, account) + accountTrie.del(slotAsKey) -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") #validateGte(slot, 0, title="Storage Slot") - # TODO - # make it more correct - # for now, we just use a table + let + account = db.getAccount(address) + slotAsKey = createTrieKeyFromSlot slot + accountTrie = initHexaryTrie(db.trie.db, account.storageRoot) - # let account = db.GetAccount(address) - # var storage = HashTrie(HexaryTrie(self.db, account.storageRoot)) + let + foundRecord = accountTrie.get(slotAsKey) - let slotAsKey = "0x" & slot.dumpHex # Convert to a number to hex big-endian representation including prefix and leading zeros - var storage = db.db - if storage.hasKey(slotAsKey): - let byteRange = storage[slotAsKey] - result = (readUintBE[256](byteRange.toOpenArray), true) + if foundRecord.len > 0: + result = (rlp.decode(foundRecord, UInt256), true) else: result = (0.u256, false) @@ -109,24 +124,21 @@ proc getNonce*(db: AccountStateDB, address: EthAddress): UInt256 = let account = db.getAccount(address) 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") var account = db.getAccount(address) - - account.codeHash = keccak256.digest code - #db.db[account.codeHash] = code + account.codeHash = keccak256.digest code.toOpenArray + # XXX: this uses the journaldb in py-evm + db.trie.put(account.codeHash.toByteRange_Unnecessary, code) db.setAccount(address, account) -proc getCode*(db: var AccountStateDB, address: EthAddress): string = +proc getCode*(db: AccountStateDB, address: EthAddress): ByteRange = let codeHash = db.getCodeHash(address) - #if db.db.hasKey(codeHash): - # result = db.db[codeHash] - #else: - result = "" + result = db.trie.get(codeHash.toByteRange_Unnecessary) -# proc rootHash*(db: AccountStateDB): string = - # TODO return self.Trie.rootHash - -# proc `rootHash=`*(db: var AccountStateDB, value: string) = - # TODO: self.Trie.rootHash = value diff --git a/nimbus/db/storage_types.nim b/nimbus/db/storage_types.nim index 978efbc67..b04fae691 100644 --- a/nimbus/db/storage_types.nim +++ b/nimbus/db/storage_types.nim @@ -11,8 +11,8 @@ type DbKey* = object # The first byte stores the key type. The rest are key-specific values - data: array[33, byte] - dataEndPos: uint8 # the last populated position in the data + data*: array[33, byte] + dataEndPos*: uint8 # the last populated position in the data StorageError* = object of Exception diff --git a/nimbus/transaction.nim b/nimbus/transaction.nim index 5d14b3c06..24e6c6a09 100644 --- a/nimbus/transaction.nim +++ b/nimbus/transaction.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - constants, stint, errors, eth_common + constants, errors, eth_common type BaseTransaction* = ref object diff --git a/nimbus/utils/header.nim b/nimbus/utils/header.nim index 2a8593240..e1265c86a 100644 --- a/nimbus/utils/header.nim +++ b/nimbus/utils/header.nim @@ -6,7 +6,7 @@ # 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 diff --git a/nimbus/validation.nim b/nimbus/validation.nim index 242ebea13..b6b71e159 100644 --- a/nimbus/validation.nim +++ b/nimbus/validation.nim @@ -7,7 +7,7 @@ import strformat, - errors, constants, stint, eth_common + errors, constants, eth_common template validateCanonicalAddress*(value: EthAddress, title: string = "Value") = # TODO: This needs to be removed diff --git a/nimbus/vm/code_stream.nim b/nimbus/vm/code_stream.nim index d3b46440a..f7cfed64c 100644 --- a/nimbus/vm/code_stream.nim +++ b/nimbus/vm/code_stream.nim @@ -7,6 +7,7 @@ import strformat, strutils, sequtils, parseutils, sets, macros, + eth_common, ../logging, ../constants, ./interpreter/opcode_values type @@ -51,6 +52,17 @@ proc read*(c: var CodeStream, size: int): seq[byte] = result = @[] 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 = len(c.bytes) @@ -78,16 +90,15 @@ proc peek*(c: var CodeStream): Op = proc updatePc*(c: var CodeStream, value: int) = c.pc = min(value, len(c)) -macro seek*(c: var CodeStream, pc: int, handler: untyped): untyped = - let c2 = ident("c") - result = quote: - var anchorPc = `c`.pc - `c`.pc = `pc` +when false: + template seek*(cs: var CodeStream, pc: int, handler: untyped): untyped = + var anchorPc = cs.pc + cs.pc = pc try: - var `c2` = `c` - `handler` + var c {.inject.} = cs + handler finally: - `c`.pc = anchorPc + cs.pc = anchorPc proc isValidOpcode*(c: var CodeStream, position: int): bool = if position >= len(c): diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index 2d6cda063..a41f29b1a 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. 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, ./interpreter/[opcode_values,gas_meter, gas_costs], ./code_stream, ./memory, ./message, ./stack, diff --git a/nimbus/vm/forks/f20150730_frontier/frontier_validation.nim b/nimbus/vm/forks/f20150730_frontier/frontier_validation.nim index 57f6969e1..e541f3ea8 100644 --- a/nimbus/vm/forks/f20150730_frontier/frontier_validation.nim +++ b/nimbus/vm/forks/f20150730_frontier/frontier_validation.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - strformat, stint, + strformat, + eth_common/eth_types, ../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransaction) = diff --git a/nimbus/vm/forks/f20150730_frontier/frontier_vm.nim b/nimbus/vm/forks/f20150730_frontier/frontier_vm.nim index 7ddb3a0a1..d34ec5439 100644 --- a/nimbus/vm/forks/f20150730_frontier/frontier_vm.nim +++ b/nimbus/vm/forks/f20150730_frontier/frontier_vm.nim @@ -6,13 +6,12 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - stint, eth_common/eth_types, + eth_common/eth_types, ../../../logging, ../../../constants, ../../../errors, ../../../block_types, ../../../vm/[base, stack], ../../../db/db_chain, ../../../utils/header, ./frontier_block, ./frontier_vm_state, ./frontier_validation - type FrontierVM* = ref object of VM @@ -34,4 +33,6 @@ proc newFrontierVM*(header: BlockHeader, chainDB: BaseChainDB): FrontierVM = result.chainDB = chainDB result.isStateless = true result.state = newFrontierVMState() + result.state.chaindb = result.chainDB + result.state.blockHeader = header result.`block` = makeFrontierBlock(header, @[]) diff --git a/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_validation.nim b/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_validation.nim index 8cc26a305..d784236f1 100644 --- a/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_validation.nim +++ b/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_validation.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - strformat, stint, + strformat, + eth_common/eth_types, ../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header proc validateTangerineTransaction*(vmState: BaseVmState, transaction: BaseTransaction) = diff --git a/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_vm.nim b/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_vm.nim index 4f1fadfe9..63462c975 100644 --- a/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_vm.nim +++ b/nimbus/vm/forks/f20161018_tangerine_whistle/tangerine_vm.nim @@ -6,8 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import + eth_common/eth_types, ../../../logging, ../../../constants, ../../../errors, - stint, ../../../block_types, ../../../vm/[base, stack], ../../../db/db_chain, ../../../utils/header, ./tangerine_block, ./tangerine_vm_state, ./tangerine_validation @@ -34,4 +34,6 @@ proc newTangerineVM*(header: BlockHeader, chainDB: BaseChainDB): TangerineVM = result.chainDB = chainDB result.isStateless = true result.state = newTangerineVMState() + result.state.chaindb = result.chainDB + result.state.blockHeader = header result.`block` = makeTangerineBlock(header, @[]) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 55c092762..a4d25ac6b 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - stint, math, eth_common, # GasInt + math, eth_common/eth_types, ../utils/[macros_gen_opcodes, utils_numeric], ./opcode_values diff --git a/nimbus/vm/interpreter/opcode_table.nim b/nimbus/vm/interpreter/opcode_table.nim index e9c2ec27e..393238192 100644 --- a/nimbus/vm/interpreter/opcode_table.nim +++ b/nimbus/vm/interpreter/opcode_table.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - tables, stint, + tables, eth_common/eth_types, ../../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] @@ -51,8 +51,8 @@ const CallDataLoad: callDataLoad, CallDataSize: callDataSize, CallDataCopy: callDataCopy, - CodeSize: codesize, - CodeCopy: codecopy, + CodeSize: codeSize, + CodeCopy: codeCopy, GasPrice: gasPrice, # TODO this wasn't used previously ExtCodeSize: extCodeSize, ExtCodeCopy: extCodeCopy, diff --git a/nimbus/vm/interpreter/opcodes_impl/arithmetic.nim b/nimbus/vm/interpreter/opcodes_impl/arithmetic.nim index a778c70fa..54cc207dd 100644 --- a/nimbus/vm/interpreter/opcodes_impl/arithmetic.nim +++ b/nimbus/vm/interpreter/opcodes_impl/arithmetic.nim @@ -6,7 +6,9 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - helpers, stint, strutils, ./impl_std_import + strutils, + eth_common/eth_types, + helpers, ./impl_std_import proc add*(computation: var BaseComputation) = # Addition diff --git a/nimbus/vm/interpreter/opcodes_impl/block.nim b/nimbus/vm/interpreter/opcodes_impl/block.nim index 16391574f..f9267846b 100644 --- a/nimbus/vm/interpreter/opcodes_impl/block.nim +++ b/nimbus/vm/interpreter/opcodes_impl/block.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../constants, ../computation, ../vm/stack, ../vm_state, stint + eth_common/eth_types, + ../constants, ../computation, ../vm/stack, ../vm_state proc blockhash*(computation: var BaseComputation) = var blockNumber = computation.stack.popInt() diff --git a/nimbus/vm/interpreter/opcodes_impl/call.nim b/nimbus/vm/interpreter/opcodes_impl/call.nim index 59eb943fe..04dc74674 100644 --- a/nimbus/vm/interpreter/opcodes_impl/call.nim +++ b/nimbus/vm/interpreter/opcodes_impl/call.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - strformat, eth_common, stint, + strformat, eth_common, # ./impl_std_import # Cannot do that due to recursive dependencies # .../vm/interpreter/opcodes_impl/impl_std_import.nim imports .../vm/computation.nim # .../vm/computation.nim imports .../vm/interpreter/opcodes_impl/call.nim diff --git a/nimbus/vm/interpreter/opcodes_impl/comparison.nim b/nimbus/vm/interpreter/opcodes_impl/comparison.nim index 752414eb3..569c92b95 100644 --- a/nimbus/vm/interpreter/opcodes_impl/comparison.nim +++ b/nimbus/vm/interpreter/opcodes_impl/comparison.nim @@ -18,11 +18,17 @@ quasiBoolean(sgt, `>`, signed=true) # Signed Greater Comparison 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 proc iszero*(computation: var BaseComputation) = diff --git a/nimbus/vm/interpreter/opcodes_impl/context.nim b/nimbus/vm/interpreter/opcodes_impl/context.nim index 00fc85ffa..61c77615b 100644 --- a/nimbus/vm/interpreter/opcodes_impl/context.nim +++ b/nimbus/vm/interpreter/opcodes_impl/context.nim @@ -7,14 +7,14 @@ import strformat, - ./impl_std_import + ranges/typedranges, + ./impl_std_import, + ../../../db/state_db proc balance*(computation: var BaseComputation) = let address = computation.stack.popAddress() - var balance: Int256 - # TODO computation.vmState.stateDB(read_only=True): - # balance = db.getBalance(address) - # computation.stack.push(balance) + let balance = computation.vmState.readOnlyStateDB.getBalance(address) + computation.stack.push balance proc origin*(computation: var BaseComputation) = computation.stack.push(computation.msg.origin) @@ -25,18 +25,53 @@ proc address*(computation: var BaseComputation) = proc caller*(computation: var BaseComputation) = computation.stack.push(computation.msg.sender) - proc callValue*(computation: var BaseComputation) = 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) = # Load call data into memory - let startPosition = computation.stack.popInt.toInt - let value = computation.msg.data[startPosition ..< startPosition + 32] - let paddedValue = padRight(value, 32, 0.byte) - let normalizedValue = paddedValue.lStrip(0.byte) - computation.stack.push(normalizedValue) + let origDataPos = computation.stack.popInt + if origDataPos >= computation.msg.data.len: + computation.stack.push(0) + return + 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) = let size = computation.msg.data.len.u256 @@ -52,19 +87,15 @@ proc callDataCopy*(computation: var BaseComputation) = reason="CALLDATACOPY fee") let (memPos, callPos, len) = (memStartPosition.toInt, calldataStartPosition.toInt, size.toInt) - computation.memory.extend(memPos, len) - let value = computation.msg.data[callPos ..< callPos + len] - let paddedValue = padRight(value, len, 0.byte) - computation.memory.write(memPos, paddedValue) + computation.memory.writePaddedResult(computation.msg.data, + memPos, callPos, len) - -proc codesize*(computation: var BaseComputation) = +proc codeSize*(computation: var BaseComputation) = let size = computation.code.len.u256 computation.stack.push(size) - -proc codecopy*(computation: var BaseComputation) = +proc codeCopy*(computation: var BaseComputation) = let (memStartPosition, codeStartPosition, size) = computation.stack.popInt(3) @@ -74,26 +105,16 @@ proc codecopy*(computation: var BaseComputation) = reason="CODECOPY: word gas cost") let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt) - computation.memory.extend(memPos, len) - # TODO - # 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) + computation.memory.writePaddedResult(computation.code.bytes, memPos, codePos, len) - -proc gasprice*(computation: var BaseComputation) = +proc gasPrice*(computation: var BaseComputation) = computation.stack.push(computation.msg.gasPrice.u256) - proc extCodeSize*(computation: var BaseComputation) = let account = computation.stack.popAddress() - # TODO - # with computation.vm_state.state_db(read_only=True) as state_db: - # code_size = len(state_db.get_code(account)) - - # computation.stack.push(code_size) + let codeSize = computation.vmState.readOnlyStateDB.getCode(account).len + computation.stack.push uint(codeSize) proc extCodeCopy*(computation: var BaseComputation) = let account = computation.stack.popAddress() @@ -105,15 +126,9 @@ proc extCodeCopy*(computation: var BaseComputation) = ) let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt) - computation.memory.extend(memPos, len) + let codeBytes = computation.vmState.readOnlyStateDB.getCode(account) - - # 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) + computation.memory.writePaddedResult(codeBytes.toOpenArray, memPos, codePos, len) proc returnDataSize*(computation: var BaseComputation) = let size = computation.returnData.len.u256 diff --git a/nimbus/vm/interpreter/opcodes_impl/helpers.nim b/nimbus/vm/interpreter/opcodes_impl/helpers.nim index 27f70ac67..efcd1f19a 100644 --- a/nimbus/vm/interpreter/opcodes_impl/helpers.nim +++ b/nimbus/vm/interpreter/opcodes_impl/helpers.nim @@ -5,7 +5,9 @@ # * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) # at your option. This file may not be copied, modified, or distributed except according to those terms. -import macros, stint +import + macros, + eth_common/eth_types template pushRes*: untyped = computation.stack.push(res) diff --git a/nimbus/vm/interpreter/opcodes_impl/impl_std_import.nim b/nimbus/vm/interpreter/opcodes_impl/impl_std_import.nim index 3672d6b54..b5d12854c 100644 --- a/nimbus/vm/interpreter/opcodes_impl/impl_std_import.nim +++ b/nimbus/vm/interpreter/opcodes_impl/impl_std_import.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - stint, stint/lenient_stint, + eth_common/eth_types, stint/lenient_stint, ../../../constants, ../../../vm_state, ../../../vm_types, ../../../vm_types, ../../../errors, ../../../logging, ../../../utils/padding, ../../../utils/bytes, ../../stack, ../../computation, ../../stack, ../../memory, ../../message, @@ -14,7 +14,7 @@ import ../opcode_values, ../gas_meter, ../gas_costs export - stint, lenient_stint, + eth_types, lenient_stint, constants, vm_state, vm_types, vm_types, errors, logging, padding, bytes, stack, computation, stack, memory, message, diff --git a/nimbus/vm/interpreter/opcodes_impl/stack_ops.nim b/nimbus/vm/interpreter/opcodes_impl/stack_ops.nim index fb491b4d3..2400e8193 100644 --- a/nimbus/vm/interpreter/opcodes_impl/stack_ops.nim +++ b/nimbus/vm/interpreter/opcodes_impl/stack_ops.nim @@ -23,14 +23,7 @@ macro pushXX(size: static[int]): untyped = let name = ident(&"push{size}") result = quote: proc `name`*(`computation`: var BaseComputation) = - let `value` = `computation`.code.read(`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) - + `computation`.stack.push `computation`.code.readVmWord(`size`) pushXX(1) pushXX(2) diff --git a/nimbus/vm/interpreter/opcodes_impl/storage.nim b/nimbus/vm/interpreter/opcodes_impl/storage.nim index 18f97e26d..0da67cabe 100644 --- a/nimbus/vm/interpreter/opcodes_impl/storage.nim +++ b/nimbus/vm/interpreter/opcodes_impl/storage.nim @@ -10,7 +10,6 @@ import ../../../utils/header, ../../../db/[db_chain, state_db] - {.this: computation.} {.experimental.} @@ -20,32 +19,26 @@ using proc sstore*(computation) = let (slot, value) = stack.popInt(2) - var currentValue = 0.u256 - var existing = false - - computation.vmState.db(readOnly=false): - (currentValue, existing) = db.getStorage(computation.msg.storageAddress, slot) + var (currentValue, existing) = computation.vmState.readOnlyStateDB.getStorage(computation.msg.storageAddress, slot) let gasParam = GasParams(kind: Op.Sstore, s_isStorageEmpty: not existing) (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: computation.gasMeter.refundGas(gasRefund) - computation.vmState.db(readOnly=false): + computation.vmState.mutateStateDB: db.setStorage(computation.msg.storageAddress, slot, value) proc sload*(computation) = 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) diff --git a/nimbus/vm/interpreter/opcodes_impl/system_ops.nim b/nimbus/vm/interpreter/opcodes_impl/system_ops.nim index 21c56673d..d8f226029 100644 --- a/nimbus/vm/interpreter/opcodes_impl/system_ops.nim +++ b/nimbus/vm/interpreter/opcodes_impl/system_ops.nim @@ -8,7 +8,7 @@ import strformat, ./call, ./impl_std_import, - stint, byteutils, eth_common + byteutils, eth_common {.this: computation.} {.experimental.} @@ -32,7 +32,7 @@ method runLogic*(create: Create, computation) = let (pos, len) = (startPosition.toInt, size.toInt) computation.memory.extend(pos, len) - # TODO: with + # TODO: with ZZZZ # with computation.vm_state.state_db(read_only=True) as state_db: # insufficient_funds = state_db.get_balance( # computation.msg.storage_address) < value @@ -94,7 +94,7 @@ method runLogic*(create: CreateByzantium, computation) = proc selfdestructEIP150(computation) = let beneficiary = stack.popAddress() - # TODO: with + # TODO: with ZZZZ # with computation.vm_state.state_db(read_only=True) as state_db: # if not state_db.account_exists(beneficiary): # computation.gas_meter.consume_gas( @@ -105,7 +105,7 @@ proc selfdestructEIP150(computation) = proc selfdestructEIP161(computation) = let beneficiary = stack.popAddress() - # TODO: with + # TODO: with ZZZZ # with computation.vm_state.state_db(read_only=True) as state_db: # is_dead = ( # not state_db.account_exists(beneficiary) or @@ -119,7 +119,7 @@ proc selfdestructEIP161(computation) = # _selfdestruct(computation, beneficiary) proc selfdestruct(computation; beneficiary: EthAddress) = - discard # TODO: with + discard # TODO: with ZZZZ # with computation.vm_state.state_db() as state_db: # local_balance = state_db.get_balance(computation.msg.storage_address) # beneficiary_balance = state_db.get_balance(beneficiary) diff --git a/nimbus/vm/interpreter/vm_forks.nim b/nimbus/vm/interpreter/vm_forks.nim index c189f8561..dd98715dd 100644 --- a/nimbus/vm/interpreter/vm_forks.nim +++ b/nimbus/vm/interpreter/vm_forks.nim @@ -6,12 +6,12 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import + eth_common/eth_types, ../../db/db_chain, ../../constants, ../../utils/header, ../base, ../forks/f20150730_frontier/frontier_vm, - ../forks/f20161018_tangerine_whistle/tangerine_vm, - stint + ../forks/f20161018_tangerine_whistle/tangerine_vm # 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 diff --git a/nimbus/vm/memory.nim b/nimbus/vm/memory.nim index be47fdfdf..f9e66186e 100644 --- a/nimbus/vm/memory.nim +++ b/nimbus/vm/memory.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - sequtils, stint, + sequtils, + eth_common/eth_types, ../constants, ../errors, ../logging, ../validation, ../utils/bytes, ./utils/utils_numeric @@ -24,10 +25,10 @@ proc len*(memory: Memory): int = 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: return - var newSize = ceil32(startPosition + size) + var newSize = ceil32(startPos + size) if newSize <= len(memory): return var sizeToExtend = newSize - len(memory) @@ -37,27 +38,35 @@ proc newMemory*(size: Natural): Memory = result = newMemory() result.extend(0, size) -proc read*(memory: var Memory, startPosition: Natural, size: Natural): seq[byte] = - result = memory.bytes[startPosition ..< (startPosition + size)] +proc read*(memory: var Memory, startPos: Natural, size: Natural): seq[byte] = + 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 if size == 0: return #echo size - #echo startPosition - #validateGte(startPosition, 0) + #echo startPos + #validateGte(startPos, 0) #validateGte(size, 0) - validateLte(startPosition + size, memory.len) + validateLte(startPos + size, memory.len) let index = memory.len - if memory.len < startPosition + size: - memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPosition + size))) # TODO: better logarithmic scaling? + if memory.len < startPos + size: + memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPos + size))) # TODO: better logarithmic scaling? 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) = - memory.write(startPosition, value.toBytes) +proc writePaddingBytes*(memory: var Memory, + 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): # - there is a string allocation with $ (?) # - then a conversion to seq (= new allocation) with toBytes diff --git a/nimbus/vm/message.nim b/nimbus/vm/message.nim index 77f803252..64c833100 100644 --- a/nimbus/vm/message.nim +++ b/nimbus/vm/message.nim @@ -6,8 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - stint, eth_common, - ../logging, ../constants, ../validation, ../vm_types + eth_common, + ../logging, ../constants, ../validation, ../vm_types proc `origin=`*(message: var Message, value: EthAddress) = message.internalOrigin = value @@ -59,7 +59,10 @@ proc newMessage*( 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") result.depth = options.depth diff --git a/nimbus/vm/stack.nim b/nimbus/vm/stack.nim index 76d924465..b8342d9da 100644 --- a/nimbus/vm/stack.nim +++ b/nimbus/vm/stack.nim @@ -7,7 +7,7 @@ import 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 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 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 toStackElement(v: seq[byte], elem: var StackElement) {.inline.} = +proc toStackElement(v: openarray[byte], elem: var StackElement) {.inline.} = # TODO: This needs to go - validateStackItem(v) + # validateStackItem(v) elem = bigEndianToInt(v) 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.} = 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... pushAux(stack, value) diff --git a/nimbus/vm/utils/utils_numeric.nim b/nimbus/vm/utils/utils_numeric.nim index 804f00b10..e01b31f76 100644 --- a/nimbus/vm/utils/utils_numeric.nim +++ b/nimbus/vm/utils/utils_numeric.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - stint, strformat, strutils, sequtils, endians, macros, rlp, + strformat, strutils, sequtils, endians, macros, + eth_common/eth_types, rlp, ../../constants, ../../utils/padding # some methods based on py-evm utils/numeric diff --git a/nimbus/vm_state.nim b/nimbus/vm_state.nim index 25758a72b..8cdc59bb7 100644 --- a/nimbus/vm_state.nim +++ b/nimbus/vm_state.nim @@ -7,7 +7,7 @@ import macros, strformat, tables, - stint, eth_common, + eth_common, ./logging, ./constants, ./errors, ./transaction, ./db/[db_chain, state_db], ./utils/header @@ -77,31 +77,46 @@ method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256 var header = vmState.prevHeaders[idx] result = header.hash -macro db*(vmState: untyped, readOnly: untyped, handler: untyped): untyped = - # vm.state.db: - # setupStateDB(fixture{"pre"}, stateDb) - # code = db.getCode(fixture{"exec"}{"address"}.getStr) - let db = ident("db") - result = quote: - block: - var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`) - `handler` - if `readOnly`: - # This acts as a secondary check that no mutation took place for - # read_only databases. - assert `db`.rootHash == `vmState`.blockHeader.stateRoot - elif `vmState`.blockHeader.stateRoot != `db`.rootHash: - `vmState`.blockHeader.stateRoot = `db`.rootHash +when false: + # this was an older version of `mutateStateDB`, kept here for reference + # until `mutateStateDB` is fully implemented. + macro db*(vmState: untyped, readOnly: bool, handler: untyped): untyped = + # vm.state.db: + # setupStateDB(fixture{"pre"}, stateDb) + # code = db.getCode(fixture{"exec"}{"address"}.getStr) + let db = ident("db") + result = quote: + block: + var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`) + `handler` + if `readOnly`: + # This acts as a secondary check that no mutation took place for + # read_only databases. + assert `db`.rootHash == `vmState`.blockHeader.stateRoot + elif `vmState`.blockHeader.stateRoot != `db`.rootHash: + `vmState`.blockHeader.stateRoot = `db`.rootHash - # TODO - # `vmState`.accessLogs.reads.update(`db`.db.accessLogs.reads) - # `vmState`.accessLogs.writes.update(`db`.db.accessLogs.writes) + # TODO + # `vmState`.accessLogs.reads.update(`db`.db.accessLogs.reads) + # `vmState`.accessLogs.writes.update(`db`.db.accessLogs.writes) - # remove the reference to the underlying `db` object to ensure that no - # further modifications can occur using the `State` object after - # leaving the context. - # TODO `db`.db = nil - # state._trie = None + # remove the reference to the underlying `db` object to ensure that no + # further modifications can occur using the `State` object after + # leaving the context. + # TODO `db`.db = nil + # 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.}= - vmState.chaindb.getStateDb(Hash256(), readOnly = true) + vmState.chaindb.getStateDb(vmState.blockHeader.stateRoot, readOnly = true) diff --git a/nimbus/vm_types.nim b/nimbus/vm_types.nim index 5a4c3c518..315eeebc9 100644 --- a/nimbus/vm_types.nim +++ b/nimbus/vm_types.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - tables, stint, eth_common, + tables, + eth_common, ./constants, ./vm_state, ./logging, ./vm/[memory, stack, code_stream], ./vm/interpreter/[gas_costs, opcode_values] # TODO - will be hidden at a lower layer diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 461037de7..80e2ced06 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -5,10 +5,13 @@ # * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ./test_code_stream, - ./test_gas_meter, - ./test_memory, - ./test_stack, - ./test_opcode, - ./test_storage_backends - # ./test_vm_json +when false: + import ./test_code_stream, + ./test_gas_meter, + ./test_memory, + ./test_stack, + ./test_opcode, + ./test_storage_backends + +when true: + import ./test_vm_json diff --git a/tests/test_gas_meter.nim b/tests/test_gas_meter.nim index 542b2bb47..fbd870eb1 100644 --- a/tests/test_gas_meter.nim +++ b/tests/test_gas_meter.nim @@ -5,9 +5,10 @@ # * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) # at your option. This file may not be copied, modified, or distributed except according to those terms. -import unittest, macros, strformat, - stint, - ../nimbus/[vm_types, errors, logging, vm/interpreter] +import + unittest, macros, strformat, + eth_common/eth_types, + ../nimbus/[vm_types, errors, logging, vm/interpreter] # TODO: quicktest # PS: parametrize can be easily immitated, but still quicktests would be even more useful diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index 472e35805..b21818c29 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -7,7 +7,7 @@ import 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/[vm_state, constants], ../nimbus/db/[db_chain, state_db], @@ -19,10 +19,15 @@ type proc validTest*(folder: string, name: string): bool = # tests we want to skip or which segfault will be skipped here # TODO fix + #if true: + # return "or0" in name + #if true: + # return folder == "vmEnvironmentalInfo" + result = "calldatacopy" notin name and "balanceAddressInputTooBigRightMyAddress." notin name and "callstatelessToReturn1" notin name and - folder notin @["vmRandomTest", "vmSystemOperations", "vmPerformance", "vmEnvironmentalInfo"] + folder notin @["vmRandomTest", "vmSystemOperations", "vmPerformance"] #result = name == "exp2.json" 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") 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) = - for ac, accountData in desiredState: - let account = accountFromHex(ac) +proc setupStateDB*(wantedState: JsonNode, stateDB: var AccountStateDB) = + for ac, accountData in wantedState: + let account = ethAddressFromHex(ac) 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 code = accountData{"code"}.getStr + let code = hexToSeqByte(accountData{"code"}.getStr).toRange let balance = accountData{"balance"}.getInt.u256 stateDB.setNonce(account, nonce) stateDB.setCode(account, code) 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 = discard parseHex(j.getStr, result) diff --git a/tests/test_memory.nim b/tests/test_memory.nim index c8e4efce2..374be9c01 100644 --- a/tests/test_memory.nim +++ b/tests/test_memory.nim @@ -5,9 +5,10 @@ # * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) # at your option. This file may not be copied, modified, or distributed except according to those terms. -import unittest, sequtils, - stint, - ../nimbus/[constants, errors, vm/memory] +import + unittest, sequtils, + eth_common/eth_types, + ../nimbus/[constants, errors, vm/memory] proc memory32: Memory = result = newMemory() diff --git a/tests/test_opcode.nim b/tests/test_opcode.nim index ab2c9eb75..a54a101de 100644 --- a/tests/test_opcode.nim +++ b/tests/test_opcode.nim @@ -6,7 +6,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - unittest, stint, tables, parseutils, + unittest, tables, parseutils, + eth_trie/[types, memdb], eth_common/eth_types, ../nimbus/[constants, vm_types, logging], ../nimbus/vm/interpreter, ../nimbus/utils/header, @@ -17,7 +18,8 @@ from eth_common import GasInt proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputation = let header = BlockHeader(blockNumber: blockNum) - var vm = newNimbusVM(header, newBaseChainDB(newMemoryDB())) + var memDb = newMemDB() + var vm = newNimbusVM(header, newBaseChainDB(trieDB memDb)) # coinbase: "", # difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.u256, # blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.u256, diff --git a/tests/test_stack.nim b/tests/test_stack.nim index 587ae65d7..78cc80d8c 100644 --- a/tests/test_stack.nim +++ b/tests/test_stack.nim @@ -5,8 +5,10 @@ # * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) # at your option. This file may not be copied, modified, or distributed except according to those terms. -import unittest, stint, - ../nimbus/[constants, errors, vm/interpreter, utils/bytes] +import + unittest, + eth_common/eth_types, + ../nimbus/[constants, errors, vm/interpreter, utils/bytes] template testPush(value: untyped, expected: untyped): untyped = diff --git a/tests/test_storage_backends.nim b/tests/test_storage_backends.nim index 3869295eb..e8de17b66 100644 --- a/tests/test_storage_backends.nim +++ b/tests/test_storage_backends.nim @@ -1,6 +1,6 @@ import 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/backends/[sqlite_backend, rocksdb_backend] diff --git a/tests/test_vm_json.nim b/tests/test_vm_json.nim index 9f669eeaf..e0676f61b 100644 --- a/tests/test_vm_json.nim +++ b/tests/test_vm_json.nim @@ -6,51 +6,64 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. 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, ../nimbus/[constants, errors, logging], ../nimbus/[vm_state, vm_types], ../nimbus/utils/[header, padding], ../nimbus/vm/interpreter, - ../nimbus/db/[db_chain, state_db, backends/memory_backend], - eth_common + ../nimbus/db/[db_chain, state_db, backends/memory_backend] proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) suite "vm json tests": 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) = var fixture: JsonNode for label, child in fixtures: fixture = child break let fenv = fixture["env"] + var emptyRlpHash = keccak256.digest(rlp.encode("").toOpenArray) let header = BlockHeader( coinbase: fenv{"currentCoinbase"}.getStr.parseAddress, difficulty: fenv{"currentDifficulty"}.getHexadecimalInt.u256, blockNumber: fenv{"currentNumber"}.getHexadecimalInt.u256, 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"] var code = "" - vm.state.db(readOnly=false): + vm.state.mutateStateDB: 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 + let toAddress = fexec{"address"}.getStr.parseAddress let message = newMessage( - to = fexec{"address"}.getStr.parseAddress, + to = toAddress, sender = fexec{"caller"}.getStr.parseAddress, 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, gas = fexec{"gas"}.getHexadecimalInt, gasPrice = fexec{"gasPrice"}.getHexadecimalInt, - options = newMessageOptions(origin=fexec{"origin"}.getStr.parseAddress)) + options = newMessageOptions(origin=fexec{"origin"}.getStr.parseAddress, + createAddress = toAddress)) #echo fixture{"exec"} var c = newCodeStreamFromUnescaped(code) @@ -101,7 +114,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) = for child in zip(computation.children, callCreates): var (childComputation, createdCall) = child 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 value = createdCall{"value"}.getHexadecimalInt.u256 @@ -110,10 +123,11 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) = check(gasLimit == childComputation.msg.gas) check(value == childComputation.msg.value) # TODO postState = fixture{"post"} + + if not fixture{"post"}.isNil: + verifyStateDb(fixture{"post"}, computation.vmState.readOnlyStateDB) else: # Error checks check(computation.isError) # TODO postState = fixture{"pre"} - - # TODO with vm.state.stateDb(readOnly=True) as stateDb: - # verifyStateDb(postState, stateDb) +