# Nimbus # Copyright (c) 2018-2023 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. {.push raises: [].} import std/tables, eth/[common, eip1559], eth/trie/trie_defs, ../db/[ledger, core_db, state_db/read_write], ../constants, ./chain_config # Annotation helpers {.pragma: noRaise, gcsafe, raises: [].} {.pragma: rlpRaise, gcsafe, raises: [RlpError].} {.pragma: catchRaise, gcsafe, raises: [CatchableError].} type GenesisAddAccountFn = proc( address: EthAddress; nonce: AccountNonce; balance: UInt256; code: openArray[byte]) {.catchRaise.} GenesisCompensateLegacySetupFn = proc() {.noRaise.} GenesisSetStorageFn = proc( address: EthAddress; slot: UInt256; val: UInt256) {.rlpRaise.} GenesisCommitFn = proc() {.noRaise.} GenesisRootHashFn = proc: Hash256 {.noRaise.} GenesisGetTrieFn = proc: CoreDbMptRef {.noRaise.} GenesisLedgerRef* = ref object ## Exportable ledger DB just for initialising Genesis. This is needed ## when using the `Aristo` backend which is not fully supported by the ## `AccountStateDB` object. ## ## Currently, using other than the `AccountStateDB` ledgers are ## experimental and test only. Eventually, the `GenesisLedgerRef` wrapper ## should disappear so that the `Ledger` object (which encapsulates ## `AccountsCache` and `AccountsLedger`) will prevail. ## addAccount: GenesisAddAccountFn compensateLegacySetup: GenesisCompensateLegacySetupFn setStorage: GenesisSetStorageFn commit: GenesisCommitFn rootHash: GenesisRootHashFn getTrie: GenesisGetTrieFn const GenesisLedgerTypeDefault* = LedgerType(0) ## Default ledger type to use, `LedgerType(0)` uses `AccountStateDB` ## rather than a `Ledger` variant. # ------------------------------------------------------------------------------ # Private functions # ------------------------------------------------------------------------------ proc initStateDbledgerRef(db: CoreDbRef; pruneTrie: bool): GenesisLedgerRef = let sdb = newAccountStateDB(db, emptyRlpHash, pruneTrie) GenesisLedgerRef( addAccount: proc( address: EthAddress; nonce: AccountNonce; balance: UInt256; code: openArray[byte]; ) {.catchRaise.} = sdb.setAccount(address, newAccount(nonce, balance)) sdb.setCode(address, code), compensateLegacySetup: proc() = if pruneTrie: db.compensateLegacySetup(), setStorage: proc( address: EthAddress; slot: UInt256; val: UInt256; ) {.rlpRaise.} = sdb.setStorage(address, slot, val), commit: proc() = discard, rootHash: proc(): Hash256 = sdb.rootHash(), getTrie: proc(): CoreDbMptRef = sdb.getTrie()) proc initAccountsLedgerRef( db: CoreDbRef; pruneTrie: bool; ledgerType: LedgerType; ): GenesisLedgerRef = let ac = ledgerType.init(db, emptyRlpHash, pruneTrie) GenesisLedgerRef( addAccount: proc( address: EthAddress; nonce: AccountNonce; balance: UInt256; code: openArray[byte]; ) = ac.setNonce(address, nonce) ac.setBalance(address, balance) ac.setCode(address, @code), compensateLegacySetup: proc() = if pruneTrie: db.compensateLegacySetup(), setStorage: proc( address: EthAddress; slot: UInt256; val: UInt256; ) = ac.setStorage(address, slot, val), commit: proc() = ac.persist(), rootHash: proc(): Hash256 = ac.rootHash(), getTrie: proc(): CoreDbMptRef = ac.getMpt()) # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ proc newStateDB*( db: CoreDbRef; pruneTrie: bool; ledgerType = LedgerType(0); ): GenesisLedgerRef = ## The flag `ledgerType` is set to zero for compatibility with legacy apps ## (see `test_state_network`). if ledgerType != LedgerType(0): db.initAccountsLedgerRef(pruneTrie, ledgerType) else: db.initStateDbledgerRef pruneTrie proc getTrie*(sdb: GenesisLedgerRef): CoreDbMptRef = ## Getter, used in `test_state_network` sdb.getTrie() proc toGenesisHeader*( g: Genesis; sdb: GenesisLedgerRef; fork: HardFork; ): BlockHeader {.gcsafe, raises: [CatchableError].} = ## Initialise block chain DB accounts derived from the `genesis.alloc` table ## of the `db` descriptor argument. ## ## The function returns the `Genesis` block header. ## # The following kludge is needed for the `LegacyDbPersistent` type database # when `pruneTrie` is enabled. For other cases, this code is irrelevant. sdb.compensateLegacySetup() for address, account in g.alloc: sdb.addAccount(address, account.nonce, account.balance, account.code) # Kludge: # # See https://github.com/status-im/nim-eth/issues/9 where other, # probably related debilities are discussed. # # This kludge also fixes the initial crash described in # https://github.com/status-im/nimbus-eth1/issues/932. sdb.compensateLegacySetup() # <-- kludge for k, v in account.storage: sdb.setStorage(address, k, v) sdb.commit() result = BlockHeader( nonce: g.nonce, timestamp: g.timestamp, extraData: g.extraData, gasLimit: g.gasLimit, difficulty: g.difficulty, mixDigest: g.mixHash, coinbase: g.coinbase, stateRoot: sdb.rootHash(), parentHash: GENESIS_PARENT_HASH, txRoot: EMPTY_ROOT_HASH, receiptRoot: EMPTY_ROOT_HASH, ommersHash: EMPTY_UNCLE_HASH ) if g.baseFeePerGas.isSome: result.baseFee = g.baseFeePerGas.get() elif fork >= London: result.baseFee = EIP1559_INITIAL_BASE_FEE.u256 if g.gasLimit == 0: result.gasLimit = GENESIS_GAS_LIMIT if g.difficulty.isZero and fork <= London: result.difficulty = GENESIS_DIFFICULTY if fork >= Shanghai: result.withdrawalsRoot = some(EMPTY_ROOT_HASH) if fork >= Cancun: result.blobGasUsed = g.blobGasUsed.get(0'u64).some result.excessBlobGas = g.excessBlobGas.get(0'u64).some result.parentBeaconBlockRoot = g.parentBeaconBlockRoot.get(Hash256()).some proc toGenesisHeader*( genesis: Genesis; fork: HardFork; chainId: ChainId; db = CoreDbRef(nil); ledgerType = GenesisLedgerTypeDefault; ): BlockHeader {.gcsafe, raises: [CatchableError].} = ## Generate the genesis block header from the `genesis` and `config` ## argument value. let db = if db.isNil: newCoreDbRef LegacyDbMemory, chainId else: db sdb = newStateDB(db, pruneTrie = true, ledgerType) toGenesisHeader(genesis, sdb, fork) proc toGenesisHeader*( params: NetworkParams; db = CoreDbRef(nil); ledgerType = GenesisLedgerTypeDefault; ): BlockHeader {.raises: [CatchableError].} = ## Generate the genesis block header from the `genesis` and `config` ## argument value. let map = toForkTransitionTable(params.config) let fork = map.toHardFork(forkDeterminationInfo(0.toBlockNumber, params.genesis.timestamp)) toGenesisHeader(params.genesis, fork, params.config.chainId, db, ledgerType) # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------