diff --git a/examples/a.nim b/examples/a.nim new file mode 100644 index 000000000..c93212fc0 --- /dev/null +++ b/examples/a.nim @@ -0,0 +1,7 @@ +import vm/code_stream, opcode_values + +var c = newCodeStream("\x60\x00\x60\x00\x60\x00\x60\x00\x67\x06\xf0\x5b\x59\xd3\xb2\x00\x00\x33\x60\xc8\x5a\x03\xf1") + +let opcodes = c.decompile() +for op in opcodes: + echo op[0], " ", op[1], " ", op[2] \ No newline at end of file diff --git a/examples/nim.cfg b/examples/nim.cfg new file mode 100644 index 000000000..149123bad --- /dev/null +++ b/examples/nim.cfg @@ -0,0 +1 @@ +-p:"../src" diff --git a/src/block.nim b/src/block.nim index 63da46c31..fbf8ed894 100644 --- a/src/block.nim +++ b/src/block.nim @@ -1,5 +1,5 @@ import - logging, constants, utils/header + logging, constants, utils / header, ttmath type CountableList*[T] = ref object diff --git a/src/chain.nim b/src/chain.nim index 0c4bb736a..1056aad6b 100644 --- a/src/chain.nim +++ b/src/chain.nim @@ -1,6 +1,6 @@ import - tables, - logging, constants, errors, validation, utils / [hexadecimal] + tables, ttmath, + logging, constants, errors, validation, utils / hexadecimal, vm / base, db / db_chain type BlockHeader* = ref object @@ -18,6 +18,9 @@ type importBlock*: bool validateBlock*: bool db*: BaseChainDB + fundedAddress*: string + fundedAddressInitialBalance*: int + fundedAddressPrivateKey*: string GenesisParams* = ref object blockNumber*: Int256 @@ -25,11 +28,11 @@ type gasLimit*: Int256 parentHash*: string coinbase*: string - nonce: string - mixHash: string - extraData: string - timestamp: int, - stateRoot: string + nonce*: string + mixHash*: string + extraData*: string + timestamp*: int + stateRoot*: string FundedAddress* = ref object balance*: Int256 @@ -40,6 +43,8 @@ type proc configureChain*(name: string, blockNumber: Int256, vm: VM, importBlock: bool = true, validateBlock: bool = true): Chain = new(result) result.vmsByRange = @[(blockNumber: blockNumber, vm: vm)] + result.importBlock = importBlock + result.validateBlock = validateBlock proc fromGenesis*( chain: Chain, @@ -48,13 +53,18 @@ proc fromGenesis*( genesisState: Table[string, FundedAddress]): Chain = ## Initialize the Chain from a genesis state var stateDB = chaindb.getStateDB(BLANK_ROOT_HASH) - for account, accountData in genesisState: - stateDB.setBalance(account, accountData.balance) - stateDB.setNonce(account, accountData.nonce) - stateDB.setCode(account, accountData.code) - + # TODO + # for account, accountData in genesisState: + # stateDB.setBalance(account, accountData.balance) + # stateDB.setNonce(account, accountData.nonce) + # stateDB.setCode(account, accountData.code) + new(result) result.db = chainDB result.header = BlockHeader() result.logger = logging.getLogger("evm.chain.chain.Chain") - chainDB.persistBlockToDB(result.getBlock()) + result.importBlock = chain.importBlock + result.validateBlock = chain.validateBlock + result.vmsByRange = chain.vmsByRange + # TODO + # chainDB.persistBlockToDB(result.getBlock) diff --git a/src/constants.nim b/src/constants.nim index 48c516f3a..0d59e50bf 100644 --- a/src/constants.nim +++ b/src/constants.nim @@ -191,7 +191,7 @@ let GENESIS_COINBASE* = ZERO_ADDRESS GENESIS_NONCE* = "\x00\x00\x00\x00\x00\x00\x00B" GENESIS_MIX_HASH* = ZERO_HASH32 - GENESIS_EXTRA_DATA = "" + GENESIS_EXTRA_DATA* = "" 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* = "V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!" diff --git a/src/db/backends/memory_backend.nim b/src/db/backends/memory_backend.nim new file mode 100644 index 000000000..8019e5db2 --- /dev/null +++ b/src/db/backends/memory_backend.nim @@ -0,0 +1,23 @@ +import tables, ttmath + +type + MemoryDB* = ref object + kvStore*: Table[string, Int256] + +proc newMemoryDB*(kvStore: Table[string, Int256]): MemoryDB = + MemoryDB(kvStore: kvStore) + +proc newMemoryDB*: MemoryDB = + MemoryDB(kvStore: initTable[string, Int256]()) + +proc get*(db: MemoryDB, key: string): Int256 = + db.kvStore[key] + +proc set*(db: var MemoryDB, key: string, value: Int256) = + db.kvStore[key] = value + +proc exists*(db: MemoryDB, key: string): bool = + db.kvStore.hasKey(key) + +proc delete*(db: var MemoryDB, key: string) = + db.kvStore.del(key) diff --git a/src/db/chain.nim b/src/db/db_chain.nim similarity index 93% rename from src/db/chain.nim rename to src/db/db_chain.nim index 4cf40fd97..3c9cfd9c8 100644 --- a/src/db/chain.nim +++ b/src/db/db_chain.nim @@ -1,12 +1,15 @@ +import strformat, tables, ttmath, state_db, backends / memory_backend + type BaseChainDB* = ref object + db*: MemoryDB # TODO db*: JournalDB -# proc makeBaseChainDB*(db: MemoryDB): BaseChainDB = -# result.db = JournalDB(db) +proc newBaseChainDB*(db: MemoryDB): BaseChainDB = + result.db = db -# proc exists*(self: BaseChainDB; key: cstring): bool = -# return self.db.exists(key) +proc exists*(self: BaseChainDB; key: string): bool = + return self.db.exists(key) # proc getCanonicalHead*(self: BaseChainDB): BlockHeader = # if notself.exists(CANONICALHEADHASHDBKEY): @@ -145,7 +148,7 @@ type # proc clear*(self: BaseChainDB): void = # self.db.clear() -# proc getStateDb*(self: BaseChainDB; stateRoot: cstring; readOnly: bool): AccountStateDB = -# return AccountStateDB() +method getStateDb*(self: BaseChainDB; stateRoot: string; readOnly: bool = false): AccountStateDB = + return newAccountStateDB(initTable[string, Int256]()) # var CANONICALHEADHASHDBKEY = cstring"v1:canonical_head_hash" diff --git a/src/vm/base.nim b/src/vm/base.nim index 5ecd3efe3..c84a6cbb1 100644 --- a/src/vm/base.nim +++ b/src/vm/base.nim @@ -1,5 +1,5 @@ import - ../logging, ../constants, ../errors, ../transaction, ../computation, "../block", ../vm_state, ../vm_state_transactions, ../db/chain, ../utils/header + ../logging, ../constants, ../errors, ../transaction, ../computation, "../block", ../vm_state, ../vm_state_transactions, ../db/db_chain, ../utils/header type VM* = ref object of RootObj diff --git a/src/vm/code_stream.nim b/src/vm/code_stream.nim index ec04c4c1f..f35ac80fb 100644 --- a/src/vm/code_stream.nim +++ b/src/vm/code_stream.nim @@ -94,3 +94,19 @@ proc isValidOpcode*(c: var CodeStream, position: int): bool = return false else: return true + +proc decompile*(original: CodeStream): seq[(int, Op, string)] = + # behave as https://etherscan.io/opcode-tool + # TODO + result = @[] + var c = newCodeStream(original.bytes) + while true: + var op = c.next + if op >= PUSH1 and op <= PUSH32: + let bytes = c.read(op.int - 95) + result.add((c.pc - 1, op, "0x" & bytes.mapIt($(it.BiggestInt.toHex(2))).join(""))) + elif op != Op.Stop: + result.add((c.pc - 1, op, "")) + else: + result.add((-1, Op.STOP, "")) + break diff --git a/src/vm/forks/frontier/frontier_validation.nim b/src/vm/forks/frontier/frontier_validation.nim index 5d978e97d..0c1eb973e 100644 --- a/src/vm/forks/frontier/frontier_validation.nim +++ b/src/vm/forks/frontier/frontier_validation.nim @@ -1,6 +1,6 @@ import strformat, - constants, errors, vm_state, transaction, utils/header + constants, errors, ttmath, vm_state, transaction, utils/header proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransaction) = let gasCost = transaction.gas * transaction.gasPrice @@ -9,7 +9,7 @@ proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransac # senderBalance = db.getBalance(transaction.sender) senderBalance = gasCost # TODO if senderBalance < gasCost: - raise newException(ValidationError, %"Sender account balance cannot afford txn gas: {transaction.sender}") + raise newException(ValidationError, &"Sender account balance cannot afford txn gas: {transaction.sender}") let totalCost = transaction.value + gasCost diff --git a/src/vm/forks/frontier/frontier_vm_state.nim b/src/vm/forks/frontier/frontier_vm_state.nim index 543e89c66..7b4c978e9 100644 --- a/src/vm/forks/frontier/frontier_vm_state.nim +++ b/src/vm/forks/frontier/frontier_vm_state.nim @@ -1,5 +1,5 @@ import - logging, constants, errors, vm_state, utils/header, db/chain + logging, constants, errors, vm_state, utils/header, db/db_chain type FrontierVMState* = object of BaseVMState diff --git a/src/vm/forks/frontier/vm.nim b/src/vm/forks/frontier/vm.nim index a16bde32a..5a496b81d 100644 --- a/src/vm/forks/frontier/vm.nim +++ b/src/vm/forks/frontier/vm.nim @@ -1,5 +1,5 @@ import - logging, constants, errors, "block", vm / [base, stack], db / chain, utils / header, + logging, constants, errors, ttmath, "block", vm / [base, stack], db / db_chain, utils / header, frontier_block, frontier_vm_state, frontier_validation diff --git a/src/vm_state.nim b/src/vm_state.nim index 4a10e0647..789ab23d4 100644 --- a/src/vm_state.nim +++ b/src/vm_state.nim @@ -1,6 +1,6 @@ import strformat, tables, - logging, constants, ttmath, errors, transaction, db/chain, utils/state, utils/header + logging, constants, ttmath, errors, transaction, db/db_chain, utils/state, utils/header type BaseVMState* = ref object of RootObj diff --git a/src/vm_state_transactions.nim b/src/vm_state_transactions.nim index 8c78b7652..a85234ddc 100644 --- a/src/vm_state_transactions.nim +++ b/src/vm_state_transactions.nim @@ -1,6 +1,6 @@ import strformat, tables, - logging, constants, errors, computation, transaction, vm_state, "block", db/chain, utils/state, utils/header + logging, constants, errors, computation, transaction, vm_state, "block", db / db_chain, utils / [state, header] method executeTransaction(vmState: var BaseVMState, transaction: BaseTransaction): (BaseComputation, Header) = # Execute the transaction in the vm diff --git a/tests/fixtures.nim b/tests/fixtures.nim new file mode 100644 index 000000000..a947602b1 --- /dev/null +++ b/tests/fixtures.nim @@ -0,0 +1,28 @@ +import unittest, strformat, tables, constants, chain, ttmath, vm / forks / frontier / vm, utils / [header, address], db / db_chain, db / backends / memory_backend + +proc chainWithoutBlockValidation: Chain = + result = configureChain("TestChain", GENESIS_BLOCK_NUMBER, newFrontierVM(Header(), newBaseChainDB(newMemoryDB())), false, false) + let privateKey = "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" # TODO privateKey(decodeHex("0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")) + let fundedAddr = privateKey # privateKey.publicKey.toCanonicalAddress + let initialBalance = 100_000_000 + let genesisParams = GenesisParams( + blockNumber: GENESIS_BLOCK_NUMBER, + difficulty: GENESIS_DIFFICULTY, + gasLimit: GENESIS_GAS_LIMIT, + parentHash: GENESIS_PARENT_HASH, + coinbase: GENESIS_COINBASE, + nonce: GENESIS_NONCE, + mixHash: GENESIS_MIX_HASH, + extraData: GENESIS_EXTRA_DATA, + timestamp: 1501851927, + stateRoot: "0x9d354f9b5ba851a35eced279ef377111387197581429cfcc7f744ef89a30b5d4") #.decodeHex) + let genesisState = {"fundedAddr": FundedAddress(balance: initialBalance.int256, nonce: 0, code: "")}.toTable() + result = fromGenesis( + result, + newBaseChainDB(newMemoryDB()), + genesisParams, + genesisState) + result.fundedAddress = fundedAddr + result.fundedAddressInitialBalance = initialBalance + result.fundedAddressPrivateKey = privateKey +