2020-09-24 21:38:09 +00:00
// 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 <http://www.gnu.org/licenses/>.
package eth
import (
"fmt"
2022-07-12 08:10:45 +00:00
"strconv"
2020-09-24 21:38:09 +00:00
2022-09-20 15:52:06 +00:00
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
2022-03-10 12:35:19 +00:00
"github.com/ethereum/go-ethereum/statediff/trie_helpers"
2021-09-16 12:49:42 +00:00
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
2022-03-10 09:53:03 +00:00
"github.com/jmoiron/sqlx"
2021-09-16 12:49:42 +00:00
2020-09-24 21:38:09 +00:00
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
)
const (
2020-10-30 16:54:22 +00:00
RetrieveHeaderByHashPgStr = ` SELECT cid , data
2021-05-26 11:37:25 +00:00
FROM eth . header_cids
2022-04-25 11:37:11 +00:00
INNER JOIN public . blocks ON (
header_cids . mh_key = blocks . key
AND header_cids . block_number = blocks . block_number
)
2020-09-24 21:38:09 +00:00
WHERE block_hash = $ 1 `
2022-11-04 04:32:09 +00:00
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 public . blocks ON (
uncle_cids . mh_key = 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 `
2020-10-31 20:00:03 +00:00
RetrieveUnclesByBlockHashPgStr = ` SELECT uncle_cids . cid , data
FROM eth . uncle_cids
2022-04-25 11:37:11 +00:00
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 public . blocks ON (
uncle_cids . mh_key = blocks . key
AND uncle_cids . block_number = blocks . block_number
)
2022-08-25 10:24:32 +00:00
WHERE header_cids . block_hash = $ 1
ORDER BY uncle_cids . parent_hash `
2022-11-04 04:32:09 +00:00
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 public . blocks ON (
transaction_cids . mh_key = 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 `
2020-10-30 16:54:22 +00:00
RetrieveTransactionsByBlockHashPgStr = ` SELECT transaction_cids . cid , data
2020-10-31 20:00:03 +00:00
FROM eth . transaction_cids
2022-04-25 11:37:11 +00:00
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 public . blocks ON (
transaction_cids . mh_key = blocks . key
AND transaction_cids . block_number = blocks . block_number
)
2021-02-19 20:23:45 +00:00
WHERE block_hash = $ 1
ORDER BY eth . transaction_cids . index ASC `
2022-11-04 04:32:09 +00:00
RetrieveReceiptsPgStr = ` SELECT receipt_cids . leaf_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 public . blocks ON (
receipt_cids . leaf_mh_key = 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 `
2021-09-16 12:49:42 +00:00
RetrieveReceiptsByBlockHashPgStr = ` SELECT receipt_cids . leaf_cid , data , eth . transaction_cids . tx_hash
2020-10-31 20:00:03 +00:00
FROM eth . receipt_cids
2022-04-25 11:37:11 +00:00
INNER JOIN eth . transaction_cids ON (
receipt_cids . tx_id = transaction_cids . tx_hash
2022-07-12 08:10:45 +00:00
AND receipt_cids . header_id = transaction_cids . header_id
2022-04-25 11:37:11 +00:00
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 public . blocks ON (
receipt_cids . leaf_mh_key = blocks . key
AND receipt_cids . block_number = blocks . block_number
)
2021-02-19 20:23:45 +00:00
WHERE block_hash = $ 1
ORDER BY eth . transaction_cids . index ASC `
2022-07-12 08:10:45 +00:00
RetrieveAccountByLeafKeyAndBlockHashPgStr = ` SELECT state_cids . cid , state_cids . mh_key , state_cids . block_number , state_cids . node_type
2020-10-31 20:00:03 +00:00
FROM eth . state_cids
2022-04-25 11:37:11 +00:00
INNER JOIN eth . header_cids ON (
state_cids . header_id = header_cids . block_hash
AND state_cids . block_number = header_cids . block_number
)
2020-10-31 20:00:03 +00:00
WHERE state_leaf_key = $ 1
2022-04-26 14:10:06 +00:00
AND header_cids . block_number <= ( SELECT block_number
2020-10-30 03:08:26 +00:00
FROM eth . header_cids
WHERE block_hash = $ 2 )
2022-04-26 14:10:06 +00:00
AND header_cids . block_hash = ( SELECT canonical_header_hash ( header_cids . block_number ) )
ORDER BY header_cids . block_number DESC
2020-10-30 03:08:26 +00:00
LIMIT 1 `
2023-02-22 01:44:46 +00:00
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = ` SELECT cid, mh_key, block_number, node_type, state_leaf_removed FROM get_storage_at_by_hash($1, $2, $3) `
2020-09-24 21:38:09 +00:00
)
2021-09-22 10:44:35 +00:00
var EmptyNodeValue = make ( [ ] byte , common . HashLength )
2021-09-16 12:49:42 +00:00
type rctIpldResult struct {
LeafCID string ` db:"leaf_cid" `
Data [ ] byte ` db:"data" `
TxHash string ` db:"tx_hash" `
}
2020-09-24 21:38:09 +00:00
type ipldResult struct {
2021-07-26 10:13:38 +00:00
CID string ` db:"cid" `
Data [ ] byte ` db:"data" `
TxHash string ` db:"tx_hash" `
2020-09-24 21:38:09 +00:00
}
2021-07-26 10:13:38 +00:00
2020-09-24 21:38:09 +00:00
type IPLDRetriever struct {
2022-03-10 09:53:03 +00:00
db * sqlx . DB
2020-09-24 21:38:09 +00:00
}
2022-03-10 09:53:03 +00:00
func NewIPLDRetriever ( db * sqlx . DB ) * IPLDRetriever {
2020-09-24 21:38:09 +00:00
return & IPLDRetriever {
db : db ,
}
}
// RetrieveHeaderByHash returns the cid and rlp bytes for the header corresponding to the provided block hash
2022-08-23 07:12:19 +00:00
func ( r * IPLDRetriever ) RetrieveHeaderByHash ( tx * sqlx . Tx , hash common . Hash ) ( string , [ ] byte , error ) {
2020-09-24 21:38:09 +00:00
headerResult := new ( ipldResult )
2022-08-23 07:12:19 +00:00
return headerResult . CID , headerResult . Data , tx . Get ( headerResult , RetrieveHeaderByHashPgStr , hash . Hex ( ) )
2020-09-24 21:38:09 +00:00
}
2022-11-04 04:32:09 +00:00
// 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
}
2020-09-24 21:38:09 +00:00
// RetrieveUnclesByBlockHash returns the cids and rlp bytes for the uncles corresponding to the provided block hash (of non-omner root block)
2022-08-23 07:12:19 +00:00
func ( r * IPLDRetriever ) RetrieveUnclesByBlockHash ( tx * sqlx . Tx , hash common . Hash ) ( [ ] string , [ ] [ ] byte , error ) {
2020-09-24 21:38:09 +00:00
uncleResults := make ( [ ] ipldResult , 0 )
2022-08-23 07:12:19 +00:00
if err := tx . Select ( & uncleResults , RetrieveUnclesByBlockHashPgStr , hash . Hex ( ) ) ; err != nil {
2020-09-24 21:38:09 +00:00
return nil , nil , err
}
cids := make ( [ ] string , len ( uncleResults ) )
uncles := make ( [ ] [ ] byte , len ( uncleResults ) )
for i , res := range uncleResults {
2020-10-26 13:58:37 +00:00
cids [ i ] = res . CID
uncles [ i ] = res . Data
2020-09-24 21:38:09 +00:00
}
return cids , uncles , nil
}
2022-11-04 04:32:09 +00:00
// 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
}
2020-09-24 21:38:09 +00:00
// RetrieveTransactionsByBlockHash returns the cids and rlp bytes for the transactions corresponding to the provided block hash
2022-08-23 07:12:19 +00:00
func ( r * IPLDRetriever ) RetrieveTransactionsByBlockHash ( tx * sqlx . Tx , hash common . Hash ) ( [ ] string , [ ] [ ] byte , error ) {
2020-09-24 21:38:09 +00:00
txResults := make ( [ ] ipldResult , 0 )
2022-08-23 07:12:19 +00:00
if err := tx . Select ( & txResults , RetrieveTransactionsByBlockHashPgStr , hash . Hex ( ) ) ; err != nil {
2020-09-24 21:38:09 +00:00
return nil , nil , err
}
cids := make ( [ ] string , len ( txResults ) )
txs := make ( [ ] [ ] byte , len ( txResults ) )
for i , res := range txResults {
2020-10-26 13:58:37 +00:00
cids [ i ] = res . CID
txs [ i ] = res . Data
2020-09-24 21:38:09 +00:00
}
return cids , txs , nil
}
2021-09-16 12:49:42 +00:00
// 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
}
2022-03-10 12:35:19 +00:00
ty , err := trie_helpers . CheckKeyType ( nodeElements )
2021-09-16 12:49:42 +00:00
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
}
2022-11-04 04:32:09 +00:00
// 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
}
2021-09-20 10:31:27 +00:00
// 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.
2022-08-23 07:12:19 +00:00
func ( r * IPLDRetriever ) RetrieveReceiptsByBlockHash ( tx * sqlx . Tx , hash common . Hash ) ( [ ] string , [ ] [ ] byte , [ ] common . Hash , error ) {
2021-09-16 12:49:42 +00:00
rctResults := make ( [ ] rctIpldResult , 0 )
2022-08-23 07:12:19 +00:00
if err := tx . Select ( & rctResults , RetrieveReceiptsByBlockHashPgStr , hash . Hex ( ) ) ; err != nil {
2021-07-26 10:13:38 +00:00
return nil , nil , nil , err
2020-09-24 21:38:09 +00:00
}
cids := make ( [ ] string , len ( rctResults ) )
rcts := make ( [ ] [ ] byte , len ( rctResults ) )
2021-07-26 10:13:38 +00:00
txs := make ( [ ] common . Hash , len ( rctResults ) )
2020-09-24 21:38:09 +00:00
for i , res := range rctResults {
2021-09-16 12:49:42 +00:00
cids [ i ] = res . LeafCID
nodeVal , err := DecodeLeafNode ( res . Data )
if err != nil {
return nil , nil , nil , err
}
rcts [ i ] = nodeVal
2021-07-26 10:13:38 +00:00
txs [ i ] = common . HexToHash ( res . TxHash )
2020-09-24 21:38:09 +00:00
}
2021-07-26 10:13:38 +00:00
return cids , rcts , txs , nil
2020-09-24 21:38:09 +00:00
}
2020-10-31 20:00:03 +00:00
type nodeInfo struct {
2021-09-23 10:36:09 +00:00
CID string ` db:"cid" `
2022-07-12 08:10:45 +00:00
MhKey string ` db:"mh_key" `
BlockNumber string ` db:"block_number" `
2021-09-23 10:36:09 +00:00
Data [ ] byte ` db:"data" `
NodeType int ` db:"node_type" `
StateLeafRemoved bool ` db:"state_leaf_removed" `
2020-10-31 20:00:03 +00:00
}
2020-09-24 21:38:09 +00:00
// RetrieveAccountByAddressAndBlockHash returns the cid and rlp bytes for the account corresponding to the provided address and block hash
2020-10-30 16:54:22 +00:00
// TODO: ensure this handles deleted accounts appropriately
2020-09-24 21:38:09 +00:00
func ( r * IPLDRetriever ) RetrieveAccountByAddressAndBlockHash ( address common . Address , hash common . Hash ) ( string , [ ] byte , error ) {
2020-10-31 20:00:03 +00:00
accountResult := new ( nodeInfo )
2020-09-24 21:38:09 +00:00
leafKey := crypto . Keccak256Hash ( address . Bytes ( ) )
if err := r . db . Get ( accountResult , RetrieveAccountByLeafKeyAndBlockHashPgStr , leafKey . Hex ( ) , hash . Hex ( ) ) ; err != nil {
return "" , nil , err
}
2021-08-31 23:10:42 +00:00
2022-12-19 08:42:23 +00:00
if accountResult . NodeType == sdtypes . Removed . Int ( ) {
2021-09-22 10:44:35 +00:00
return "" , EmptyNodeValue , nil
2020-10-31 20:00:03 +00:00
}
2021-08-31 23:10:42 +00:00
2022-07-12 08:10:45 +00:00
blockNumber , err := strconv . ParseUint ( accountResult . BlockNumber , 10 , 64 )
if err != nil {
return "" , nil , err
}
accountResult . Data , err = shared . FetchIPLD ( r . db , accountResult . MhKey , blockNumber )
if err != nil {
return "" , nil , err
}
2020-09-24 21:38:09 +00:00
var i [ ] interface { }
2020-10-26 13:58:37 +00:00
if err := rlp . DecodeBytes ( accountResult . Data , & i ) ; err != nil {
2020-09-24 21:38:09 +00:00
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" )
}
2020-10-26 13:58:37 +00:00
return accountResult . CID , i [ 1 ] . ( [ ] byte ) , nil
2020-09-24 21:38:09 +00:00
}
2021-06-18 06:42:29 +00:00
// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash
2021-05-26 11:37:25 +00:00
func ( r * IPLDRetriever ) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash ( address common . Address , key , hash common . Hash ) ( string , [ ] byte , [ ] byte , error ) {
2020-10-31 20:00:03 +00:00
storageResult := new ( nodeInfo )
2020-10-30 16:54:22 +00:00
stateLeafKey := crypto . Keccak256Hash ( address . Bytes ( ) )
2021-06-18 06:42:29 +00:00
storageHash := crypto . Keccak256Hash ( key . Bytes ( ) )
if err := r . db . Get ( storageResult , RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr , stateLeafKey . Hex ( ) , storageHash . Hex ( ) , hash . Hex ( ) ) ; err != nil {
2021-05-26 11:37:25 +00:00
return "" , nil , nil , err
2020-10-30 16:54:22 +00:00
}
2022-12-19 08:42:23 +00:00
if storageResult . StateLeafRemoved || storageResult . NodeType == sdtypes . Removed . Int ( ) {
2021-09-22 10:44:35 +00:00
return "" , EmptyNodeValue , EmptyNodeValue , nil
2020-10-30 16:54:22 +00:00
}
2022-07-12 08:10:45 +00:00
blockNumber , err := strconv . ParseUint ( storageResult . BlockNumber , 10 , 64 )
if err != nil {
return "" , nil , nil , err
}
storageResult . Data , err = shared . FetchIPLD ( r . db , storageResult . MhKey , blockNumber )
if err != nil {
return "" , nil , nil , err
}
2020-10-30 16:54:22 +00:00
var i [ ] interface { }
if err := rlp . DecodeBytes ( storageResult . Data , & i ) ; err != nil {
err = fmt . Errorf ( "error decoding storage leaf node rlp: %s" , err . Error ( ) )
2021-05-26 11:37:25 +00:00
return "" , nil , nil , err
2020-10-30 16:54:22 +00:00
}
if len ( i ) != 2 {
2021-05-26 11:37:25 +00:00
return "" , nil , nil , fmt . Errorf ( "eth IPLDRetriever expected storage leaf node rlp to decode into two elements" )
2020-10-30 16:54:22 +00:00
}
2021-05-26 11:37:25 +00:00
return storageResult . CID , storageResult . Data , i [ 1 ] . ( [ ] byte ) , nil
2020-10-30 16:54:22 +00:00
}