2020-01-17 23:16:01 +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/>.
2020-01-16 23:21:49 +00:00
package eth
import (
"context"
2021-04-19 10:16:59 +00:00
"database/sql"
2020-01-16 23:21:49 +00:00
"errors"
2020-10-30 03:07:39 +00:00
"fmt"
2020-01-16 23:21:49 +00:00
"math/big"
2021-09-21 12:10:55 +00:00
"time"
2020-01-16 23:21:49 +00:00
"github.com/ethereum/go-ethereum/common"
2021-02-24 22:20:06 +00:00
"github.com/ethereum/go-ethereum/common/hexutil"
2020-10-20 20:33:18 +00:00
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
2020-10-28 03:04:19 +00:00
"github.com/ethereum/go-ethereum/core/bloombits"
2020-10-20 20:33:18 +00:00
"github.com/ethereum/go-ethereum/core/state"
2020-01-16 23:21:49 +00:00
"github.com/ethereum/go-ethereum/core/types"
2020-10-20 20:33:18 +00:00
"github.com/ethereum/go-ethereum/core/vm"
2020-10-30 03:07:39 +00:00
"github.com/ethereum/go-ethereum/crypto"
2020-08-11 03:53:01 +00:00
"github.com/ethereum/go-ethereum/ethdb"
2020-10-28 03:04:19 +00:00
"github.com/ethereum/go-ethereum/event"
2020-10-20 20:33:18 +00:00
"github.com/ethereum/go-ethereum/params"
2020-01-16 23:21:49 +00:00
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
2021-02-24 22:20:06 +00:00
"github.com/ethereum/go-ethereum/trie"
2022-03-10 09:53:03 +00:00
"github.com/jmoiron/sqlx"
2021-09-21 12:10:55 +00:00
log "github.com/sirupsen/logrus"
2022-05-06 08:26:43 +00:00
validator "github.com/vulcanize/eth-ipfs-state-validator/v4/pkg"
ipfsethdb "github.com/vulcanize/ipfs-ethdb/v4/postgres"
2021-09-21 12:10:55 +00:00
2022-03-10 12:35:19 +00:00
ethServerShared "github.com/ethereum/go-ethereum/statediff/indexer/shared"
2022-05-20 13:16:36 +00:00
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
2020-01-16 23:21:49 +00:00
)
var (
2022-07-12 08:10:45 +00:00
errPendingBlockNumber = errors . New ( "pending block number not supported" )
errNegativeBlockNumber = errors . New ( "negative block number not supported" )
errHeaderHashNotFound = errors . New ( "header for hash not found" )
errHeaderNotFound = errors . New ( "header not found" )
errMultipleHeadersForHash = errors . New ( "more than one headers for the given hash" )
errTxHashNotFound = errors . New ( "transaction for hash not found" )
errTxHashInMultipleBlocks = errors . New ( "transaction for hash found in more than one canonical block" )
2021-08-20 07:37:11 +00:00
// errMissingSignature is returned if a block's extra-data section doesn't seem
// to contain a 65 byte secp256k1 signature.
2020-10-20 20:33:18 +00:00
)
const (
2022-07-12 08:10:45 +00:00
RetrieveCanonicalBlockHashByNumber = ` SELECT block_hash
FROM canonical_header_hash ( $ 1 ) AS block_hash
WHERE block_hash IS NOT NULL `
2020-10-20 20:33:18 +00:00
RetrieveCanonicalHeaderByNumber = ` SELECT cid , data FROM eth . header_cids
2022-04-26 14:10:06 +00:00
INNER JOIN public . blocks ON (
header_cids . mh_key = blocks . key
AND header_cids . block_number = blocks . block_number
)
2022-03-14 09:57:58 +00:00
WHERE block_hash = ( SELECT canonical_header_hash ( $ 1 ) ) `
2022-03-10 12:35:19 +00:00
RetrieveTD = ` SELECT CAST ( td as Text ) FROM eth . header_cids
2020-10-26 13:58:37 +00:00
WHERE header_cids . block_hash = $ 1 `
2022-07-12 08:10:45 +00:00
RetrieveRPCTransaction = ` SELECT blocks . data , header_id , transaction_cids . block_number , index
FROM public . blocks , eth . transaction_cids
2020-10-26 13:58:37 +00:00
WHERE blocks . key = transaction_cids . mh_key
2022-04-26 14:10:06 +00:00
AND blocks . block_number = transaction_cids . block_number
2022-07-12 08:10:45 +00:00
AND transaction_cids . tx_hash = $ 1
AND transaction_cids . header_id = ( SELECT canonical_header_hash ( transaction_cids . block_number ) ) `
2020-10-30 03:07:39 +00:00
RetrieveCodeHashByLeafKeyAndBlockHash = ` SELECT code_hash FROM eth . state_accounts , eth . state_cids , eth . header_cids
2022-04-26 14:10:06 +00:00
WHERE state_accounts . header_id = state_cids . header_id
AND state_accounts . state_path = state_cids . state_path
AND state_accounts . block_number = state_cids . block_number
2022-03-10 12:35:19 +00:00
AND state_cids . header_id = header_cids . block_hash
2022-04-26 14:10:06 +00:00
AND state_cids . block_number = header_cids . block_number
2020-10-30 03:07:39 +00:00
AND 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 `
2020-10-30 03:07:39 +00:00
RetrieveCodeByMhKey = ` SELECT data FROM public.blocks WHERE key = $1 `
2020-01-16 23:21:49 +00:00
)
2021-09-21 12:10:55 +00:00
const (
StateDBGroupCacheName = "statedb"
)
2020-01-16 23:21:49 +00:00
type Backend struct {
2020-10-20 20:33:18 +00:00
// underlying postgres db
2022-03-10 09:53:03 +00:00
DB * sqlx . DB
2020-10-20 20:33:18 +00:00
// postgres db interfaces
Retriever * CIDRetriever
Fetcher * IPLDFetcher
IPLDRetriever * IPLDRetriever
// ethereum interfaces
EthDB ethdb . Database
StateDatabase state . Database
Config * Config
}
type Config struct {
2021-09-21 12:10:55 +00:00
ChainConfig * params . ChainConfig
VMConfig vm . Config
DefaultSender * common . Address
RPCGasCap * big . Int
GroupCacheConfig * shared . GroupCacheConfig
2020-01-16 23:21:49 +00:00
}
2022-03-10 09:53:03 +00:00
func NewEthBackend ( db * sqlx . DB , c * Config ) ( * Backend , error ) {
2021-09-21 12:10:55 +00:00
gcc := c . GroupCacheConfig
groupName := gcc . StateDB . Name
if groupName == "" {
groupName = StateDBGroupCacheName
}
2020-01-17 23:16:01 +00:00
r := NewCIDRetriever ( db )
2022-03-10 09:53:03 +00:00
ethDB := ipfsethdb . NewDatabase ( db , ipfsethdb . CacheConfig {
2021-09-21 12:10:55 +00:00
Name : groupName ,
Size : gcc . StateDB . CacheSizeInMB * 1024 * 1024 ,
ExpiryDuration : time . Minute * time . Duration ( gcc . StateDB . CacheExpiryInMins ) ,
} )
2021-12-29 20:57:21 +00:00
logStateDBStatsOnTimer ( ethDB . ( * ipfsethdb . Database ) , gcc )
2021-08-20 07:37:11 +00:00
2020-01-16 23:21:49 +00:00
return & Backend {
2020-10-20 20:33:18 +00:00
DB : db ,
Retriever : r ,
Fetcher : NewIPLDFetcher ( db ) ,
IPLDRetriever : NewIPLDRetriever ( db ) ,
EthDB : ethDB ,
StateDatabase : state . NewDatabase ( ethDB ) ,
Config : c ,
2020-01-17 23:16:01 +00:00
} , nil
2020-01-16 23:21:49 +00:00
}
2020-10-26 13:58:37 +00:00
// ChainDb returns the backend's underlying chain database
func ( b * Backend ) ChainDb ( ) ethdb . Database {
return b . EthDB
}
2020-10-20 20:33:18 +00:00
// HeaderByNumber gets the canonical header for the provided block number
2020-01-16 23:21:49 +00:00
func ( b * Backend ) HeaderByNumber ( ctx context . Context , blockNumber rpc . BlockNumber ) ( * types . Header , error ) {
var err error
2020-05-01 21:25:58 +00:00
number := blockNumber . Int64 ( )
2020-01-16 23:21:49 +00:00
if blockNumber == rpc . LatestBlockNumber {
2020-02-20 22:12:52 +00:00
number , err = b . Retriever . RetrieveLastBlockNumber ( )
2020-01-16 23:21:49 +00:00
if err != nil {
return nil , err
}
}
2020-10-20 20:33:18 +00:00
if blockNumber == rpc . EarliestBlockNumber {
number , err = b . Retriever . RetrieveFirstBlockNumber ( )
if err != nil {
return nil , err
}
}
2020-01-16 23:21:49 +00:00
if blockNumber == rpc . PendingBlockNumber {
return nil , errPendingBlockNumber
}
2020-10-20 20:33:18 +00:00
if number < 0 {
return nil , errNegativeBlockNumber
}
_ , canonicalHeaderRLP , err := b . GetCanonicalHeader ( uint64 ( number ) )
if err != nil {
return nil , err
}
header := new ( types . Header )
return header , rlp . DecodeBytes ( canonicalHeaderRLP , header )
}
2020-05-01 21:25:58 +00:00
2020-10-20 20:33:18 +00:00
// HeaderByHash gets the header for the provided block hash
func ( b * Backend ) HeaderByHash ( ctx context . Context , hash common . Hash ) ( * types . Header , error ) {
2022-08-23 07:12:19 +00:00
// Begin tx
tx , err := b . DB . Beginx ( )
if err != nil {
return nil , err
}
defer func ( ) {
if p := recover ( ) ; p != nil {
shared . Rollback ( tx )
panic ( p )
} else if err != nil {
shared . Rollback ( tx )
} else {
err = tx . Commit ( )
}
} ( )
_ , headerRLP , err := b . IPLDRetriever . RetrieveHeaderByHash ( tx , hash )
2020-10-20 20:33:18 +00:00
if err != nil {
return nil , err
2020-08-11 03:53:01 +00:00
}
header := new ( types . Header )
2020-10-20 20:33:18 +00:00
return header , rlp . DecodeBytes ( headerRLP , header )
2020-01-16 23:21:49 +00:00
}
2020-10-26 13:58:37 +00:00
// HeaderByNumberOrHash gets the header for the provided block hash or number
func ( b * Backend ) HeaderByNumberOrHash ( ctx context . Context , blockNrOrHash rpc . BlockNumberOrHash ) ( * types . Header , error ) {
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
return b . HeaderByNumber ( ctx , blockNr )
}
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
header , err := b . HeaderByHash ( ctx , hash )
if err != nil {
return nil , err
}
if header == nil {
return nil , errors . New ( "header for hash not found" )
}
2021-04-16 13:02:04 +00:00
canonicalHash , err := b . GetCanonicalHash ( header . Number . Uint64 ( ) )
if err != nil {
return nil , err
}
if blockNrOrHash . RequireCanonical && canonicalHash != hash {
2020-10-26 13:58:37 +00:00
return nil , errors . New ( "hash is not currently canonical" )
}
return header , nil
}
return nil , errors . New ( "invalid arguments; neither block nor hash specified" )
}
2022-06-17 14:45:54 +00:00
func ( b * Backend ) PendingBlockAndReceipts ( ) ( * types . Block , types . Receipts ) {
return nil , nil
}
2020-10-20 20:33:18 +00:00
// GetTd gets the total difficulty at the given block hash
2020-01-21 19:12:35 +00:00
func ( b * Backend ) GetTd ( blockHash common . Hash ) ( * big . Int , error ) {
var tdStr string
2020-10-26 13:58:37 +00:00
err := b . DB . Get ( & tdStr , RetrieveTD , blockHash . String ( ) )
2020-01-21 19:12:35 +00:00
if err != nil {
return nil , err
}
td , ok := new ( big . Int ) . SetString ( tdStr , 10 )
if ! ok {
return nil , errors . New ( "total difficulty retrieved from Postgres cannot be converted to an integer" )
}
return td , nil
}
2020-10-26 13:58:37 +00:00
// CurrentBlock returns the current block
2021-04-19 21:52:58 +00:00
func ( b * Backend ) CurrentBlock ( ) ( * types . Block , error ) {
block , err := b . BlockByNumber ( context . Background ( ) , rpc . LatestBlockNumber )
return block , err
2020-10-26 13:58:37 +00:00
}
// BlockByNumberOrHash returns block by number or hash
func ( b * Backend ) BlockByNumberOrHash ( ctx context . Context , blockNrOrHash rpc . BlockNumberOrHash ) ( * types . Block , error ) {
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
return b . BlockByNumber ( ctx , blockNr )
2020-01-21 19:12:35 +00:00
}
2020-10-26 13:58:37 +00:00
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
header , err := b . HeaderByHash ( ctx , hash )
if err != nil {
2020-01-21 19:12:35 +00:00
return nil , err
}
2020-10-26 13:58:37 +00:00
if header == nil {
return nil , errors . New ( "header for hash not found" )
}
2021-04-16 13:02:04 +00:00
canonicalHash , err := b . GetCanonicalHash ( header . Number . Uint64 ( ) )
if err != nil {
return nil , err
}
if blockNrOrHash . RequireCanonical && canonicalHash != hash {
2020-10-26 13:58:37 +00:00
return nil , errors . New ( "hash is not currently canonical" )
}
block , err := b . BlockByHash ( ctx , hash )
if err != nil {
return nil , err
}
if block == nil {
return nil , errors . New ( "header found, but block body is missing" )
}
return block , nil
2020-01-21 19:12:35 +00:00
}
2020-10-26 13:58:37 +00:00
return nil , errors . New ( "invalid arguments; neither block nor hash specified" )
2020-01-16 23:21:49 +00:00
}
2020-01-26 19:55:26 +00:00
2022-08-23 07:12:19 +00:00
// BlockByNumber returns the requested canonical block
2020-01-26 19:55:26 +00:00
func ( b * Backend ) BlockByNumber ( ctx context . Context , blockNumber rpc . BlockNumber ) ( * types . Block , error ) {
var err error
2020-05-01 21:25:58 +00:00
number := blockNumber . Int64 ( )
2020-01-26 19:55:26 +00:00
if blockNumber == rpc . LatestBlockNumber {
2020-02-20 22:12:52 +00:00
number , err = b . Retriever . RetrieveLastBlockNumber ( )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
}
2020-10-20 20:33:18 +00:00
if blockNumber == rpc . EarliestBlockNumber {
number , err = b . Retriever . RetrieveFirstBlockNumber ( )
if err != nil {
return nil , err
}
}
2020-01-26 19:55:26 +00:00
if blockNumber == rpc . PendingBlockNumber {
return nil , errPendingBlockNumber
}
2020-10-20 20:33:18 +00:00
if number < 0 {
return nil , errNegativeBlockNumber
}
2022-08-23 07:12:19 +00:00
2020-10-20 20:33:18 +00:00
// Get the canonical hash
2021-04-16 13:02:04 +00:00
canonicalHash , err := b . GetCanonicalHash ( uint64 ( number ) )
2020-10-20 20:33:18 +00:00
if err != nil {
2021-04-19 10:16:59 +00:00
if err == sql . ErrNoRows {
return nil , nil
}
2020-10-20 20:33:18 +00:00
return nil , err
}
2020-05-01 21:25:58 +00:00
2022-08-23 07:12:19 +00:00
return b . BlockByHash ( ctx , canonicalHash )
}
// BlockByHash returns the requested block
func ( b * Backend ) BlockByHash ( ctx context . Context , hash common . Hash ) ( * types . Block , error ) {
2020-05-01 21:25:58 +00:00
// Begin tx
2020-05-01 16:07:47 +00:00
tx , err := b . DB . Beginx ( )
if err != nil {
return nil , err
}
2020-05-01 21:25:58 +00:00
defer func ( ) {
if p := recover ( ) ; p != nil {
2022-03-10 12:35:19 +00:00
shared . Rollback ( tx )
2020-05-01 21:25:58 +00:00
panic ( p )
} else if err != nil {
2022-03-10 12:35:19 +00:00
shared . Rollback ( tx )
2020-05-01 21:25:58 +00:00
} else {
err = tx . Commit ( )
}
} ( )
2020-02-20 22:13:19 +00:00
2022-08-23 07:12:19 +00:00
// Fetch header
header , err := b . GetHeaderByBlockHash ( tx , hash )
2020-01-26 19:55:26 +00:00
if err != nil {
2022-08-23 07:12:19 +00:00
log . Error ( "error fetching header: " , err )
2021-04-19 10:16:59 +00:00
if err == sql . ErrNoRows {
return nil , nil
}
2020-01-26 19:55:26 +00:00
return nil , err
}
2022-06-28 11:10:07 +00:00
2022-08-23 07:12:19 +00:00
// Fetch uncles
uncles , err := b . GetUnclesByBlockHash ( tx , hash )
if err != nil && err != sql . ErrNoRows {
log . Error ( "error fetching uncles: " , err )
2020-01-26 19:55:26 +00:00
return nil , err
}
2022-06-28 11:10:07 +00:00
2022-08-25 08:04:17 +00:00
// When num. of uncles = 2,
// Check if calculated uncle hash matches the one in header
// If not, re-order the two uncles
// Assumption: Max num. of uncles in mainnet = 2
if len ( uncles ) == 2 {
uncleHash := types . CalcUncleHash ( uncles )
if uncleHash != header . UncleHash {
uncles [ 0 ] , uncles [ 1 ] = uncles [ 1 ] , uncles [ 0 ]
uncleHash = types . CalcUncleHash ( uncles )
// Check if uncle hash matches after re-ordering
if uncleHash != header . UncleHash {
log . Error ( "uncle hash mismatch for block hash: " , hash . Hex ( ) )
}
}
}
2022-08-23 07:12:19 +00:00
// Fetch transactions
transactions , err := b . GetTransactionsByBlockHash ( tx , hash )
if err != nil && err != sql . ErrNoRows {
log . Error ( "error fetching transactions: " , err )
2020-01-26 19:55:26 +00:00
return nil , err
}
2022-08-23 07:12:19 +00:00
// Fetch receipts
receipts , err := b . GetReceiptsByBlockHash ( tx , hash )
if err != nil && err != sql . ErrNoRows {
log . Error ( "error fetching receipts: " , err )
2020-01-26 19:55:26 +00:00
return nil , err
}
2021-09-16 12:49:42 +00:00
2020-01-26 19:55:26 +00:00
// Compose everything together into a complete block
2022-08-23 07:12:19 +00:00
return types . NewBlock ( header , transactions , uncles , receipts , new ( trie . Trie ) ) , err
2020-01-26 19:55:26 +00:00
}
2022-08-23 07:12:19 +00:00
// GetHeaderByBlockHash retrieves header for a provided block hash
func ( b * Backend ) GetHeaderByBlockHash ( tx * sqlx . Tx , hash common . Hash ) ( * types . Header , error ) {
_ , headerRLP , err := b . IPLDRetriever . RetrieveHeaderByHash ( tx , hash )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
2020-05-01 21:25:58 +00:00
2022-08-23 07:12:19 +00:00
header := new ( types . Header )
return header , rlp . DecodeBytes ( headerRLP , header )
}
2020-05-01 21:25:58 +00:00
2022-08-23 07:12:19 +00:00
// GetUnclesByBlockHash retrieves uncles for a provided block hash
func ( b * Backend ) GetUnclesByBlockHash ( tx * sqlx . Tx , hash common . Hash ) ( [ ] * types . Header , error ) {
_ , uncleBytes , err := b . IPLDRetriever . RetrieveUnclesByBlockHash ( tx , hash )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
2022-08-23 07:12:19 +00:00
uncles := make ( [ ] * types . Header , len ( uncleBytes ) )
for i , bytes := range uncleBytes {
2020-02-20 22:13:19 +00:00
var uncle types . Header
2022-08-23 07:12:19 +00:00
err = rlp . DecodeBytes ( bytes , & uncle )
2021-04-19 13:42:09 +00:00
if err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2022-08-23 07:12:19 +00:00
uncles [ i ] = & uncle
2020-01-26 19:55:26 +00:00
}
2022-08-23 07:12:19 +00:00
return uncles , nil
}
// GetTransactionsByBlockHash retrieves transactions for a provided block hash
func ( b * Backend ) GetTransactionsByBlockHash ( tx * sqlx . Tx , hash common . Hash ) ( types . Transactions , error ) {
_ , transactionBytes , err := b . IPLDRetriever . RetrieveTransactionsByBlockHash ( tx , hash )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
2022-08-23 07:12:19 +00:00
txs := make ( types . Transactions , len ( transactionBytes ) )
for i , txBytes := range transactionBytes {
var tx types . Transaction
if err := tx . UnmarshalBinary ( txBytes ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2022-08-23 07:12:19 +00:00
txs [ i ] = & tx
2020-01-26 19:55:26 +00:00
}
2022-08-23 07:12:19 +00:00
return txs , nil
}
// GetReceiptsByBlockHash retrieves receipts for a provided block hash
func ( b * Backend ) GetReceiptsByBlockHash ( tx * sqlx . Tx , hash common . Hash ) ( types . Receipts , error ) {
_ , receiptBytes , txs , err := b . IPLDRetriever . RetrieveReceiptsByBlockHash ( tx , hash )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
2022-08-23 07:12:19 +00:00
rcts := make ( types . Receipts , len ( receiptBytes ) )
for i , rctBytes := range receiptBytes {
rct := new ( types . Receipt )
if err := rct . UnmarshalBinary ( rctBytes ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2022-08-23 07:12:19 +00:00
rct . TxHash = txs [ i ]
rcts [ i ] = rct
2020-01-26 19:55:26 +00:00
}
2022-08-23 07:12:19 +00:00
return rcts , nil
2020-01-26 19:55:26 +00:00
}
// GetTransaction retrieves a tx by hash
// It also returns the blockhash, blocknumber, and tx index associated with the transaction
func ( b * Backend ) GetTransaction ( ctx context . Context , txHash common . Hash ) ( * types . Transaction , common . Hash , uint64 , uint64 , error ) {
2022-07-12 08:10:45 +00:00
type txRes struct {
2020-08-11 03:53:01 +00:00
Data [ ] byte ` db:"data" `
2022-07-12 08:10:45 +00:00
HeaderID string ` db:"header_id" `
2020-08-11 03:53:01 +00:00
BlockNumber uint64 ` db:"block_number" `
Index uint64 ` db:"index" `
2020-05-01 16:07:47 +00:00
}
2022-07-12 08:10:45 +00:00
var res = make ( [ ] txRes , 0 )
if err := b . DB . Select ( & res , RetrieveRPCTransaction , txHash . String ( ) ) ; err != nil {
2020-05-01 16:07:47 +00:00
return nil , common . Hash { } , 0 , 0 , err
}
2022-07-12 08:10:45 +00:00
if len ( res ) == 0 {
return nil , common . Hash { } , 0 , 0 , errTxHashNotFound
} else if len ( res ) > 1 {
// a transaction can be part of a only one canonical block
return nil , common . Hash { } , 0 , 0 , errTxHashInMultipleBlocks
}
2020-02-20 22:13:19 +00:00
var transaction types . Transaction
2022-07-12 08:10:45 +00:00
if err := transaction . UnmarshalBinary ( res [ 0 ] . Data ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , common . Hash { } , 0 , 0 , err
}
2022-07-12 08:10:45 +00:00
return & transaction , common . HexToHash ( res [ 0 ] . HeaderID ) , res [ 0 ] . BlockNumber , res [ 0 ] . Index , nil
2020-01-26 19:55:26 +00:00
}
2020-04-02 03:34:06 +00:00
2020-10-26 13:58:37 +00:00
// GetReceipts retrieves receipts for provided block hash
func ( b * Backend ) GetReceipts ( ctx context . Context , hash common . Hash ) ( types . Receipts , error ) {
2022-08-23 07:12:19 +00:00
// Begin tx
tx , err := b . DB . Beginx ( )
if err != nil {
return nil , err
}
defer func ( ) {
if p := recover ( ) ; p != nil {
shared . Rollback ( tx )
panic ( p )
} else if err != nil {
shared . Rollback ( tx )
} else {
err = tx . Commit ( )
}
} ( )
_ , receiptBytes , txs , err := b . IPLDRetriever . RetrieveReceiptsByBlockHash ( tx , hash )
2020-10-26 13:58:37 +00:00
if err != nil {
return nil , err
2020-04-02 03:34:06 +00:00
}
2020-10-30 03:07:39 +00:00
rcts := make ( types . Receipts , len ( receiptBytes ) )
2020-10-26 13:58:37 +00:00
for i , rctBytes := range receiptBytes {
rct := new ( types . Receipt )
2021-07-27 12:07:50 +00:00
if err := rct . UnmarshalBinary ( rctBytes ) ; err != nil {
2020-10-26 13:58:37 +00:00
return nil , err
2020-04-02 03:34:06 +00:00
}
2021-07-26 10:13:38 +00:00
rct . TxHash = txs [ i ]
2020-10-26 13:58:37 +00:00
rcts [ i ] = rct
2020-04-02 03:34:06 +00:00
}
2020-10-26 13:58:37 +00:00
return rcts , nil
2020-04-02 03:34:06 +00:00
}
2020-10-26 13:58:37 +00:00
// GetLogs returns all the logs for the given block hash
func ( b * Backend ) GetLogs ( ctx context . Context , hash common . Hash ) ( [ ] [ ] * types . Log , error ) {
2022-08-23 07:12:19 +00:00
// Begin tx
tx , err := b . DB . Beginx ( )
if err != nil {
return nil , err
}
defer func ( ) {
if p := recover ( ) ; p != nil {
shared . Rollback ( tx )
panic ( p )
} else if err != nil {
shared . Rollback ( tx )
} else {
err = tx . Commit ( )
}
} ( )
_ , receiptBytes , txs , err := b . IPLDRetriever . RetrieveReceiptsByBlockHash ( tx , hash )
2020-04-02 03:34:06 +00:00
if err != nil {
return nil , err
}
2020-10-26 13:58:37 +00:00
logs := make ( [ ] [ ] * types . Log , len ( receiptBytes ) )
for i , rctBytes := range receiptBytes {
var rct types . Receipt
if err := rlp . DecodeBytes ( rctBytes , & rct ) ; err != nil {
return nil , err
}
2021-05-21 03:30:38 +00:00
2021-07-26 10:13:38 +00:00
for _ , log := range rct . Logs {
log . TxHash = txs [ i ]
}
2020-10-26 13:58:37 +00:00
logs [ i ] = rct . Logs
2020-04-02 03:34:06 +00:00
}
2020-10-26 13:58:37 +00:00
return logs , nil
2020-04-02 03:34:06 +00:00
}
2020-09-25 13:58:18 +00:00
// StateAndHeaderByNumberOrHash returns the statedb and header for the provided block number or hash
func ( b * Backend ) StateAndHeaderByNumberOrHash ( ctx context . Context , blockNrOrHash rpc . BlockNumberOrHash ) ( * state . StateDB , * types . Header , error ) {
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
return b . StateAndHeaderByNumber ( ctx , blockNr )
}
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
header , err := b . HeaderByHash ( ctx , hash )
if err != nil {
return nil , nil , err
}
if header == nil {
return nil , nil , errors . New ( "header for hash not found" )
}
2021-04-16 13:02:04 +00:00
canonicalHash , err := b . GetCanonicalHash ( header . Number . Uint64 ( ) )
if err != nil {
return nil , nil , err
}
if blockNrOrHash . RequireCanonical && canonicalHash != hash {
2020-09-25 13:58:18 +00:00
return nil , nil , errors . New ( "hash is not currently canonical" )
}
2021-02-19 20:23:45 +00:00
stateDb , err := state . New ( header . Root , b . StateDatabase , nil )
2020-09-25 13:58:18 +00:00
return stateDb , header , err
}
return nil , nil , errors . New ( "invalid arguments; neither block nor hash specified" )
}
// StateAndHeaderByNumber returns the statedb and header for a provided block number
func ( b * Backend ) StateAndHeaderByNumber ( ctx context . Context , number rpc . BlockNumber ) ( * state . StateDB , * types . Header , error ) {
// Pending state is only known by the miner
if number == rpc . PendingBlockNumber {
2020-10-20 20:33:18 +00:00
return nil , nil , errPendingBlockNumber
2020-09-25 13:58:18 +00:00
}
// Otherwise resolve the block number and return its state
header , err := b . HeaderByNumber ( ctx , number )
if err != nil {
return nil , nil , err
}
if header == nil {
return nil , nil , errors . New ( "header not found" )
}
2021-02-19 20:23:45 +00:00
stateDb , err := state . New ( header . Root , b . StateDatabase , nil )
2020-09-25 13:58:18 +00:00
return stateDb , header , err
}
2020-10-20 20:33:18 +00:00
// GetCanonicalHash gets the canonical hash for the provided number, if there is one
2021-04-16 13:02:04 +00:00
func ( b * Backend ) GetCanonicalHash ( number uint64 ) ( common . Hash , error ) {
2020-10-20 20:33:18 +00:00
var hashResult string
if err := b . DB . Get ( & hashResult , RetrieveCanonicalBlockHashByNumber , number ) ; err != nil {
2021-04-16 13:02:04 +00:00
return common . Hash { } , err
2020-10-20 20:33:18 +00:00
}
2021-04-16 13:02:04 +00:00
return common . HexToHash ( hashResult ) , nil
2020-10-20 20:33:18 +00:00
}
type rowResult struct {
CID string
Data [ ] byte
}
// GetCanonicalHeader gets the canonical header for the provided number, if there is one
func ( b * Backend ) GetCanonicalHeader ( number uint64 ) ( string , [ ] byte , error ) {
headerResult := new ( rowResult )
return headerResult . CID , headerResult . Data , b . DB . QueryRowx ( RetrieveCanonicalHeaderByNumber , number ) . StructScan ( headerResult )
}
// GetEVM constructs and returns a vm.EVM
2021-09-29 05:27:11 +00:00
func ( b * Backend ) GetEVM ( ctx context . Context , msg core . Message , state * state . StateDB , header * types . Header ) ( * vm . EVM , func ( ) error , error ) {
vmError := func ( ) error { return nil }
2021-02-19 20:23:45 +00:00
txContext := core . NewEVMTxContext ( msg )
2021-09-29 05:27:11 +00:00
context := core . NewEVMBlockContext ( header , b , nil )
return vm . NewEVM ( context , txContext , state , b . Config . ChainConfig , b . Config . VMConfig ) , vmError , nil
2020-10-20 20:33:18 +00:00
}
2020-10-30 03:07:39 +00:00
// GetAccountByNumberOrHash returns the account object for the provided address at the block corresponding to the provided number or hash
2021-10-07 09:35:11 +00:00
func ( b * Backend ) GetAccountByNumberOrHash ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( * types . StateAccount , error ) {
2020-10-30 03:07:39 +00:00
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
2021-04-21 15:00:01 +00:00
return b . GetAccountByNumber ( ctx , address , blockNr )
2020-10-30 03:07:39 +00:00
}
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
return b . GetAccountByHash ( ctx , address , hash )
}
return nil , errors . New ( "invalid arguments; neither block nor hash specified" )
}
// GetAccountByNumber returns the account object for the provided address at the canonical block at the provided height
2021-10-07 09:35:11 +00:00
func ( b * Backend ) GetAccountByNumber ( ctx context . Context , address common . Address , blockNumber rpc . BlockNumber ) ( * types . StateAccount , error ) {
2021-04-21 15:00:01 +00:00
var err error
number := blockNumber . Int64 ( )
if blockNumber == rpc . LatestBlockNumber {
number , err = b . Retriever . RetrieveLastBlockNumber ( )
if err != nil {
return nil , err
}
}
if blockNumber == rpc . EarliestBlockNumber {
number , err = b . Retriever . RetrieveFirstBlockNumber ( )
if err != nil {
return nil , err
}
}
if blockNumber == rpc . PendingBlockNumber {
return nil , errPendingBlockNumber
}
hash , err := b . GetCanonicalHash ( uint64 ( number ) )
2021-06-09 18:50:12 +00:00
if err == sql . ErrNoRows {
2021-06-10 06:20:25 +00:00
return nil , errHeaderNotFound
2021-06-03 16:49:48 +00:00
} else if err != nil {
return nil , err
2020-10-30 03:07:39 +00:00
}
2021-06-03 16:49:48 +00:00
2020-10-30 03:07:39 +00:00
return b . GetAccountByHash ( ctx , address , hash )
}
// GetAccountByHash returns the account object for the provided address at the block with the provided hash
2021-10-07 09:35:11 +00:00
func ( b * Backend ) GetAccountByHash ( ctx context . Context , address common . Address , hash common . Hash ) ( * types . StateAccount , error ) {
2021-06-03 16:49:48 +00:00
_ , err := b . HeaderByHash ( context . Background ( ) , hash )
if err == sql . ErrNoRows {
2021-06-10 06:20:25 +00:00
return nil , errHeaderHashNotFound
} else if err != nil {
return nil , err
2021-06-03 16:49:48 +00:00
}
2020-10-30 03:07:39 +00:00
_ , accountRlp , err := b . IPLDRetriever . RetrieveAccountByAddressAndBlockHash ( address , hash )
if err != nil {
return nil , err
}
2021-06-03 16:49:48 +00:00
2021-10-07 09:35:11 +00:00
acct := new ( types . StateAccount )
2020-10-30 03:07:39 +00:00
return acct , rlp . DecodeBytes ( accountRlp , acct )
}
// GetCodeByNumberOrHash returns the byte code for the contract deployed at the provided address at the block with the provided hash or block number
func ( b * Backend ) GetCodeByNumberOrHash ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( [ ] byte , error ) {
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
2021-04-21 11:16:47 +00:00
return b . GetCodeByNumber ( ctx , address , blockNr )
2020-10-30 03:07:39 +00:00
}
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
return b . GetCodeByHash ( ctx , address , hash )
}
return nil , errors . New ( "invalid arguments; neither block nor hash specified" )
}
// GetCodeByNumber returns the byte code for the contract deployed at the provided address at the canonical block with the provided block number
2021-04-21 15:00:01 +00:00
func ( b * Backend ) GetCodeByNumber ( ctx context . Context , address common . Address , blockNumber rpc . BlockNumber ) ( [ ] byte , error ) {
var err error
number := blockNumber . Int64 ( )
if blockNumber == rpc . LatestBlockNumber {
number , err = b . Retriever . RetrieveLastBlockNumber ( )
2021-04-21 11:16:47 +00:00
if err != nil {
return nil , err
}
}
2021-04-21 15:00:01 +00:00
if blockNumber == rpc . EarliestBlockNumber {
number , err = b . Retriever . RetrieveFirstBlockNumber ( )
if err != nil {
return nil , err
}
}
if blockNumber == rpc . PendingBlockNumber {
return nil , errPendingBlockNumber
}
hash , err := b . GetCanonicalHash ( uint64 ( number ) )
2021-04-16 13:02:04 +00:00
if err != nil {
return nil , err
}
2020-10-30 03:07:39 +00:00
if hash == ( common . Hash { } ) {
return nil , fmt . Errorf ( "no canoncial block hash found for provided height (%d)" , number )
}
return b . GetCodeByHash ( ctx , address , hash )
}
// GetCodeByHash returns the byte code for the contract deployed at the provided address at the block with the provided hash
func ( b * Backend ) GetCodeByHash ( ctx context . Context , address common . Address , hash common . Hash ) ( [ ] byte , error ) {
codeHash := make ( [ ] byte , 0 )
leafKey := crypto . Keccak256Hash ( address . Bytes ( ) )
// Begin tx
tx , err := b . DB . Beginx ( )
if err != nil {
return nil , err
}
defer func ( ) {
if p := recover ( ) ; p != nil {
2022-03-10 12:35:19 +00:00
shared . Rollback ( tx )
2020-10-30 03:07:39 +00:00
panic ( p )
} else if err != nil {
2022-03-10 12:35:19 +00:00
shared . Rollback ( tx )
2020-10-30 03:07:39 +00:00
} else {
err = tx . Commit ( )
}
} ( )
2021-04-19 13:42:09 +00:00
err = tx . Get ( & codeHash , RetrieveCodeHashByLeafKeyAndBlockHash , leafKey . Hex ( ) , hash . Hex ( ) )
if err != nil {
2020-10-30 03:07:39 +00:00
return nil , err
}
2021-04-19 13:42:09 +00:00
var mhKey string
2021-09-21 12:10:55 +00:00
mhKey , err = ethServerShared . MultihashKeyFromKeccak256 ( common . BytesToHash ( codeHash ) )
2020-10-30 03:07:39 +00:00
if err != nil {
return nil , err
}
code := make ( [ ] byte , 0 )
err = tx . Get ( & code , RetrieveCodeByMhKey , mhKey )
return code , err
}
2020-10-30 16:54:22 +00:00
// GetStorageByNumberOrHash returns the storage value for the provided contract address an storage key at the block corresponding to the provided number or hash
2021-06-18 06:42:29 +00:00
func ( b * Backend ) GetStorageByNumberOrHash ( ctx context . Context , address common . Address , key common . Hash , blockNrOrHash rpc . BlockNumberOrHash ) ( hexutil . Bytes , error ) {
2020-10-30 16:54:22 +00:00
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
2021-06-18 06:42:29 +00:00
return b . GetStorageByNumber ( ctx , address , key , blockNr )
2020-10-30 16:54:22 +00:00
}
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
2021-06-18 06:42:29 +00:00
return b . GetStorageByHash ( ctx , address , key , hash )
2020-10-30 16:54:22 +00:00
}
return nil , errors . New ( "invalid arguments; neither block nor hash specified" )
}
// GetStorageByNumber returns the storage value for the provided contract address an storage key at the block corresponding to the provided number
2021-06-18 06:42:29 +00:00
func ( b * Backend ) GetStorageByNumber ( ctx context . Context , address common . Address , key common . Hash , blockNumber rpc . BlockNumber ) ( hexutil . Bytes , error ) {
2021-04-21 15:00:01 +00:00
var err error
number := blockNumber . Int64 ( )
if blockNumber == rpc . LatestBlockNumber {
number , err = b . Retriever . RetrieveLastBlockNumber ( )
if err != nil {
return nil , err
}
}
if blockNumber == rpc . EarliestBlockNumber {
number , err = b . Retriever . RetrieveFirstBlockNumber ( )
if err != nil {
return nil , err
}
}
if blockNumber == rpc . PendingBlockNumber {
return nil , errPendingBlockNumber
}
hash , err := b . GetCanonicalHash ( uint64 ( number ) )
2021-06-10 06:20:25 +00:00
if err == sql . ErrNoRows {
return nil , errHeaderNotFound
} else if err != nil {
2021-04-16 13:02:04 +00:00
return nil , err
}
2021-06-10 06:20:25 +00:00
2021-06-18 06:42:29 +00:00
return b . GetStorageByHash ( ctx , address , key , hash )
2020-10-30 16:54:22 +00:00
}
// GetStorageByHash returns the storage value for the provided contract address an storage key at the block corresponding to the provided hash
2021-06-18 06:42:29 +00:00
func ( b * Backend ) GetStorageByHash ( ctx context . Context , address common . Address , key , hash common . Hash ) ( hexutil . Bytes , error ) {
2021-06-10 06:20:25 +00:00
_ , err := b . HeaderByHash ( context . Background ( ) , hash )
if err == sql . ErrNoRows {
return nil , errHeaderHashNotFound
} else if err != nil {
return nil , err
}
2021-05-26 11:37:25 +00:00
_ , _ , storageRlp , err := b . IPLDRetriever . RetrieveStorageAtByAddressAndStorageSlotAndBlockHash ( address , key , hash )
2020-10-30 16:54:22 +00:00
return storageRlp , err
}
2020-10-20 20:33:18 +00:00
// Engine satisfied the ChainContext interface
func ( b * Backend ) Engine ( ) consensus . Engine {
// TODO: we need to support more than just ethash based engines
return ethash . NewFaker ( )
}
// GetHeader satisfied the ChainContext interface
func ( b * Backend ) GetHeader ( hash common . Hash , height uint64 ) * types . Header {
header , err := b . HeaderByHash ( context . Background ( ) , hash )
if err != nil {
return nil
}
return header
}
2020-10-26 13:58:37 +00:00
2021-12-29 20:57:21 +00:00
// ValidateTrie validates the trie for the given stateRoot
2021-09-21 12:10:55 +00:00
func ( b * Backend ) ValidateTrie ( stateRoot common . Hash ) error {
return validator . NewValidator ( nil , b . EthDB ) . ValidateTrie ( stateRoot )
}
2020-10-26 13:58:37 +00:00
// RPCGasCap returns the configured gas cap for the rpc server
func ( b * Backend ) RPCGasCap ( ) * big . Int {
return b . Config . RPCGasCap
}
func ( b * Backend ) SubscribeNewTxsEvent ( chan <- core . NewTxsEvent ) event . Subscription {
panic ( "implement me" )
}
func ( b * Backend ) SubscribeChainEvent ( ch chan <- core . ChainEvent ) event . Subscription {
panic ( "implement me" )
}
func ( b * Backend ) SubscribeRemovedLogsEvent ( ch chan <- core . RemovedLogsEvent ) event . Subscription {
panic ( "implement me" )
}
func ( b * Backend ) SubscribeLogsEvent ( ch chan <- [ ] * types . Log ) event . Subscription {
panic ( "implement me" )
}
func ( b * Backend ) SubscribePendingLogsEvent ( ch chan <- [ ] * types . Log ) event . Subscription {
panic ( "implement me" )
}
func ( b * Backend ) BloomStatus ( ) ( uint64 , uint64 ) {
panic ( "implement me" )
}
func ( b * Backend ) ServiceFilter ( ctx context . Context , session * bloombits . MatcherSession ) {
panic ( "implement me" )
}
2021-09-21 12:10:55 +00:00
func logStateDBStatsOnTimer ( ethDB * ipfsethdb . Database , gcc * shared . GroupCacheConfig ) {
// No stats logging if interval isn't a positive integer.
if gcc . StateDB . LogStatsIntervalInSecs <= 0 {
return
}
ticker := time . NewTicker ( time . Duration ( gcc . StateDB . LogStatsIntervalInSecs ) * time . Second )
go func ( ) {
for range ticker . C {
log . Infof ( "%s groupcache stats: %+v" , StateDBGroupCacheName , ethDB . GetCacheStats ( ) )
}
} ( )
}