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:
parent
6f28d11866
commit
18b7bbb3b0
184
VMTests.md
184
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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, @[])
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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, @[])
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue