Separate Chain from ChainDB

This commit is contained in:
Yuriy Glukhov 2018-08-29 11:49:01 +03:00
parent df88326cfd
commit e3be8ca30a
5 changed files with 86 additions and 97 deletions

View File

@ -11,7 +11,7 @@ import
../errors, ../block_types, ../utils/header, ../constants, ./storage_types.nim ../errors, ../block_types, ../utils/header, ../constants, ./storage_types.nim
type type
BaseChainDB* = ref object of AbstractChainDB BaseChainDB* = ref object
db*: TrieDatabaseRef db*: TrieDatabaseRef
# TODO db*: JournalDB # TODO db*: JournalDB
@ -80,6 +80,9 @@ proc getBlockHeader*(self: BaseChainDB; n: BlockNumber): BlockHeader =
## Raises BlockNotFound error if the block is not in the DB. ## Raises BlockNotFound error if the block is not in the DB.
self.getBlockHeader(self.getBlockHash(n)) self.getBlockHeader(self.getBlockHash(n))
proc getBlockBody*(self: BaseChainDB, h: Hash256, output: var BlockBody): bool =
discard # TODO:
proc getScore*(self: BaseChainDB; blockHash: Hash256): int = proc getScore*(self: BaseChainDB; blockHash: Hash256): int =
rlp.decode(self.db.get(blockHashToScoreKey(blockHash).toOpenArray).toRange, int) rlp.decode(self.db.get(blockHashToScoreKey(blockHash).toOpenArray).toRange, int)
@ -250,34 +253,6 @@ proc persistBlockToDb*(self: BaseChainDB; blk: Block) =
proc getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB = proc getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB =
result = newAccountStateDB(self.db, stateRoot) result = newAccountStateDB(self.db, stateRoot)
method genesisHash*(db: BaseChainDB): KeccakHash =
db.getBlockHash(0.toBlockNumber)
method getBlockHeader*(db: BaseChainDB, b: HashOrNum): BlockHeaderRef =
var h: BlockHeader
var ok = case b.isHash
of true:
db.getBlockHeader(b.hash, h)
else:
db.getBlockHeader(b.number, h)
if ok:
result.new()
result[] = h
method getBestBlockHeader*(self: BaseChainDB): BlockHeaderRef =
result.new()
result[] = self.getCanonicalHead()
method getSuccessorHeader*(db: BaseChainDB, h: BlockHeader): BlockHeaderRef =
let n = h.blockNumber + 1
var r: BlockHeader
if db.getBlockHeader(n, r):
result.new()
result[] = r
method getBlockBody*(db: BaseChainDB, blockHash: KeccakHash): BlockBodyRef =
result = nil
# Deprecated: # Deprecated:
proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader {.deprecated.} = proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader {.deprecated.} =
@ -288,3 +263,4 @@ proc lookupBlockHash*(self: BaseChainDB; n: BlockNumber): Hash256 {.deprecated.}
proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockHeader {.deprecated.} = proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockHeader {.deprecated.} =
self.getBlockHeader(n) self.getBlockHeader(n)

View File

@ -11,7 +11,7 @@ import
os, strutils, net, eth_common, db/[storage_types, db_chain], os, strutils, net, eth_common, db/[storage_types, db_chain],
asyncdispatch2, json_rpc/rpcserver, eth_keys, asyncdispatch2, json_rpc/rpcserver, eth_keys,
eth_p2p, eth_p2p/rlpx_protocols/[eth, les], eth_p2p, eth_p2p/rlpx_protocols/[eth, les],
config, genesis, rpc/[common, p2p], config, genesis, rpc/[common, p2p], p2p/chain,
eth_trie eth_trie
const UseSqlite = true const UseSqlite = true
@ -85,10 +85,10 @@ proc start(): NimbusObject =
nimbus.ethNode = newEthereumNode(keypair, address, conf.net.networkId, nimbus.ethNode = newEthereumNode(keypair, address, conf.net.networkId,
nil, nimbusClientId) nil, nimbusClientId)
nimbus.ethNode.chain = chainDB nimbus.ethNode.chain = newChain(chainDB)
if RpcFlags.Enabled in conf.rpc.flags: if RpcFlags.Enabled in conf.rpc.flags:
setupP2PRpc(nimbus.ethNode, nimbus.rpcServer) setupEthRpc(nimbus.ethNode, chainDB, nimbus.rpcServer)
## Starting servers ## Starting servers
nimbus.state = Starting nimbus.state = Starting
@ -101,10 +101,9 @@ proc start(): NimbusObject =
waitFor nimbus.ethNode.connectToNetwork(conf.net.bootNodes) waitFor nimbus.ethNode.connectToNetwork(conf.net.bootNodes)
# TODO: temp code until the CLI/RPC interface is fleshed out # TODO: temp code until the CLI/RPC interface is fleshed out
if os.getenv("START_SYNC") == "1": let status = waitFor nimbus.ethNode.fastBlockchainSync()
let status = waitFor nimbus.ethNode.fastBlockchainSync() if status != syncSuccess:
if status != syncSuccess: echo "Block sync failed: ", status
echo "Block sync failed: ", status
nimbus.state = Running nimbus.state = Running
result = nimbus result = nimbus
@ -119,8 +118,8 @@ proc process*(nimbus: NimbusObject) =
proc signalBreak(udata: pointer) = proc signalBreak(udata: pointer) =
nimbus.state = Stopping nimbus.state = Stopping
# Adding SIGINT, SIGTERM handlers # Adding SIGINT, SIGTERM handlers
discard addSignal(SIGINT, signalBreak) # discard addSignal(SIGINT, signalBreak)
discard addSignal(SIGTERM, signalBreak) # discard addSignal(SIGTERM, signalBreak)
# Main loop # Main loop
while nimbus.state == Running: while nimbus.state == Running:

