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"
"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/common/math"
"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"
2020-08-11 03:53:01 +00:00
2021-02-24 22:20:06 +00:00
"github.com/vulcanize/ipfs-ethdb"
2020-09-02 15:19:25 +00:00
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
2020-10-30 03:07:39 +00:00
shared2 "github.com/vulcanize/ipld-eth-indexer/pkg/shared"
2020-08-31 15:47:06 +00:00
"github.com/vulcanize/ipld-eth-server/pkg/shared"
2020-01-16 23:21:49 +00:00
)
var (
2020-10-20 20:33:18 +00:00
errPendingBlockNumber = errors . New ( "pending block number not supported" )
errNegativeBlockNumber = errors . New ( "negative block number not supported" )
)
const (
RetrieveCanonicalBlockHashByNumber = ` SELECT block_hash FROM eth . header_cids
INNER JOIN public . blocks ON ( header_cids . mh_key = blocks . key )
2020-11-09 16:29:04 +00:00
WHERE id = ( SELECT canonical_header_id ( $ 1 ) ) `
2020-10-20 20:33:18 +00:00
RetrieveCanonicalHeaderByNumber = ` SELECT cid , data FROM eth . header_cids
INNER JOIN public . blocks ON ( header_cids . mh_key = blocks . key )
2020-11-09 16:29:04 +00:00
WHERE id = ( SELECT canonical_header_id ( $ 1 ) ) `
2020-10-26 13:58:37 +00:00
RetrieveTD = ` SELECT td FROM eth . header_cids
WHERE header_cids . block_hash = $ 1 `
RetrieveRPCTransaction = ` SELECT blocks . data , block_hash , block_number , index FROM public . blocks , eth . transaction_cids , eth . header_cids
WHERE blocks . key = transaction_cids . mh_key
AND transaction_cids . header_id = header_cids . id
AND transaction_cids . tx_hash = $ 1 `
2020-10-30 03:07:39 +00:00
RetrieveCodeHashByLeafKeyAndBlockHash = ` SELECT code_hash FROM eth . state_accounts , eth . state_cids , eth . header_cids
WHERE state_accounts . state_id = state_cids . id
AND state_cids . header_id = header_cids . id
AND state_leaf_key = $ 1
2020-10-30 03:08:26 +00:00
AND block_number <= ( SELECT block_number
FROM eth . header_cids
WHERE block_hash = $ 2 )
2020-11-09 16:29:04 +00:00
AND header_cids . id = ( SELECT canonical_header_id ( block_number ) )
2020-10-30 03:08:26 +00:00
ORDER BY block_number DESC
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
)
type Backend struct {
2020-10-20 20:33:18 +00:00
// underlying postgres db
DB * postgres . DB
// postgres db interfaces
Retriever * CIDRetriever
Fetcher * IPLDFetcher
IPLDRetriever * IPLDRetriever
// ethereum interfaces
EthDB ethdb . Database
StateDatabase state . Database
Config * Config
}
type Config struct {
ChainConfig * params . ChainConfig
VmConfig vm . Config
DefaultSender * common . Address
RPCGasCap * big . Int
2020-01-16 23:21:49 +00:00
}
2020-10-20 20:33:18 +00:00
func NewEthBackend ( db * postgres . DB , c * Config ) ( * Backend , error ) {
2020-01-17 23:16:01 +00:00
r := NewCIDRetriever ( db )
2020-10-20 20:33:18 +00:00
ethDB := ipfsethdb . NewDatabase ( db . DB )
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 ) {
_ , headerRLP , err := b . IPLDRetriever . RetrieveHeaderByHash ( hash )
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" )
}
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
// BlockByNumber returns the requested canonical block.
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
}
// 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-01-26 19:55:26 +00:00
// Retrieve all the CIDs for the block
2020-10-20 20:33:18 +00:00
// TODO: optimize this by retrieving iplds directly rather than the cids first (this is remanent from when we fetched iplds through ipfs blockservice interface)
headerCID , uncleCIDs , txCIDs , rctCIDs , err := b . Retriever . RetrieveBlockByHash ( canonicalHash )
2020-01-26 19:55:26 +00:00
if err != nil {
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
}
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 {
shared . Rollback ( tx )
panic ( p )
} else if err != nil {
shared . Rollback ( tx )
} else {
err = tx . Commit ( )
}
} ( )
2020-02-20 22:13:19 +00:00
2020-01-26 19:55:26 +00:00
// Fetch and decode the header IPLD
2020-05-01 16:07:47 +00:00
headerIPLD , err := b . Fetcher . FetchHeader ( tx , headerCID )
2020-01-26 19:55:26 +00:00
if err != nil {
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
}
2020-02-20 22:13:19 +00:00
var header types . Header
2020-02-23 23:14:29 +00:00
if err := rlp . DecodeBytes ( headerIPLD . Data , & header ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
// Fetch and decode the uncle IPLDs
2020-05-01 16:07:47 +00:00
uncleIPLDs , err := b . Fetcher . FetchUncles ( tx , uncleCIDs )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
var uncles [ ] * types . Header
for _ , uncleIPLD := range uncleIPLDs {
2020-02-20 22:13:19 +00:00
var uncle types . Header
if err := rlp . DecodeBytes ( uncleIPLD . Data , & uncle ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2020-02-20 22:13:19 +00:00
uncles = append ( uncles , & uncle )
2020-01-26 19:55:26 +00:00
}
// Fetch and decode the transaction IPLDs
2020-05-01 16:07:47 +00:00
txIPLDs , err := b . Fetcher . FetchTrxs ( tx , txCIDs )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
var transactions [ ] * types . Transaction
for _ , txIPLD := range txIPLDs {
2020-05-01 21:25:58 +00:00
var transaction types . Transaction
if err := rlp . DecodeBytes ( txIPLD . Data , & transaction ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2020-05-01 21:25:58 +00:00
transactions = append ( transactions , & transaction )
2020-01-26 19:55:26 +00:00
}
// Fetch and decode the receipt IPLDs
2020-05-01 16:07:47 +00:00
rctIPLDs , err := b . Fetcher . FetchRcts ( tx , rctCIDs )
2020-01-26 19:55:26 +00:00
if err != nil {
return nil , err
}
var receipts [ ] * types . Receipt
for _ , rctIPLD := range rctIPLDs {
2020-02-20 22:13:19 +00:00
var receipt types . Receipt
if err := rlp . DecodeBytes ( rctIPLD . Data , & receipt ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2020-02-20 22:13:19 +00:00
receipts = append ( receipts , & receipt )
2020-01-26 19:55:26 +00:00
}
// Compose everything together into a complete block
2021-02-19 20:23:45 +00:00
return types . NewBlock ( & header , transactions , uncles , receipts , new ( trie . Trie ) ) , err
2020-01-26 19:55:26 +00:00
}
// BlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
// detail, otherwise only the transaction hash is returned.
func ( b * Backend ) BlockByHash ( ctx context . Context , hash common . Hash ) ( * types . Block , error ) {
// Retrieve all the CIDs for the block
2020-02-20 22:12:52 +00:00
headerCID , uncleCIDs , txCIDs , rctCIDs , err := b . Retriever . RetrieveBlockByHash ( hash )
2020-01-26 19:55:26 +00:00
if err != nil {
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
}
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 {
shared . Rollback ( tx )
panic ( p )
} else if err != nil {
shared . Rollback ( tx )
} else {
err = tx . Commit ( )
}
} ( )
2020-01-26 19:55:26 +00:00
// Fetch and decode the header IPLD
2020-05-01 16:07:47 +00:00
headerIPLD , err := b . Fetcher . FetchHeader ( tx , headerCID )
2020-01-26 19:55:26 +00:00
if err != nil {
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
}
2020-02-20 22:13:19 +00:00
var header types . Header
2020-02-23 23:14:29 +00:00
if err := rlp . DecodeBytes ( headerIPLD . Data , & header ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
// Fetch and decode the uncle IPLDs
2020-05-01 16:07:47 +00:00
uncleIPLDs , err := b . Fetcher . FetchUncles ( tx , uncleCIDs )
2020-01-26 19:55:26 +00:00
if err != nil {
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
}
var uncles [ ] * types . Header
for _ , uncleIPLD := range uncleIPLDs {
2020-02-20 22:13:19 +00:00
var uncle types . Header
if err := rlp . DecodeBytes ( uncleIPLD . Data , & uncle ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2020-02-20 22:13:19 +00:00
uncles = append ( uncles , & uncle )
2020-01-26 19:55:26 +00:00
}
// Fetch and decode the transaction IPLDs
2020-05-01 16:07:47 +00:00
txIPLDs , err := b . Fetcher . FetchTrxs ( tx , txCIDs )
2020-01-26 19:55:26 +00:00
if err != nil {
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
}
var transactions [ ] * types . Transaction
for _ , txIPLD := range txIPLDs {
2020-05-01 21:25:58 +00:00
var transaction types . Transaction
if err := rlp . DecodeBytes ( txIPLD . Data , & transaction ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2020-05-01 21:25:58 +00:00
transactions = append ( transactions , & transaction )
2020-01-26 19:55:26 +00:00
}
// Fetch and decode the receipt IPLDs
2020-05-01 16:07:47 +00:00
rctIPLDs , err := b . Fetcher . FetchRcts ( tx , rctCIDs )
2020-01-26 19:55:26 +00:00
if err != nil {
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
}
var receipts [ ] * types . Receipt
for _ , rctIPLD := range rctIPLDs {
2020-02-20 22:13:19 +00:00
var receipt types . Receipt
if err := rlp . DecodeBytes ( rctIPLD . Data , & receipt ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , err
}
2020-02-20 22:13:19 +00:00
receipts = append ( receipts , & receipt )
2020-01-26 19:55:26 +00:00
}
// Compose everything together into a complete block
2021-02-19 20:23:45 +00:00
return types . NewBlock ( & header , transactions , uncles , receipts , new ( trie . Trie ) ) , err
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 ) {
2020-08-11 03:53:01 +00:00
var tempTxStruct struct {
Data [ ] byte ` db:"data" `
2020-01-26 19:55:26 +00:00
BlockHash string ` db:"block_hash" `
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
}
2020-10-26 13:58:37 +00:00
if err := b . DB . Get ( & tempTxStruct , RetrieveRPCTransaction , txHash . String ( ) ) ; err != nil {
2020-05-01 16:07:47 +00:00
return nil , common . Hash { } , 0 , 0 , err
}
2020-02-20 22:13:19 +00:00
var transaction types . Transaction
2020-08-11 03:53:01 +00:00
if err := rlp . DecodeBytes ( tempTxStruct . Data , & transaction ) ; err != nil {
2020-01-26 19:55:26 +00:00
return nil , common . Hash { } , 0 , 0 , err
}
2020-08-11 03:53:01 +00:00
return & transaction , common . HexToHash ( tempTxStruct . BlockHash ) , tempTxStruct . BlockNumber , tempTxStruct . 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 ) {
_ , receiptBytes , err := b . IPLDRetriever . RetrieveReceiptsByBlockHash ( hash )
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 )
if err := rlp . DecodeBytes ( rctBytes , rct ) ; err != nil {
return nil , err
2020-04-02 03:34:06 +00:00
}
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 ) {
_ , receiptBytes , err := b . IPLDRetriever . RetrieveReceiptsByBlockHash ( 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
}
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
func ( b * Backend ) GetEVM ( ctx context . Context , msg core . Message , state * state . StateDB , header * types . Header ) ( * vm . EVM , error ) {
state . SetBalance ( msg . From ( ) , math . MaxBig256 )
2021-02-19 20:23:45 +00:00
vmctx := core . NewEVMBlockContext ( header , b , nil )
txContext := core . NewEVMTxContext ( msg )
return vm . NewEVM ( vmctx , txContext , state , b . Config . ChainConfig , b . Config . VmConfig ) , 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
func ( b * Backend ) GetAccountByNumberOrHash ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( * state . Account , error ) {
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
return b . GetAccountByNumber ( ctx , address , uint64 ( blockNr . Int64 ( ) ) )
}
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
func ( b * Backend ) GetAccountByNumber ( ctx context . Context , address common . Address , number uint64 ) ( * state . Account , error ) {
2021-04-16 13:02:04 +00:00
hash , err := b . GetCanonicalHash ( number )
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 . GetAccountByHash ( ctx , address , hash )
}
// GetAccountByHash returns the account object for the provided address at the block with the provided hash
func ( b * Backend ) GetAccountByHash ( ctx context . Context , address common . Address , hash common . Hash ) ( * state . Account , error ) {
_ , accountRlp , err := b . IPLDRetriever . RetrieveAccountByAddressAndBlockHash ( address , hash )
if err != nil {
return nil , err
}
acct := new ( state . Account )
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 {
return b . GetCodeByNumber ( ctx , address , uint64 ( blockNr . Int64 ( ) ) )
}
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
func ( b * Backend ) GetCodeByNumber ( ctx context . Context , address common . Address , number uint64 ) ( [ ] byte , error ) {
2021-04-16 13:02:04 +00:00
hash , err := b . GetCanonicalHash ( number )
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 {
shared . Rollback ( tx )
panic ( p )
} else if err != nil {
shared . Rollback ( tx )
} else {
err = tx . Commit ( )
}
} ( )
if err := tx . Get ( & codeHash , RetrieveCodeHashByLeafKeyAndBlockHash , leafKey . Hex ( ) , hash . Hex ( ) ) ; err != nil {
return nil , err
}
mhKey , err := shared2 . MultihashKeyFromKeccak256 ( common . BytesToHash ( codeHash ) )
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
func ( b * Backend ) GetStorageByNumberOrHash ( ctx context . Context , address common . Address , storageLeafKey common . Hash , blockNrOrHash rpc . BlockNumberOrHash ) ( hexutil . Bytes , error ) {
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
return b . GetStorageByNumber ( ctx , address , storageLeafKey , uint64 ( blockNr . Int64 ( ) ) )
}
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
return b . GetStorageByHash ( ctx , address , storageLeafKey , hash )
}
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
func ( b * Backend ) GetStorageByNumber ( ctx context . Context , address common . Address , storageLeafKey common . Hash , number uint64 ) ( hexutil . Bytes , error ) {
2021-04-16 13:02:04 +00:00
hash , err := b . GetCanonicalHash ( number )
if err != nil {
return nil , err
}
2020-10-30 16:54:22 +00:00
if hash == ( common . Hash { } ) {
return nil , fmt . Errorf ( "no canoncial block hash found for provided height (%d)" , number )
}
return b . GetStorageByHash ( ctx , address , storageLeafKey , hash )
}
// GetStorageByHash returns the storage value for the provided contract address an storage key at the block corresponding to the provided hash
func ( b * Backend ) GetStorageByHash ( ctx context . Context , address common . Address , storageLeafKey , hash common . Hash ) ( hexutil . Bytes , error ) {
_ , storageRlp , err := b . IPLDRetriever . RetrieveStorageAtByAddressAndStorageKeyAndBlockHash ( address , storageLeafKey , hash )
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
// 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" )
}