diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index b443eac106..0b79739646 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -507,6 +507,8 @@ async function run() { const chainName = path.parse(args.gethGenesis).base.split('.')[0] const genesisParams = await parseCustomParams(genesisFile, chainName) const genesisState = genesisFile.alloc ? await parseGenesisState(genesisFile) : {} + console.log('Genesis block hash: ', genesisParams.genesis.hash) + console.log('Genesis state root: ', genesisParams.genesis.stateRoot) common = new Common({ chain: genesisParams.name, customChains: [[genesisParams, genesisState]], diff --git a/packages/common/package.json b/packages/common/package.json index 948e5addbb..249fdd9588 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -32,6 +32,7 @@ "lint:fix": "../../config/cli/lint-fix.sh", "tape": "tape -r ts-node/register", "test": "npm run test:node && npm run test:browser", + "test:temp": "npm run tape -- ./tests/customChains.spec.ts", "test:node": "npm run tape -- ./tests/*.spec.ts", "test:browser": "karma start karma.conf.js", "docs:build": "typedoc --options typedoc.js" diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index cd186ca39f..490c609f49 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -9,6 +9,7 @@ import { Chain as IChain, GenesisBlock, GenesisState, + GethGenesisState, Hardfork as HardforkParams, } from './types' @@ -132,7 +133,7 @@ export interface CommonOpts extends BaseOpts { * Initialize (in addition to the supported chains) with the selected * custom chains * - * Usage (directly with the respective chain intialization via the {@link CommonOpts.chain} option): + * Usage (directly with the respective chain initialization via the {@link CommonOpts.chain} option): * * Pattern 1 (without genesis state): * @@ -149,7 +150,7 @@ export interface CommonOpts extends BaseOpts { * const common = new Common({ chain: 'myCustomChain1', customChains: [ [ myCustomChain1, chain1GenesisState ] ]}) * ``` */ - customChains?: IChain[] | [IChain, GenesisState][] + customChains?: IChain[] | [IChain, GenesisState | GethGenesisState][] } /** @@ -185,7 +186,7 @@ export default class Common extends EventEmitter { private _hardfork: string | Hardfork private _supportedHardforks: Array = [] private _eips: number[] = [] - private _customChains: IChain[] | [IChain, GenesisState][] + private _customChains: IChain[] | [IChain, GenesisState | GethGenesisState][] /** * Creates a {@link Common} object for a custom chain, based on a standard one. @@ -379,22 +380,33 @@ export default class Common extends EventEmitter { * representation. Or, a Dictionary of chain parameters for a private network. * @returns The dictionary with parameters set as chain */ - setChain(chain: string | number | Chain | BN | object): any { + setChain(chain: string | number | Chain | BN | object): IChain { if (typeof chain === 'number' || typeof chain === 'string' || BN.isBN(chain)) { // Filter out genesis states if passed in to customChains let plainCustomChains: IChain[] - if ( - this._customChains && - this._customChains.length > 0 && - Array.isArray(this._customChains[0]) - ) { - plainCustomChains = (this._customChains as [IChain, GenesisState][]).map((e) => e[0]) + if (this._customChains?.length && Array.isArray(this._customChains[0])) { + plainCustomChains = (this._customChains as [IChain, GenesisState | GethGenesisState][]).map( + ([chain, state]) => { + if ('hash' && 'stateRoot' in state) { + return { + ...chain, + genesis: { + ...chain.genesis, + hash: state.hash, + stateRoot: state.stateRoot, + }, + } as IChain + } + + return chain + } + ) } else { plainCustomChains = this._customChains as IChain[] } this._chainParams = Common._getChainParams(chain, plainCustomChains) } else if (typeof chain === 'object') { - if (this._customChains.length > 0) { + if (this._customChains.length) { throw new Error( 'Chain must be a string, number, or BN when initialized with customChains passed in' ) @@ -1001,6 +1013,15 @@ export default class Common extends EventEmitter { * all values are provided as hex-prefixed strings. */ genesisState(): GenesisState { + // Custom chains with genesis state provided + if (this._customChains?.length && Array.isArray(this._customChains[0])) { + for (const chainArrayWithGenesis of this._customChains as [IChain, GenesisState][]) { + if (chainArrayWithGenesis[0].name === this.chainName()) { + return chainArrayWithGenesis[1] + } + } + } + // Use require statements here in favor of import statements // to load json files on demand // (high memory usage by large mainnet.json genesis state file) @@ -1019,19 +1040,6 @@ export default class Common extends EventEmitter { return require('./genesisStates/sepolia.json') } - // Custom chains with genesis state provided - if ( - this._customChains && - this._customChains.length > 0 && - Array.isArray(this._customChains[0]) - ) { - for (const chainArrayWithGenesis of this._customChains as [IChain, GenesisState][]) { - if (chainArrayWithGenesis[0].name === this.chainName()) { - return chainArrayWithGenesis[1] - } - } - } - return {} } diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 99c659e20c..79f3b241da 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -40,10 +40,21 @@ export interface Chain { } } -export interface GenesisState { - [key: string]: string | [string, [[string, string]]] // balance | [balance, code, [[storageKey, storageValue]]] +export interface GethGenesisState { + json?: any + hash: string + stateRoot: string +} + +export interface AccountState { + balance: string + nonce: string + code?: string + storage?: Record } +export type GenesisState = Record + export interface eipsType { [key: number]: any } diff --git a/packages/common/tests/customChains.spec.ts b/packages/common/tests/customChains.spec.ts index a636a2c3df..09d6a3d2a4 100644 --- a/packages/common/tests/customChains.spec.ts +++ b/packages/common/tests/customChains.spec.ts @@ -4,10 +4,9 @@ import Common, { Chain, ConsensusType, CustomChain, Hardfork } from '../src/' import testnet from './data/testnet.json' import testnet2 from './data/testnet2.json' import testnet3 from './data/testnet3.json' +import { AccountState, Chain as IChain, GenesisState, GethGenesisState } from '../src/types' -import { Chain as IChain, GenesisState } from '../src/types' - -tape('[Common]: Custom chains', function (t: tape.Test) { +tape.only('[Common]: Custom chains', function (t: tape.Test) { t.test( 'chain -> object: should provide correct access to private network chain parameters', function (st: tape.Test) { @@ -210,4 +209,44 @@ tape('[Common]: Custom chains', function (t: tape.Test) { st.end() }) + + t.test('custom genesis state', function (st: tape.Test) { + const mockedCode = '0xcde' + const firstAddress = '0x0000000000000000000000000000000000000001' + const genesisState = { + '0x0000000000000000000000000000000000000000': '0x1', + [firstAddress]: { + balance: '0x1', + nonce: '0x', + storage: {}, + code: mockedCode, + }, + } + const customChainsWithGenesis: [IChain, GenesisState][] = [[testnet, genesisState]] + const c = new Common({ + chain: 'testnet', + customChains: customChainsWithGenesis, + }) + + const expectedGenesisState = c.genesisState()[firstAddress] as AccountState + + st.equal(c.genesisState(), genesisState) + st.equal(expectedGenesisState.code, genesisState[firstAddress].code) + st.end() + }) + + t.test('custom hash and state root sent in genesis state', function (st: tape.Test) { + const genesisState = { + stateRoot: 'cool-state-root', + hash: 'cool-hash', + } + const customChainsWithGenesis: [IChain, GethGenesisState][] = [[testnet, genesisState]] + const c = new Common({ + chain: 'testnet', + customChains: customChainsWithGenesis, + }) + st.equal(c.genesis().hash, genesisState.hash) + st.equal(c.genesis().stateRoot, genesisState.stateRoot) + st.end() + }) })