51
nimbus/p2p/chain.nim Normal file
View File

@ -0,0 +1,51 @@
import ../db/db_chain, eth_common, chronicles, ../vm_state, ../vm_types, ../transaction,
../vm/[computation, interpreter_dispatch, message]
type
Chain* = ref object of AbstractChainDB
db: BaseChainDB
proc newChain*(db: BaseChainDB): Chain =
result.new
result.db = db
method genesisHash*(c: Chain): KeccakHash =
c.db.getBlockHash(0.toBlockNumber)
method getBlockHeader*(c: Chain, b: HashOrNum, output: var BlockHeader): bool =
case b.isHash
of true:
c.db.getBlockHeader(b.hash, output)
else:
c.db.getBlockHeader(b.number, output)
method getBestBlockHeader*(c: Chain): BlockHeader =
c.db.getCanonicalHead()
method getSuccessorHeader*(c: Chain, h: BlockHeader, output: var BlockHeader): bool =
let n = h.blockNumber + 1
c.db.getBlockHeader(n, output)
method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef =
result = nil
method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]) =
# Run the VM here
assert(headers.len == bodies.len)
for i in 0 ..< headers.len:
let head = c.db.getCanonicalHead()
# assert(head.blockNumber == headers[i].blockNumber - 1)
let vmState = newBaseVMState(head, c.db)
if bodies[i].transactions.len != 0:
# echo "block: ", headers[i].blockNumber
for t in bodies[i].transactions:
var msg: Message
# echo "trns: ", t
# let msg = newMessage(t.gasLimit, t.gasPrice, t.to, t.getSender,
# let c = newBaseComputation(vmState,
discard

View File

