Go-Ethereum Genesis Block – Why No Transactions in Geth Private Network?

genesisgo-ethereum

I started geth in dev mode by the following command:

geth --datadir /home/mehrdad/eth/data_bc/ -http -dev

just after the blockchain was started I tried to see created accounts (in interactive geth console)

> eth.accounts
["0x850859299b5ac91f816fd5d71e2b0874f21693be"]

and this account balance was :

> eth.getBalance('0x850859299b5ac91f816fd5d71e2b0874f21693be')
1.15792089237316195423570985008687907853269984665640564038680477259368543059927e+77

but I can not find any transactions in the genesis block

> eth.getBlock(0)
{
  baseFeePerGas: 1000000000,
  difficulty: 1,
  extraData: "0x0000000000000000000000000000000000000000000000000000000000000000850859299b5ac91f816fd5d71e2b0874f21693be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  gasLimit: 11500000,
  gasUsed: 0,
  hash: "0xdbcd7720b77876158ac7b9108990dee1a8a3b51998b5f831a9f7fe3122c2b635",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x0000000000000000000000000000000000000000",
  mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  nonce: "0x0000000000000000",
  number: 0,
  parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 627,
  stateRoot: "0xd6187edd886476ba6188162d8217f74aef98c7b5dd68f9a23758c4acbd3f2a0e",
  timestamp: 0,
  totalDifficulty: 1,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}

I wonder where is the initial allocation data (initial balance) of the account is saved in the block chain?

I was looking for transactions in the genesis block (like the genesis block of Ethereum main net) but there was no transactions.

I thought the blocks are the most fundamental data structures in the ethereum block chain, and by running all blocks, starting with the genesis, one can obtain the current state of the system. now I think that maybe there is also the initial state for accounts that its information is not included in any block.
am I right or I am missing something ?

and how can I get this information by Web3 libraries?

Best Answer

You can't have a transaction in Genesis block because to send a transaction you need to increment the nonce, but there is no account yet before genesis block so you don't know the value of the nonce field.

The genesis block is created by a separate process from the block processing function. It doesn't go through consensus algorithm either. The genesis block is created by special function that only creates accounts with their balances and nonces:

func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideLondon *big.Int) (*params.ChainConfig, common.Hash, error) {
    if genesis != nil && genesis.Config == nil {
        return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
    }
    // Just commit the new block if there is no stored genesis block.
    stored := rawdb.ReadCanonicalHash(db, 0)
    if (stored == common.Hash{}) {
        if genesis == nil {
            log.Info("Writing default main-net genesis block")
            genesis = DefaultGenesisBlock()
        } else {
            log.Info("Writing custom genesis block")
        }
        block, err := genesis.Commit(db)
        if err != nil {
            return genesis.Config, common.Hash{}, err
        }
        return genesis.Config, block.Hash(), nil
    }
    // We have the genesis block in database(perhaps in ancient database)
    // but the corresponding state is missing.
    header := rawdb.ReadHeader(db, stored, 0)
    if _, err := state.New(header.Root, state.NewDatabaseWithConfig(db, nil), nil); err != nil {
        if genesis == nil {
            genesis = DefaultGenesisBlock()
        }
        // Ensure the stored genesis matches with the given one.
        hash := genesis.ToBlock(nil).Hash()
        if hash != stored {
            return genesis.Config, hash, &GenesisMismatchError{stored, hash}
        }
        block, err := genesis.Commit(db)
        if err != nil {
            return genesis.Config, hash, err
        }
        return genesis.Config, block.Hash(), nil
    }
    // Check whether the genesis block is already written.
    if genesis != nil {
        hash := genesis.ToBlock(nil).Hash()
        if hash != stored {
            return genesis.Config, hash, &GenesisMismatchError{stored, hash}
        }
    }
    // Get the existing chain configuration.
    newcfg := genesis.configOrDefault(stored)
    if overrideLondon != nil {
        newcfg.LondonBlock = overrideLondon
    }
    if err := newcfg.CheckConfigForkOrder(); err != nil {
        return newcfg, common.Hash{}, err
    }
    storedcfg := rawdb.ReadChainConfig(db, stored)
    if storedcfg == nil {
        log.Warn("Found genesis block without chain config")
        rawdb.WriteChainConfig(db, stored, newcfg)
        return newcfg, stored, nil
    }
    // Special case: don't change the existing config of a non-mainnet chain if no new
    // config is supplied. These chains would get AllProtocolChanges (and a compat error)
    // if we just continued here.
    if genesis == nil && stored != params.MainnetGenesisHash {
        return storedcfg, stored, nil
    }
    // Check config compatibility and write the config. Compatibility errors
    // are returned to the caller unless we're already at block zero.
    height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db))
    if height == nil {
        return newcfg, stored, fmt.Errorf("missing block number for head header hash")
    }
    compatErr := storedcfg.CheckCompatible(newcfg, *height)
    if compatErr != nil && *height != 0 && compatErr.RewindTo != 0 {
        return newcfg, stored, compatErr
    }
    rawdb.WriteChainConfig(db, stored, newcfg)
    return newcfg, stored, nil
}

https://github.com/ethereum/go-ethereum/blob/dfeb2f7e8001aef1005a8d5e1605bae1de0b4f12/core/genesis.go#L161

So, basicaly it parses the JSON file and adds accounts to the State, after that it generates the hash of the block with Parent Hash = 0x00000...0000

now I think that maybe there is also the initial state for accounts that its information is not included in any block.

the information about the state of accounts is included in the StateDB, you can walk the Trie and find it out. It is just not built from transactions but from JSON file. To walk the trie you need to write your own code reusing packages from go-ethereum, it is not provided in the RPC API

Related Topic