2018-04-06 14:52:10 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * 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.
|
|
|
|
|
2018-01-29 17:40:22 +00:00
|
|
|
import
|
|
|
|
strformat, tables,
|
2018-04-11 15:12:28 +00:00
|
|
|
../constants, ../errors, ../validation, ../account, ../logging, ../utils_numeric, .. / utils / [padding, bytes, keccak],
|
2018-05-07 12:41:54 +00:00
|
|
|
stint, rlp
|
2018-01-29 17:40:22 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
AccountStateDB* = ref object
|
2018-05-14 20:13:13 +00:00
|
|
|
db*: Table[string, BytesRange]
|
2018-02-13 17:18:08 +00:00
|
|
|
rootHash*: string # TODO trie
|
2018-01-29 17:40:22 +00:00
|
|
|
|
2018-02-13 17:18:08 +00:00
|
|
|
proc newAccountStateDB*(db: Table[string, string], readOnly: bool = false): AccountStateDB =
|
2018-05-14 20:13:13 +00:00
|
|
|
result = AccountStateDB(db: initTable[string, BytesRange]())
|
2018-01-29 17:40:22 +00:00
|
|
|
|
|
|
|
proc logger*(db: AccountStateDB): Logger =
|
|
|
|
logging.getLogger("db.State")
|
|
|
|
|
2018-04-06 14:52:10 +00:00
|
|
|
proc getAccount(db: AccountStateDB, address: string): Account =
|
2018-01-29 17:40:22 +00:00
|
|
|
# let rlpAccount = db.trie[address]
|
|
|
|
# if not rlpAccount.isNil:
|
|
|
|
# account = rlp.decode[Account](rlpAccount)
|
|
|
|
# account.mutable = true
|
|
|
|
# else:
|
|
|
|
# account = newAccount()
|
|
|
|
result = newAccount() # TODO
|
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
proc setAccount(db: AccountStateDB, address: string, account: Account) =
|
2018-01-29 17:40:22 +00:00
|
|
|
# db.trie[address] = rlp.encode[Account](account)
|
2018-04-06 14:52:10 +00:00
|
|
|
discard # TODO
|
2018-01-29 17:40:22 +00:00
|
|
|
|
2018-02-13 17:18:08 +00:00
|
|
|
|
|
|
|
proc getCodeHash*(db: AccountStateDB, address: string): string =
|
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
|
|
|
let account = db.getAccount(address)
|
|
|
|
result = account.codeHash
|
2018-01-29 17:40:22 +00:00
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
proc getBalance*(db: AccountStateDB, address: string): UInt256 =
|
2018-01-29 17:40:22 +00:00
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
|
|
|
let account = db.getAccount(address)
|
|
|
|
account.balance
|
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
proc setBalance*(db: var AccountStateDB, address: string, balance: UInt256) =
|
2018-01-29 17:40:22 +00:00
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
|
|
|
let account = db.getAccount(address)
|
|
|
|
account.balance = balance
|
|
|
|
db.setAccount(address, account)
|
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
proc deltaBalance*(db: var AccountStateDB, address: string, delta: UInt256) =
|
2018-01-29 17:40:22 +00:00
|
|
|
db.setBalance(address, db.getBalance(address) + delta)
|
|
|
|
|
2018-02-13 17:18:08 +00:00
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
proc setStorage*(db: var AccountStateDB, address: string, slot: UInt256, value: UInt256) =
|
|
|
|
#validateGte(value, 0, title="Storage Value")
|
|
|
|
#validateGte(slot, 0, title="Storage Slot")
|
2018-02-13 17:18:08 +00:00
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
2018-04-06 14:52:10 +00:00
|
|
|
|
2018-02-13 17:18:08 +00:00
|
|
|
# TODO
|
2018-02-20 17:27:43 +00:00
|
|
|
# let account = db.getAccount(address)
|
2018-02-13 17:18:08 +00:00
|
|
|
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot))
|
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
let slotAsKey = slot.intToBigEndian.pad32.toString
|
|
|
|
var storage = db.db
|
|
|
|
# TODO fix
|
2018-02-26 11:59:56 +00:00
|
|
|
if value > 0:
|
2018-05-07 12:41:54 +00:00
|
|
|
let encodedValue = rlp.encode value.intToBigEndian
|
2018-05-14 20:13:13 +00:00
|
|
|
storage[slotAsKey] = encodedValue
|
2018-02-26 11:59:56 +00:00
|
|
|
else:
|
|
|
|
storage.del(slotAsKey)
|
|
|
|
#storage[slotAsKey] = value
|
2018-02-13 17:18:08 +00:00
|
|
|
# account.storageRoot = storage.rootHash
|
|
|
|
# db.setAccount(address, account)
|
|
|
|
|
2018-02-26 11:59:56 +00:00
|
|
|
proc getStorage*(db: var AccountStateDB, address: string, slot: UInt256): (UInt256, bool) =
|
2018-02-13 17:18:08 +00:00
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
2018-02-20 17:27:43 +00:00
|
|
|
#validateGte(slot, 0, title="Storage Slot")
|
2018-04-06 14:52:10 +00:00
|
|
|
|
2018-02-13 17:18:08 +00:00
|
|
|
# TODO
|
2018-02-20 17:27:43 +00:00
|
|
|
# make it more correct
|
|
|
|
# for now, we just use a table
|
2018-04-06 14:52:10 +00:00
|
|
|
|
2018-02-13 17:18:08 +00:00
|
|
|
# let account = db.GetAccount(address)
|
|
|
|
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot))
|
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
let slotAsKey = slot.intToBigEndian.pad32.toString
|
|
|
|
var storage = db.db
|
|
|
|
if storage.hasKey(slotAsKey):
|
2018-02-26 11:59:56 +00:00
|
|
|
#result = storage[slotAsKey]
|
2018-05-14 20:13:13 +00:00
|
|
|
# XXX: `bigEndianToInt` can be refactored to work with a BytesRange/openarray
|
|
|
|
# Then we won't need to call `toSeq` here.
|
|
|
|
result = (storage[slotAsKey].toSeq.bigEndianToInt, true)
|
2018-02-20 17:27:43 +00:00
|
|
|
else:
|
2018-02-26 11:59:56 +00:00
|
|
|
result = (0.u256, false)
|
2018-02-13 17:18:08 +00:00
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
proc setNonce*(db: var AccountStateDB, address: string, nonce: UInt256) =
|
2018-02-13 17:18:08 +00:00
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
2018-02-20 17:27:43 +00:00
|
|
|
#validateGte(nonce, 0, title="Nonce")
|
2018-02-13 17:18:08 +00:00
|
|
|
|
|
|
|
var account = db.getAccount(address)
|
|
|
|
account.nonce = nonce
|
|
|
|
|
|
|
|
db.setAccount(address, account)
|
|
|
|
|
2018-02-20 17:27:43 +00:00
|
|
|
proc getNonce*(db: AccountStateDB, address: string): UInt256 =
|
2018-02-13 17:18:08 +00:00
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
|
|
|
|
|
|
|
let account = db.getAccount(address)
|
|
|
|
return account.nonce
|
|
|
|
|
|
|
|
proc setCode*(db: var AccountStateDB, address: string, code: string) =
|
|
|
|
validateCanonicalAddress(address, title="Storage Address")
|
|
|
|
|
|
|
|
var account = db.getAccount(address)
|
|
|
|
|
|
|
|
account.codeHash = keccak(code)
|
2018-02-20 17:27:43 +00:00
|
|
|
#db.db[account.codeHash] = code
|
2018-02-13 17:18:08 +00:00
|
|
|
db.setAccount(address, account)
|
|
|
|
|
|
|
|
proc getCode*(db: var AccountStateDB, address: string): string =
|
|
|
|
let codeHash = db.getCodeHash(address)
|
2018-02-20 17:27:43 +00:00
|
|
|
#if db.db.hasKey(codeHash):
|
|
|
|
# result = db.db[codeHash]
|
|
|
|
#else:
|
|
|
|
result = ""
|
2018-02-13 17:18:08 +00:00
|
|
|
|
|
|
|
# proc rootHash*(db: AccountStateDB): string =
|
|
|
|
# TODO return self.Trie.rootHash
|
|
|
|
|
|
|
|
# proc `rootHash=`*(db: var AccountStateDB, value: string) =
|
|
|
|
# TODO: self.Trie.rootHash = value
|