diff --git a/pkg/eth/interfaces.go b/pkg/eth/interfaces.go deleted file mode 100644 index 9e2dcae3..00000000 --- a/pkg/eth/interfaces.go +++ /dev/null @@ -1,47 +0,0 @@ -// VulcanizeDB -// Copyright © 2019 Vulcanize - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package eth - -import ( - "context" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/rpc" -) - -// FilterBackend is the geth interface we need to satisfy to use their filters -type FilterBackend interface { - ChainDb() ethdb.Database - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) - HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) - - SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription - SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription - SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription - SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription - SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription - - BloomStatus() (uint64, uint64) - ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) -} diff --git a/pkg/eth/ipld_retriever.go b/pkg/eth/ipld_retriever.go deleted file mode 100644 index eb849ca1..00000000 --- a/pkg/eth/ipld_retriever.go +++ /dev/null @@ -1,367 +0,0 @@ -// VulcanizeDB -// Copyright © 2019 Vulcanize - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package eth - -import ( - "fmt" - "strconv" - - "github.com/cerc-io/ipld-eth-server/v4/pkg/shared" - "github.com/ethereum/go-ethereum/statediff/trie_helpers" - sdtypes "github.com/ethereum/go-ethereum/statediff/types" - "github.com/jmoiron/sqlx" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" -) - -const ( - RetrieveHeaderByHashPgStr = `SELECT cid, data - FROM eth.header_cids - INNER JOIN ipld.blocks ON ( - header_cids.cid = blocks.key - AND header_cids.block_number = blocks.block_number - ) - WHERE block_hash = $1` - RetrieveUnclesPgStr = `SELECT uncle_cids.cid, data - FROM eth.uncle_cids - INNER JOIN eth.header_cids ON ( - uncle_cids.header_id = header_cids.block_hash - AND uncle_cids.block_number = header_cids.block_number - ) - INNER JOIN ipld.blocks ON ( - uncle_cids.cid = blocks.key - AND uncle_cids.block_number = blocks.block_number - ) - WHERE header_cids.block_hash = $1 - AND header_cids.block_number = $2 - ORDER BY uncle_cids.parent_hash` - RetrieveUnclesByBlockHashPgStr = `SELECT uncle_cids.cid, data - FROM eth.uncle_cids - INNER JOIN eth.header_cids ON ( - uncle_cids.header_id = header_cids.block_hash - AND uncle_cids.block_number = header_cids.block_number - ) - INNER JOIN ipld.blocks ON ( - uncle_cids.cid = blocks.key - AND uncle_cids.block_number = blocks.block_number - ) - WHERE header_cids.block_hash = $1 - ORDER BY uncle_cids.parent_hash` - RetrieveTransactionsPgStr = `SELECT transaction_cids.cid, data - FROM eth.transaction_cids - INNER JOIN eth.header_cids ON ( - transaction_cids.header_id = header_cids.block_hash - AND transaction_cids.block_number = header_cids.block_number - ) - INNER JOIN ipld.blocks ON ( - transaction_cids.cid = blocks.key - AND transaction_cids.block_number = blocks.block_number - ) - WHERE block_hash = $1 - AND header_cids.block_number = $2 - ORDER BY eth.transaction_cids.index ASC` - RetrieveTransactionsByBlockHashPgStr = `SELECT transaction_cids.cid, data - FROM eth.transaction_cids - INNER JOIN eth.header_cids ON ( - transaction_cids.header_id = header_cids.block_hash - AND transaction_cids.block_number = header_cids.block_number - ) - INNER JOIN ipld.blocks ON ( - transaction_cids.cid = blocks.key - AND transaction_cids.block_number = blocks.block_number - ) - WHERE block_hash = $1 - ORDER BY eth.transaction_cids.index ASC` - RetrieveReceiptsPgStr = `SELECT receipt_cids.cid, data, eth.transaction_cids.tx_hash - FROM eth.receipt_cids - INNER JOIN eth.transaction_cids ON ( - receipt_cids.tx_id = transaction_cids.tx_hash - AND receipt_cids.header_id = transaction_cids.header_id - AND receipt_cids.block_number = transaction_cids.block_number - ) - INNER JOIN eth.header_cids ON ( - transaction_cids.header_id = header_cids.block_hash - AND transaction_cids.block_number = header_cids.block_number - ) - INNER JOIN ipld.blocks ON ( - receipt_cids.cid = blocks.key - AND receipt_cids.block_number = blocks.block_number - ) - WHERE block_hash = $1 - AND header_cids.block_number = $2 - ORDER BY eth.transaction_cids.index ASC` - RetrieveReceiptsByBlockHashPgStr = `SELECT receipt_cids.cid, data, eth.transaction_cids.tx_hash - FROM eth.receipt_cids - INNER JOIN eth.transaction_cids ON ( - receipt_cids.tx_id = transaction_cids.tx_hash - AND receipt_cids.header_id = transaction_cids.header_id - AND receipt_cids.block_number = transaction_cids.block_number - ) - INNER JOIN eth.header_cids ON ( - transaction_cids.header_id = header_cids.block_hash - AND transaction_cids.block_number = header_cids.block_number - ) - INNER JOIN ipld.blocks ON ( - receipt_cids.cid = blocks.key - AND receipt_cids.block_number = blocks.block_number - ) - WHERE block_hash = $1 - ORDER BY eth.transaction_cids.index ASC` - RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.cid, state_cids.block_number, state_cids.node_type - FROM eth.state_cids - INNER JOIN eth.header_cids ON ( - state_cids.header_id = header_cids.block_hash - AND state_cids.block_number = header_cids.block_number - ) - WHERE state_leaf_key = $1 - AND header_cids.block_number <= (SELECT block_number - FROM eth.header_cids - WHERE block_hash = $2) - AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number)) - ORDER BY header_cids.block_number DESC - LIMIT 1` - RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT cid, block_number, node_type, state_leaf_removed FROM get_storage_at_by_hash($1, $2, $3)` -) - -var EmptyNodeValue = make([]byte, common.HashLength) - -type rctIpldResult struct { - LeafCID string `db:"cid"` - Data []byte `db:"data"` - TxHash string `db:"tx_hash"` -} - -type ipldResult struct { - CID string `db:"cid"` - Data []byte `db:"data"` - TxHash string `db:"tx_hash"` -} - -type IPLDRetriever struct { - db *sqlx.DB -} - -func NewIPLDRetriever(db *sqlx.DB) *IPLDRetriever { - return &IPLDRetriever{ - db: db, - } -} - -// RetrieveHeaderByHash returns the cid and rlp bytes for the header corresponding to the provided block hash -func (r *IPLDRetriever) RetrieveHeaderByHash(tx *sqlx.Tx, hash common.Hash) (string, []byte, error) { - headerResult := new(ipldResult) - return headerResult.CID, headerResult.Data, tx.Get(headerResult, RetrieveHeaderByHashPgStr, hash.Hex()) -} - -// RetrieveUncles returns the cids and rlp bytes for the uncles corresponding to the provided block hash, number (of non-omner root block) -func (r *IPLDRetriever) RetrieveUncles(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) { - uncleResults := make([]ipldResult, 0) - if err := tx.Select(&uncleResults, RetrieveUnclesPgStr, hash.Hex(), number); err != nil { - return nil, nil, err - } - cids := make([]string, len(uncleResults)) - uncles := make([][]byte, len(uncleResults)) - for i, res := range uncleResults { - cids[i] = res.CID - uncles[i] = res.Data - } - return cids, uncles, nil -} - -// RetrieveUnclesByBlockHash returns the cids and rlp bytes for the uncles corresponding to the provided block hash (of non-omner root block) -func (r *IPLDRetriever) RetrieveUnclesByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]string, [][]byte, error) { - uncleResults := make([]ipldResult, 0) - if err := tx.Select(&uncleResults, RetrieveUnclesByBlockHashPgStr, hash.Hex()); err != nil { - return nil, nil, err - } - cids := make([]string, len(uncleResults)) - uncles := make([][]byte, len(uncleResults)) - for i, res := range uncleResults { - cids[i] = res.CID - uncles[i] = res.Data - } - return cids, uncles, nil -} - -// RetrieveTransactions returns the cids and rlp bytes for the transactions corresponding to the provided block hash, number -func (r *IPLDRetriever) RetrieveTransactions(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) { - txResults := make([]ipldResult, 0) - if err := tx.Select(&txResults, RetrieveTransactionsPgStr, hash.Hex(), number); err != nil { - return nil, nil, err - } - cids := make([]string, len(txResults)) - txs := make([][]byte, len(txResults)) - for i, res := range txResults { - cids[i] = res.CID - txs[i] = res.Data - } - return cids, txs, nil -} - -// RetrieveTransactionsByBlockHash returns the cids and rlp bytes for the transactions corresponding to the provided block hash -func (r *IPLDRetriever) RetrieveTransactionsByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]string, [][]byte, error) { - txResults := make([]ipldResult, 0) - if err := tx.Select(&txResults, RetrieveTransactionsByBlockHashPgStr, hash.Hex()); err != nil { - return nil, nil, err - } - cids := make([]string, len(txResults)) - txs := make([][]byte, len(txResults)) - for i, res := range txResults { - cids[i] = res.CID - txs[i] = res.Data - } - return cids, txs, nil -} - -// DecodeLeafNode decodes the leaf node data -func DecodeLeafNode(node []byte) ([]byte, error) { - var nodeElements []interface{} - if err := rlp.DecodeBytes(node, &nodeElements); err != nil { - return nil, err - } - ty, err := trie_helpers.CheckKeyType(nodeElements) - if err != nil { - return nil, err - } - - if ty != sdtypes.Leaf { - return nil, fmt.Errorf("expected leaf node but found %s", ty) - } - return nodeElements[1].([]byte), nil -} - -// RetrieveReceipts returns the cids and rlp bytes for the receipts corresponding to the provided block hash, number. -// cid returned corresponds to the leaf node data which contains the receipt. -func (r *IPLDRetriever) RetrieveReceipts(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, []common.Hash, error) { - rctResults := make([]rctIpldResult, 0) - if err := tx.Select(&rctResults, RetrieveReceiptsPgStr, hash.Hex(), number); err != nil { - return nil, nil, nil, err - } - cids := make([]string, len(rctResults)) - rcts := make([][]byte, len(rctResults)) - txs := make([]common.Hash, len(rctResults)) - - for i, res := range rctResults { - cids[i] = res.LeafCID - nodeVal, err := DecodeLeafNode(res.Data) - if err != nil { - return nil, nil, nil, err - } - rcts[i] = nodeVal - txs[i] = common.HexToHash(res.TxHash) - } - - return cids, rcts, txs, nil -} - -// RetrieveReceiptsByBlockHash returns the cids and rlp bytes for the receipts corresponding to the provided block hash. -// cid returned corresponds to the leaf node data which contains the receipt. -func (r *IPLDRetriever) RetrieveReceiptsByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]string, [][]byte, []common.Hash, error) { - rctResults := make([]rctIpldResult, 0) - if err := tx.Select(&rctResults, RetrieveReceiptsByBlockHashPgStr, hash.Hex()); err != nil { - return nil, nil, nil, err - } - cids := make([]string, len(rctResults)) - rcts := make([][]byte, len(rctResults)) - txs := make([]common.Hash, len(rctResults)) - - for i, res := range rctResults { - cids[i] = res.LeafCID - nodeVal, err := DecodeLeafNode(res.Data) - if err != nil { - return nil, nil, nil, err - } - rcts[i] = nodeVal - txs[i] = common.HexToHash(res.TxHash) - } - - return cids, rcts, txs, nil -} - -type nodeInfo struct { - CID string `db:"cid"` - BlockNumber string `db:"block_number"` - Data []byte `db:"data"` - NodeType int `db:"node_type"` - StateLeafRemoved bool `db:"state_leaf_removed"` -} - -// RetrieveAccountByAddressAndBlockHash returns the cid and rlp bytes for the account corresponding to the provided address and block hash -// TODO: ensure this handles deleted accounts appropriately -func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockHash(address common.Address, hash common.Hash) (string, []byte, error) { - accountResult := new(nodeInfo) - leafKey := crypto.Keccak256Hash(address.Bytes()) - if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil { - return "", nil, err - } - - if accountResult.NodeType == sdtypes.Removed.Int() { - return "", EmptyNodeValue, nil - } - - blockNumber, err := strconv.ParseUint(accountResult.BlockNumber, 10, 64) - if err != nil { - return "", nil, err - } - accountResult.Data, err = shared.FetchIPLD(r.db, accountResult.CID, blockNumber) - if err != nil { - return "", nil, err - } - - var i []interface{} - if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil { - return "", nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error()) - } - if len(i) != 2 { - return "", nil, fmt.Errorf("eth IPLDRetriever expected state leaf node rlp to decode into two elements") - } - return accountResult.CID, i[1].([]byte), nil -} - -// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash -func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, []byte, error) { - storageResult := new(nodeInfo) - stateLeafKey := crypto.Keccak256Hash(address.Bytes()) - storageHash := crypto.Keccak256Hash(key.Bytes()) - if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil { - return "", nil, nil, err - } - if storageResult.StateLeafRemoved || storageResult.NodeType == sdtypes.Removed.Int() { - return "", EmptyNodeValue, EmptyNodeValue, nil - } - - blockNumber, err := strconv.ParseUint(storageResult.BlockNumber, 10, 64) - if err != nil { - return "", nil, nil, err - } - storageResult.Data, err = shared.FetchIPLD(r.db, storageResult.CID, blockNumber) - if err != nil { - return "", nil, nil, err - } - - var i []interface{} - if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil { - err = fmt.Errorf("error decoding storage leaf node rlp: %s", err.Error()) - return "", nil, nil, err - } - if len(i) != 2 { - return "", nil, nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements") - } - return storageResult.CID, storageResult.Data, i[1].([]byte), nil -} diff --git a/pkg/eth/sql.go b/pkg/eth/sql.go new file mode 100644 index 00000000..20283cdc --- /dev/null +++ b/pkg/eth/sql.go @@ -0,0 +1,154 @@ +package eth + +const ( + RetrieveHeaderByHashPgStr = `SELECT cid, data + FROM eth.header_cids + INNER JOIN ipld.blocks ON ( + header_cids.cid = blocks.key + AND header_cids.block_number = blocks.block_number + ) + WHERE block_hash = $1` + RetrieveUnclesPgStr = `SELECT uncle_cids.cid, data + FROM eth.uncle_cids + INNER JOIN eth.header_cids ON ( + uncle_cids.header_id = header_cids.block_hash + AND uncle_cids.block_number = header_cids.block_number + ) + INNER JOIN ipld.blocks ON ( + uncle_cids.cid = blocks.key + AND uncle_cids.block_number = blocks.block_number + ) + WHERE header_cids.block_hash = $1 + AND header_cids.block_number = $2 + ORDER BY uncle_cids.parent_hash` + RetrieveUnclesByBlockHashPgStr = `SELECT uncle_cids.cid, data + FROM eth.uncle_cids + INNER JOIN eth.header_cids ON ( + uncle_cids.header_id = header_cids.block_hash + AND uncle_cids.block_number = header_cids.block_number + ) + INNER JOIN ipld.blocks ON ( + uncle_cids.cid = blocks.key + AND uncle_cids.block_number = blocks.block_number + ) + WHERE header_cids.block_hash = $1 + ORDER BY uncle_cids.parent_hash` + RetrieveTransactionsPgStr = `SELECT transaction_cids.cid, data + FROM eth.transaction_cids + INNER JOIN eth.header_cids ON ( + transaction_cids.header_id = header_cids.block_hash + AND transaction_cids.block_number = header_cids.block_number + ) + INNER JOIN ipld.blocks ON ( + transaction_cids.cid = blocks.key + AND transaction_cids.block_number = blocks.block_number + ) + WHERE block_hash = $1 + AND header_cids.block_number = $2 + ORDER BY eth.transaction_cids.index ASC` + RetrieveTransactionsByBlockHashPgStr = `SELECT transaction_cids.cid, data + FROM eth.transaction_cids + INNER JOIN eth.header_cids ON ( + transaction_cids.header_id = header_cids.block_hash + AND transaction_cids.block_number = header_cids.block_number + ) + INNER JOIN ipld.blocks ON ( + transaction_cids.cid = blocks.key + AND transaction_cids.block_number = blocks.block_number + ) + WHERE block_hash = $1 + ORDER BY eth.transaction_cids.index ASC` + RetrieveReceiptsPgStr = `SELECT receipt_cids.cid, data, eth.transaction_cids.tx_hash + FROM eth.receipt_cids + INNER JOIN eth.transaction_cids ON ( + receipt_cids.tx_id = transaction_cids.tx_hash + AND receipt_cids.header_id = transaction_cids.header_id + AND receipt_cids.block_number = transaction_cids.block_number + ) + INNER JOIN eth.header_cids ON ( + transaction_cids.header_id = header_cids.block_hash + AND transaction_cids.block_number = header_cids.block_number + ) + INNER JOIN ipld.blocks ON ( + receipt_cids.cid = blocks.key + AND receipt_cids.block_number = blocks.block_number + ) + WHERE block_hash = $1 + AND header_cids.block_number = $2 + ORDER BY eth.transaction_cids.index ASC` + RetrieveReceiptsByBlockHashPgStr = `SELECT receipt_cids.cid, data, eth.transaction_cids.tx_hash + FROM eth.receipt_cids + INNER JOIN eth.transaction_cids ON ( + receipt_cids.tx_id = transaction_cids.tx_hash + AND receipt_cids.header_id = transaction_cids.header_id + AND receipt_cids.block_number = transaction_cids.block_number + ) + INNER JOIN eth.header_cids ON ( + transaction_cids.header_id = header_cids.block_hash + AND transaction_cids.block_number = header_cids.block_number + ) + INNER JOIN ipld.blocks ON ( + receipt_cids.cid = blocks.key + AND receipt_cids.block_number = blocks.block_number + ) + WHERE block_hash = $1 + ORDER BY eth.transaction_cids.index ASC` + RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.cid, state_cids.block_number, state_cids.node_type + FROM eth.state_cids + INNER JOIN eth.header_cids ON ( + state_cids.header_id = header_cids.block_hash + AND state_cids.block_number = header_cids.block_number + ) + WHERE state_leaf_key = $1 + AND header_cids.block_number <= (SELECT block_number + FROM eth.header_cids + WHERE block_hash = $2) + AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number)) + ORDER BY header_cids.block_number DESC + LIMIT 1` + RetrieveFilteredGQLLogs = `SELECT CAST(eth.log_cids.block_number as Text), eth.log_cids.header_id as block_hash, + eth.log_cids.cid, eth.log_cids.index, eth.log_cids.rct_id, eth.log_cids.address, + eth.log_cids.topic0, eth.log_cids.topic1, eth.log_cids.topic2, eth.log_cids.topic3, eth.log_cids.log_data, + data, eth.receipt_cids.cid, eth.receipt_cids.post_status, eth.receipt_cids.tx_id AS tx_hash + FROM eth.log_cids, eth.receipt_cids, ipld.blocks + WHERE eth.log_cids.rct_id = receipt_cids.tx_id + AND eth.log_cids.header_id = receipt_cids.header_id + AND eth.log_cids.block_number = receipt_cids.block_number + AND log_cids.cid = blocks.key + AND log_cids.block_number = blocks.block_number + AND receipt_cids.header_id = $1` + RetrieveFilteredLogs = `SELECT CAST(eth.log_cids.block_number as Text), eth.log_cids.cid, eth.log_cids.index, eth.log_cids.rct_id, + eth.log_cids.address, eth.log_cids.topic0, eth.log_cids.topic1, eth.log_cids.topic2, eth.log_cids.topic3, + eth.log_cids.log_data, eth.transaction_cids.tx_hash, eth.transaction_cids.index as txn_index, + eth.receipt_cids.cid as cid, eth.receipt_cids.post_status, header_cids.block_hash + FROM eth.log_cids, eth.receipt_cids, eth.transaction_cids, eth.header_cids + WHERE eth.log_cids.rct_id = receipt_cids.tx_id + AND eth.log_cids.header_id = eth.receipt_cids.header_id + AND eth.log_cids.block_number = eth.receipt_cids.block_number + AND receipt_cids.tx_id = transaction_cids.tx_hash + AND receipt_cids.header_id = transaction_cids.header_id + AND receipt_cids.block_number = transaction_cids.block_number + AND transaction_cids.header_id = header_cids.block_hash + AND transaction_cids.block_number = header_cids.block_number` + RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT cid, block_number, node_type, state_leaf_removed FROM get_storage_at_by_hash($1, $2, $3)` +) + +type rctIpldResult struct { + LeafCID string `db:"cid"` + Data []byte `db:"data"` + TxHash string `db:"tx_hash"` +} + +type ipldResult struct { + CID string `db:"cid"` + Data []byte `db:"data"` + TxHash string `db:"tx_hash"` +} + +type nodeInfo struct { + CID string `db:"cid"` + BlockNumber string `db:"block_number"` + Data []byte `db:"data"` + NodeType int `db:"node_type"` + StateLeafRemoved bool `db:"state_leaf_removed"` +}