@ -47,15 +47,17 @@ func headerFromTag(chain:BaseChainDB, blockTag: string): BlockHeader =
let blockNum = stint.fromHex(UInt256, tag) let blockNum = stint.fromHex(UInt256, tag)
result = chain.getBlockHeader(blockNum) result = chain.getBlockHeader(blockNum)
proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
template chain: untyped = BaseChainDB(node.chain) # TODO: Sensible casting
proc accountDbFromTag(tag: string, readOnly = true): AccountStateDb = proc accountDbFromTag(tag: string, readOnly = true): AccountStateDb =
let let
header = chain.headerFromTag(tag) header = chain.headerFromTag(tag)
vmState = newBaseVMState(header, chain) vmState = newBaseVMState(header, chain)
result = vmState.chaindb.getStateDb(vmState.blockHeader.hash, readOnly) result = vmState.chaindb.getStateDb(vmState.blockHeader.hash, readOnly)
proc getBlockBody(hash: KeccakHash): BlockBody =
if not chain.getBlockBody(hash, result):
raise newException(ValueError, "Cannot find hash")
rpcsrv.rpc("net_version") do() -> uint: rpcsrv.rpc("net_version") do() -> uint:
let conf = getConfiguration() let conf = getConfiguration()
result = conf.net.networkId result = conf.net.networkId
@ -144,23 +146,15 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
## data: hash of a block ## data: hash of a block
## Returns integer of the number of transactions in this block. ## Returns integer of the number of transactions in this block.
var hashData = strToHash(data.string) var hashData = strToHash(data.string)
let body = chain.getBlockBody(hashData) result = getBlockBody(hashData).transactions.len
if body == nil:
raise newException(ValueError, "Cannot find hash")
result = body.transactions.len
rpcsrv.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> int: rpcsrv.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> int:
## Returns the number of transactions in a block matching the given block number. ## Returns the number of transactions in a block matching the given block number.
## ##
## data: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter. ## data: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
## Returns integer of the number of transactions in this block. ## Returns integer of the number of transactions in this block.
let let header = chain.headerFromTag(quantityTag)
header = chain.headerFromTag(quantityTag) result = getBlockBody(header.hash).transactions.len
accountDb = accountDbFromTag(quantityTag)
body = chain.getBlockBody(header.hash)
if body == nil:
raise newException(ValueError, "Cannot find hash")
result = body.transactions.len
rpcsrv.rpc("eth_getUncleCountByBlockHash") do(data: HexDataStr) -> int: rpcsrv.rpc("eth_getUncleCountByBlockHash") do(data: HexDataStr) -> int:
## Returns the number of uncles in a block from a block matching the given block hash. ## Returns the number of uncles in a block from a block matching the given block hash.
@ -168,22 +162,15 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
## data: hash of a block. ## data: hash of a block.
## Returns integer of the number of uncles in this block. ## Returns integer of the number of uncles in this block.
var hashData = strToHash(data.string) var hashData = strToHash(data.string)
let body = chain.getBlockBody(hashData) result = getBlockBody(hashData).uncles.len
if body == nil:
raise newException(ValueError, "Cannot find hash")
result = body.uncles.len
rpcsrv.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string) -> int: rpcsrv.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string) -> int:
## Returns the number of uncles in a block from a block matching the given block number. ## Returns the number of uncles in a block from a block matching the given block number.
## ##
## quantityTag: integer of a block number, or the string "latest", "earliest" or "pending", see the default block parameter. ## quantityTag: integer of a block number, or the string "latest", "earliest" or "pending", see the default block parameter.
## Returns integer of uncles in this block. ## Returns integer of uncles in this block.
let let header = chain.headerFromTag(quantityTag)
header = chain.headerFromTag(quantityTag) result = getBlockBody(header.hash).uncles.len
body = chain.getBlockBody(header.hash)
if body == nil:
raise newException(ValueError, "Cannot find hash")
result = body.uncles.len
rpcsrv.rpc("eth_getCode") do(data: EthAddressStr, quantityTag: string) -> HexDataStr: rpcsrv.rpc("eth_getCode") do(data: EthAddressStr, quantityTag: string) -> HexDataStr:
## Returns code at a given address. ## Returns code at a given address.
@ -243,7 +230,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
## Returns the amount of gas used. ## Returns the amount of gas used.
discard discard
func populateBlockObject(header: BlockHeader, blockBody: BlockBodyRef): BlockObject = func populateBlockObject(header: BlockHeader, blockBody: BlockBody): BlockObject =
result.number = new BlockNumber result.number = new BlockNumber
result.number[] = header.blockNumber result.number[] = header.blockNumber
result.hash = new Hash256 result.hash = new Hash256
@ -286,10 +273,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
let let
h = data.string.strToHash h = data.string.strToHash
header = chain.getBlockHeader(h) header = chain.getBlockHeader(h)
body = chain.getBlockBody(h) populateBlockObject(header, getBlockBody(h))
if body == nil:
raise newException(ValueError, "Cannot find hash")
populateBlockObject(header, body)
rpcsrv.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject: rpcsrv.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject:
## Returns information about a block by block number. ## Returns information about a block by block number.
@ -299,10 +283,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
## Returns BlockObject or nil when no block was found. ## Returns BlockObject or nil when no block was found.
let let
header = chain.headerFromTag(quantityTag) header = chain.headerFromTag(quantityTag)
body = chain.getBlockBody(header.hash) populateBlockObject(header, getBlockBody(header.hash))
if body == nil:
raise newException(ValueError, "Cannot find hash")
populateBlockObject(header, body)
func populateTransactionObject(transaction: Transaction, txHash: Hash256, txCount: UInt256, txIndex: int, blockHeader: BlockHeader, gas: int64): TransactionObject = func populateTransactionObject(transaction: Transaction, txHash: Hash256, txCount: UInt256, txIndex: int, blockHeader: BlockHeader, gas: int64): TransactionObject =
result.hash = txHash result.hash = txHash
@ -332,11 +313,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
txDetails = chain.getTransactionKey(h) txDetails = chain.getTransactionKey(h)
header = chain.getBlockHeader(txDetails.blockNumber) header = chain.getBlockHeader(txDetails.blockNumber)
blockHash = chain.getBlockHash(txDetails.blockNumber) blockHash = chain.getBlockHash(txDetails.blockNumber)
body = chain.getBlockBody(blockHash) transaction = getBlockBody(blockHash).transactions[txDetails.index]
if body == nil:
raise newException(ValueError, "Cannot find hash")
let
transaction = body.transactions[txDetails.index]
vmState = newBaseVMState(header, chain) vmState = newBaseVMState(header, chain)
addressDb = vmState.chaindb.getStateDb(blockHash, true) addressDb = vmState.chaindb.getStateDb(blockHash, true)
# TODO: Get/calculate address for this transaction # TODO: Get/calculate address for this transaction
@ -355,12 +332,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
## Returns requested transaction information. ## Returns requested transaction information.
let let
blockHash = data.string.strToHash() blockHash = data.string.strToHash()
body = chain.getBlockBody(blockHash)
if body == nil:
raise newException(ValueError, "Cannot find hash")
let
header = chain.getBlockHeader(blockHash) header = chain.getBlockHeader(blockHash)
transaction = body.transactions[quantity] transaction = getBlockBody(blockHash).transactions[quantity]
vmState = newBaseVMState(header, chain) vmState = newBaseVMState(header, chain)
addressDb = vmState.chaindb.getStateDb(blockHash, true) addressDb = vmState.chaindb.getStateDb(blockHash, true)
# TODO: Get/calculate address for this transaction # TODO: Get/calculate address for this transaction
@ -380,11 +353,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
let let
header = chain.headerFromTag(quantityTag) header = chain.headerFromTag(quantityTag)
blockHash = header.hash blockHash = header.hash
body = chain.getBlockBody(blockHash) transaction = getBlockBody(blockHash).transactions[quantity]
if body == nil:
raise newException(ValueError, "Cannot find hash")
let
transaction = body.transactions[quantity]
vmState = newBaseVMState(header, chain) vmState = newBaseVMState(header, chain)
addressDb = vmState.chaindb.getStateDb(blockHash, true) addressDb = vmState.chaindb.getStateDb(blockHash, true)
# TODO: Get/calculate address for this transaction # TODO: Get/calculate address for this transaction
@ -429,9 +398,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
h = data.string.strToHash() h = data.string.strToHash()
txDetails = chain.getTransactionKey(h) txDetails = chain.getTransactionKey(h)
header = chain.getBlockHeader(txDetails.blockNumber) header = chain.getBlockHeader(txDetails.blockNumber)
body = chain.getBlockBody(h) body = getBlockBody(header.hash)
if body == nil:
raise newException(ValueError, "Cannot find hash")
var idx = 0 var idx = 0
for receipt in chain.getReceipts(header, Receipt): for receipt in chain.getReceipts(header, Receipt):
if idx == txDetails.index: if idx == txDetails.index:
@ -446,9 +413,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
## Returns BlockObject or nil when no block was found. ## Returns BlockObject or nil when no block was found.
let let
blockHash = data.string.strToHash() blockHash = data.string.strToHash()
body = chain.getBlockBody(blockHash) body = getBlockBody(blockHash)
if body == nil:
raise newException(ValueError, "Cannot find hash")
if quantity < 0 or quantity >= body.uncles.len: if quantity < 0 or quantity >= body.uncles.len:
raise newException(ValueError, "Uncle index out of range") raise newException(ValueError, "Uncle index out of range")
let uncle = body.uncles[quantity] let uncle = body.uncles[quantity]
@ -462,9 +427,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) =
## Returns BlockObject or nil when no block was found. ## Returns BlockObject or nil when no block was found.
let let
header = chain.headerFromTag(quantityTag) header = chain.headerFromTag(quantityTag)
body = chain.getBlockBody(header.hash) body = getBlockBody(header.hash)
if body == nil:
raise newException(ValueError, "Cannot find hash")
if quantity < 0 or quantity >= body.uncles.len: if quantity < 0 or quantity >= body.uncles.len:
raise newException(ValueError, "Uncle index out of range") raise newException(ValueError, "Uncle index out of range")
let uncle = body.uncles[quantity] let uncle = body.uncles[quantity]

View File

@ -60,7 +60,7 @@ proc doTests =
rpcServer = newRpcSocketServer(["localhost:8545"]) rpcServer = newRpcSocketServer(["localhost:8545"])
client = newRpcSocketClient() client = newRpcSocketClient()
setupCommonRpc(rpcServer) setupCommonRpc(rpcServer)
setupP2PRpc(ethNode, rpcServer) setupEthRpc(ethNode, chain, rpcServer)
# Begin tests # Begin tests
rpcServer.start() rpcServer.start()