Added genesis initialization

This commit is contained in:
Yuriy Glukhov 2018-08-01 15:50:44 +03:00
parent 1921f9b389
commit c4f4a37d2c
7 changed files with 249 additions and 46 deletions

View File

@ -8,7 +8,7 @@
# those terms.
import parseopt, strutils
import asyncdispatch2, eth_keys, eth_p2p
import asyncdispatch2, eth_keys, eth_p2p, eth_common
const
NimbusName* = "Nimbus"
@ -94,14 +94,16 @@ type
flags*: set[RpcFlags] ## RPC flags
binds*: seq[TransportAddress] ## RPC bind address
PublicNetwork* = enum
CustomNet = 0
MainNet = 1
MordenNet = 2
RopstenNet = 3
RinkebyNet = 4
KovanNet = 42
NetworkFlags* = enum
## Ethereum network flags
RopstenNet, ## Use test Ropsten network
RinkebyNet, ## Use test Rinkeby network
MordenNet, ## Use test Morden network
KovanNet, ## Use test Kovan network
CustomNet, ## Use custom network
MainNet, ## Use main network only
NoDiscover, ## Peer discovery disabled
V5Discover, ## Dicovery V5 enabled
@ -128,6 +130,22 @@ type
## Debug configuration object
flags*: set[DebugFlags] ## Debug flags
ChainConfig* = object
chainId*: uint
homesteadBlock*: BlockNumber
daoForkBlock*: BlockNumber
daoForkSupport*: bool
# EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
eip150Block*: BlockNumber
eip150Hash*: Hash256
eip155Block*: BlockNumber
eip158Block*: BlockNumber
byzantiumBlock*: BlockNumber
constantinopleBlock*: BlockNumber
NimbusConfiguration* = ref object
## Main Nimbus configuration object
rpc*: RpcConfiguration ## JSON-RPC configuration
@ -138,6 +156,47 @@ var nimbusConfig {.threadvar.}: NimbusConfiguration
proc getConfiguration*(): NimbusConfiguration {.gcsafe.}
proc publicChainConfig*(id: PublicNetwork): ChainConfig =
result = case id
of MainNet:
ChainConfig(
chainId: MainNet.uint,
homesteadBlock: 1150000.u256,
daoForkBlock: 1920000.u256,
daoForkSupport: true,
eip150Block: 2463000.u256,
eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
eip155Block: 2675000.u256,
eip158Block: 2675000.u256,
byzantiumBlock: 4370000.u256
)
of RopstenNet:
ChainConfig(
chainId: RopstenNet.uint,
homesteadBlock: 0.u256,
daoForkSupport: true,
eip150Block: 0.u256,
eip150Hash: toDigest("41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
eip155Block: 10.u256,
eip158Block: 10.u256,
byzantiumBlock: 1700000.u256
)
of RinkebyNet:
ChainConfig(
chainId: RinkebyNet.uint,
homesteadBlock: 1.u256,
daoForkSupport: true,
eip150Block: 2.u256,
eip150Hash: toDigest("9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"),
eip155Block: 3.u256,
eip158Block: 3.u256,
byzantiumBlock: 1035301.u256
)
else:
raise newException(Exception, "No chain config for " & $id)
result.chainId = uint(id)
proc processList(v: string, o: var seq[string]) =
## Process comma-separated list of strings.
if len(v) > 0:
@ -243,42 +302,43 @@ proc processRpcArguments(key, value: string): ConfigStatus =
else:
result = EmptyOption
template setBootnodes(onodes, nodes: untyped): untyped =
proc setBootnodes(onodes: var seq[ENode], nodeUris: openarray[string]) =
var node: ENode
for item in (nodes):
onodes = newSeqOfCap[ENode](nodeUris.len)
for item in nodeUris:
doAssert(processENode(item, node) == Success)
(onodes).add(node)
onodes.add(node)
proc setNetwork(conf: var NetConfiguration, network: NetworkFlags,
id: uint = 0) =
proc toPublicNetwork*(id: uint): PublicNetwork {.inline.} =
for i in PublicNetwork.low .. PublicNetwork.high:
if uint(i) == id:
result = i
break
proc setNetwork(conf: var NetConfiguration, id: PublicNetwork) =
## Set network id and default network bootnodes
conf.flags.excl({MainNet, MordenNet, RopstenNet, RinkebyNet, KovanNet,
CustomNet})
conf.flags.incl(network)
assert(not conf.bootNodes.isNil) # Nim bug #7833
case network
conf.networkId = uint(id)
case id
of MainNet:
conf.networkId = uint(1)
conf.bootNodes.setLen(0)
conf.bootNodes.setBootnodes(MainnetBootnodes)
of MordenNet:
conf.networkId = uint(2)
discard
of RopstenNet:
conf.networkId = uint(3)
conf.bootNodes.setLen(0)
conf.bootNodes.setBootnodes(RopstenBootnodes)
of RinkebyNet:
conf.networkId = uint(4)
conf.bootNodes.setLen(0)
conf.bootNodes.setBootnodes(RinkebyBootnodes)
of KovanNet:
conf.networkId = uint(42)
conf.bootNodes.setLen(0)
conf.bootNodes.setBootnodes(KovanBootnodes)
of CustomNet:
discard
proc setNetwork(conf: var NetConfiguration, id: uint) =
## Set network id and default network bootnodes
let pubNet = toPublicNetwork(id)
if pubNet == CustomNet:
conf.networkId = id
else:
discard
conf.setNetwork(pubNet)
proc processNetArguments(key, value: string): ConfigStatus =
## Processes only `Networking` related command line options
@ -307,24 +367,11 @@ proc processNetArguments(key, value: string): ConfigStatus =
var res = 0
result = processInteger(value, res)
if result == Success:
case res
of 1:
config.net.setNetwork(MainNet)
of 2:
config.net.setNetwork(MordenNet)
of 3:
config.net.setNetwork(RopstenNet)
of 4:
config.net.setNetwork(RinkebyNet)
of 42:
config.net.setNetwork(KovanNet)
else:
config.net.setNetwork(CustomNet, uint(res))
config.net.setNetwork(uint(result))
elif skey == "nodiscover":
config.net.flags.incl(NoDiscover)
elif skey == "v5discover":
config.net.flags.incl(V5Discover)
config.net.bootNodes.setLen(0)
config.net.bootNodes.setBootnodes(DiscoveryV5Bootnodes)
elif skey == "port":
var res = 0
@ -403,8 +450,7 @@ proc initConfiguration(): NimbusConfiguration =
result.rpc.binds = @[initTAddress("127.0.0.1:8545")]
## Network defaults
result.net.bootNodes = @[] # Nim bug #7833
result.net.setNetwork(RopstenNet)
result.net.setNetwork(MainNet)
result.net.maxPeers = 25
result.net.maxPendingPeers = 0
result.net.bindPort = 30303'u16

View File

@ -171,6 +171,8 @@ proc persistHeaderToDb*(self: BaseChainDB; header: BlockHeader): seq[BlockHeader
else: self.getScore(header.parentHash).u256 + header.difficulty
self.db.put(blockHashToScoreKey(header.hash).toOpenArray, rlp.encode(score).toOpenArray)
self.addBlockNumberToHashLookup(header)
var headScore: int
try:
headScore = self.getScore(self.getCanonicalHead().hash)
@ -230,6 +232,25 @@ proc persistBlockToDb*(self: BaseChainDB; blk: Block) =
proc getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB =
result = newAccountStateDB(self.db, stateRoot)
method genesisHash*(db: BaseChainDB): KeccakHash =
db.lookupBlockHash(0.toBlockNumber)
method getBlockHeader*(db: BaseChainDB, b: HashOrNum): BlockHeaderRef =
result.new()
case b.isHash
of true:
result[] = db.getBlockHeaderByHash(b.hash)
else:
result[] = db.getBlockHeaderByHash(db.lookupBlockHash(b.number))
method getBestBlockHeader*(self: BaseChainDB): BlockHeaderRef =
result.new()
result[] = self.getCanonicalHead()
# method getSuccessorHeader*(db: BaseChainDB,
# h: BlockHeader): BlockHeaderRef =
# notImplemented
# method getBlockBody*(db: BaseChainDB, blockHash: KeccakHash): BlockBodyRef =
# notImplemented

112
nimbus/genesis.nim Normal file
View File

@ -0,0 +1,112 @@
import db/[db_chain, state_db], genesis_alloc, eth_common, tables, stint,
byteutils, times, config, rlp, ranges, block_types, eth_trie,
eth_trie/memdb, constants, nimcrypto, chronicles
type
Genesis* = object
config*: ChainConfig
nonce*: BlockNonce
timestamp*: EthTime
extraData*: seq[byte]
gasLimit*: GasInt
difficulty*: DifficultyInt
mixhash*: Hash256
coinbase*: EthAddress
alloc*: GenesisAlloc
GenesisAlloc = TableRef[EthAddress, GenesisAccount]
GenesisAccount = object
code*: seq[byte]
storage*: Table[UInt256, UInt256]
balance*: UInt256
nonce*: UInt256
#privateKey: seq[byte] # For tests?
proc toAddress(n: UInt256): EthAddress =
let a = n.toByteArrayBE()
result[0 .. ^1] = a[12 .. ^1]
proc decodePrealloc(data: seq[byte]): GenesisAlloc =
result = newTable[EthAddress, GenesisAccount]()
for tup in rlp.decode(data.toRange, seq[(UInt256, UInt256)]):
result[toAddress(tup[0])] = GenesisAccount(balance: tup[1])
proc defaultGenesisBlockForNetwork*(id: PublicNetwork): Genesis =
result = case id
of MainNet:
Genesis(
nonce: 66.toBlockNonce,
extraData: hexToSeqByte("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"),
gasLimit: 5000,
difficulty: 17179869184.u256,
alloc: decodePrealloc(mainnetAllocData)
)
of RopstenNet:
Genesis(
nonce: 66.toBlockNonce,
extraData: hexToSeqByte("0x3535353535353535353535353535353535353535353535353535353535353535"),
gasLimit: 16777216,
difficulty: 1048576.u256,
alloc: decodePrealloc(testnetAllocData)
)
of RinkebyNet:
Genesis(
nonce: 66.toBlockNonce,
extraData: hexToSeqByte("0x3535353535353535353535353535353535353535353535353535353535353535"),
gasLimit: 16777216,
difficulty: 1048576.u256,
alloc: decodePrealloc(testnetAllocData)
)
else:
raise newException(Exception, "No default genesis for " & $id)
result.config = publicChainConfig(id)
proc toBlock*(g: Genesis): BlockHeader =
let tdb = trieDB(newMemDB())
var trie = initHexaryTrie(tdb)
var sdb = newAccountStateDB(tdb, trie.rootHash)
for address, account in g.alloc:
sdb.setBalance(address, account.balance)
sdb.setCode(address, account.code.toRange)
sdb.setNonce(address, account.nonce)
for k, v in account.storage:
sdb.setStorage(address, k, v)
var root = sdb.rootHash
block tempRootHashStub: # TODO: Remove this block when we calculate the root hash correctly
if g.config.chainId == 1:
const correctMainnetRootHash = toDigest("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544")
if root != correctMainnetRootHash:
error "Root hash incorrect. Stubbing it out."
root = correctMainnetRootHash
else:
error "Yay! Root hash is correct. Please remove the block where this message comes from."
result = BlockHeader(
nonce: g.nonce,
timestamp: g.timestamp,
extraData: g.extraData,
gasLimit: g.gasLimit,
difficulty: g.difficulty,
mixDigest: g.mixhash,
coinbase: g.coinbase,
stateRoot: root,
parentHash: GENESIS_PARENT_HASH,
txRoot: BLANK_ROOT_HASH,
receiptRoot: BLANK_ROOT_HASH,
ommersHash: EMPTY_UNCLE_HASH
)
if g.gasLimit == 0:
result.gasLimit = GENESIS_GAS_LIMIT
if g.difficulty == 0:
result.difficulty = GENESIS_DIFFICULTY
proc commit*(g: Genesis, db: BaseChainDB) =
let b = g.toBlock()
assert(b.blockNumber == 0, "can't commit genesis block with number > 0")
discard db.persistHeaderToDb(b)

11
nimbus/genesis_alloc.nim Normal file

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@ import
os, strutils, net, eth_common, db/[storage_types, db_chain],
asyncdispatch2, json_rpc/rpcserver, eth_keys,
eth_p2p, eth_p2p/rlpx_protocols/[eth, les],
config, rpc/[common, p2p],
config, genesis, rpc/[common, p2p],
eth_trie
const UseSqlite = true
@ -46,7 +46,12 @@ proc newTrieDb(): TrieDatabaseRef =
result = trieDB(newChainDb(":memory:"))
proc initializeEmptyDb(db: BaseChainDB) =
echo "Initializing empty DB (TODO)"
echo "Writing genesis to DB"
let networkId = getConfiguration().net.networkId.toPublicNetwork()
if networkId == CustomNet:
raise newException(Exception, "Custom genesis not implemented")
else:
defaultGenesisBlockForNetwork(networkId).commit(db)
proc start(): NimbusObject =
var nimbus = NimbusObject()

View File

@ -11,7 +11,8 @@ when true:
./test_memory,
./test_stack,
./test_opcode,
./test_storage_backends
./test_storage_backends,
./test_genesis
when false:
import ./test_vm_json

7
tests/test_genesis.nim Normal file
View File

@ -0,0 +1,7 @@
import unittest, ../nimbus/[genesis, config], eth_common
suite "Genesis":
test "Correct mainnet hash":
let g = defaultGenesisBlockForNetwork(MainNet)
let b = g.toBlock
check(b.blockHash == "D4E56740F876AEF8C010B86A40D5F56745A118D0906A34E69AEC8C0DB1CB8FA3".toDigest)