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_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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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,

View File

@ -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) =

View File

@ -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, @[])

View File

@ -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) =

View File

@ -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, @[])

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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) =

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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,

View File

@ -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 =

View File

@ -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]

View File

@ -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)