# Digging into IPLD blocks. ### JSON Decoded * IPLD is stored in `blocks` table * Actual data in stored in `eth.` tables which are indexed. #### Data for block num 2 mh_key: `/blocks/DMQGSADIA6THF5FDH42G47QWCC6LWI3ORTOBVCUU3ZS4QUV5HA65SIY` CID: `bagiacgzaneagqb5gol2kgpzunz7bmef4xmrw5dg4dkfjjxtfzbjl2ob53erq` JSON IPLD: ```json= { "bloom": "0x00000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000002000020000000000000000000800000000000000000000000010000000000000000000001000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000004000020000000000000000000000000000000000000000800000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "difficulty": 2, "extra": "0xd883010a02846765746888676f312e31352e35856c696e757800000000000000f599bfe3192d77e03ed67ba1d35c285c493457c7e0d54cd872b90c954dbe2f100a6306b1f1887f764393e02887c657d9a5f9d2a6f00ae3fcc155f8714005f71701", "gaslimit": 280925489332226, "gasused": 1228199, "mixdigest": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": 2, "parent": { "/": "bagiacgzaxfsywswwzpi624hr6bbgwo2q3ofwdp5jaipb7zgj27s4e3cj5t6q" }, "receipts": { "/": "bagkacgzabc5v6foe263w342zv2pygyu7nqmlhlggbedtvsu36tafmv3nntlq" }, "root": { "/": "baglacgzaofo7kmaojlhmg7bd6ywn24j5sj2qnzgciyofnx6wixlamwgwc6za" }, "time": 1629445383, "tx": { "/": "bagjacgza2bf5epg2fn2jytr2b5fuaahox65pb5f6rvoh4aypak6okrnov7qq" }, "uncles": { "/": "bagiqcgzadxge32g6y5oxvk4fwvt3ntgudljreri3ssfhie7qufbp2qgusndq" } } ``` Parent(Block 1): `bagiacgzaxfsywswwzpi624hr6bbgwo2q3ofwdp5jaipb7zgj27s4e3cj5t6q` #### Relation between `mh_key` and `cid` ```go= // indexer_test.go // MultihashKeyFromCID converts a cid into a blockstore-prefixed multihash db key string func MultihashKeyFromCID(c cid.Cid) string { dbKey := dshelp.MultihashToDsKey(c.Hash()) return blockstore.BlockPrefix.String() + dbKey.String() } func TestCIDtoMhKey(t *testing.T) { headerCID, err := cid.Parse("bagiacgzaneagqb5gol2kgpzunz7bmef4xmrw5dg4dkfjjxtfzbjl2ob53erq") require.NoError(t, err) mhKey := shared.MultihashKeyFromCID(headerCID) require.Equal(t, mhKey, "/blocks/DMQGSADIA6THF5FDH42G47QWCC6LWI3ORTOBVCUU3ZS4QUV5HA65SIY") } ``` #### Relation between `data` and `cid` ```go= // NewEthHeader converts a *types.Header into an EthHeader IPLD node func NewEthHeader(header *types.Header) (*EthHeader, error) { headerRLP, err := rlp.EncodeToBytes(header) if err != nil { return nil, err } c, err := RawdataToCid(MEthHeader, headerRLP, mh.KECCAK_256) if err != nil { return nil, err } return &EthHeader{ Header: header, cid: c, rawdata: headerRLP, }, nil } ``` We take the RLP encoding of any struct and then covert it to CID by adding a data type to it ### Verify tx root hash in block header ```go= func TestTxHash(t *testing.T) { var ethTxNodes []*EthTx transactionTrie := newTxTrie() txData, err := ioutil.ReadFile("./test_data/tx_data") require.NoError(t, err) tx := new(types.Transaction) tx.UnmarshalBinary(txData) ethTx, err := NewEthTx(tx) require.NoError(t, err) ethTxNodes = append(ethTxNodes, ethTx) // There is only one txn err = transactionTrie.add(0, ethTx.RawData()) require.NoError(t, err) txRHash := common.BytesToHash(transactionTrie.rootHash()) cid := commonHashToCid(MEthTxTrie, txRHash) fmt.Println(cid) require.Equal(t, cid.String(), "bagjacgza2bf5epg2fn2jytr2b5fuaahox65pb5f6rvoh4aypak6okrnov7qq") } ``` ```go= func TestTxHash(t *testing.T) { var ethTxNodes []*EthTx transactionTrie := newTxTrie() txData, err := ioutil.ReadFile("./test_data/tx_data") require.NoError(t, err) tx := new(types.Transaction) tx.UnmarshalBinary(txData) ethTx, err := NewEthTx(tx) require.NoError(t, err) ethTxNodes = append(ethTxNodes, ethTx) // There is only one txn err = transactionTrie.add(0, ethTx.RawData()) require.NoError(t, err) txRHash := common.BytesToHash(transactionTrie.rootHash()) cid := commonHashToCid(MEthTxTrie, txRHash) n, err := transactionTrie.getNodes() require.NoError(t, err) fmt.Println("Length: ", len(n)) // Same data in stored twice. One as txn trie leaf and another time as txn. fmt.Println("Tx encoded data:", common.Bytes2Hex(ethTx.RawData())) fmt.Println("Trie node:", common.Bytes2Hex(n[0].RawData())) fmt.Println("CID:", cid) require.Equal(t, cid.String(), "bagjacgza2bf5epg2fn2jytr2b5fuaahox65pb5f6rvoh4aypak6okrnov7qq") } ``` #### Decoding a TX IPLD ```go= func TestTxHash(t *testing.T) { txData, err := ioutil.ReadFile("./test_data/tx_data") require.NoError(t, err) tx := new(types.Transaction) tx.UnmarshalBinary(txData) data, err := json.MarshalIndent(tx, " ", " ") require.NoError(t, err) fmt.Println(string(data)) } ``` #### Converting log trie node to log data ```go= func TestProcessLogs(t *testing.T) { logs := []*types.Log{mocks.MockLog1, mocks.MockLog2} nodes, cids, _, err := processLogs(logs) require.NoError(t, err) for idx, n := range nodes { var nodeElements []interface{} rlp.DecodeBytes(n.RawData(), &nodeElements) require.NoError(t, err) // This is not needed var log types.Log err = rlp.DecodeBytes(nodeElements[1].([]byte), &log) require.NoError(t, err) fmt.Println("Log: ", log) logRaw, err := rlp.EncodeToBytes(logs[idx]) require.NoError(t, err) require.Equal(t, logRaw, nodeElements[1].([]byte)) } require.GreaterOrEqual(t, len(nodes), len(logs)) require.Equal(t, len(logs), len(cids)) } ``` ### Trie keys is index https://ethereum.stackexchange.com/questions/35784/verifying-a-transaction-root-by-hand **Important** https://eth.wiki/en/fundamentals/patricia-tree https://ethereum.stackexchange.com/questions/16117/proving-the-existence-of-logs-to-the-blockchain ``` Each receipt, denoted BR[i] for the ith transaction, is placed in an index-keyed trie and the root recorded in the header as He. The transaction receipt is a tuple of four items comprising the post-transaction state, Rσ, the cumulative gas used in the block containing the transaction receipt as of immediately after the transaction has happened, Ru, the set of logs created through execution of the transaction, Rl and the Bloom filter composed from information in those logs... ``` https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/