eth JSON-RPC #22
20
README.md
20
README.md
@ -94,12 +94,30 @@ ipld-eth-server currently recapitulates portions of the Ethereum JSON-RPC api st
|
||||
|
||||
The currently supported standard endpoints are:
|
||||
`eth_call`
|
||||
`eth_getBalance`
|
||||
`eth_getStorageAt`
|
||||
`eth_getCode`
|
||||
`eth_getProof`
|
||||
`eth_blockNumber`
|
||||
`eth_getLogs`
|
||||
`eth_getHeaderByNumber`
|
||||
`eth_getHeaderByHash`
|
||||
`eth_getBlockByNumber`
|
||||
`eth_getBlockByHash`
|
||||
`eth_getTransactionCount`
|
||||
`eth_getBlockTransactionCountByHash`
|
||||
`eth_getBlockTransactionCountByNumber`
|
||||
`eth_getTransactionByHash`
|
||||
`eth_getRawTransactionByHash`
|
||||
`eth_getTransactionByBlockHashAndIndex`
|
||||
`eth_getTransactionByBlockNumberAndIndex`
|
||||
`eth_getRawTransactionByBlockHashAndIndex`
|
||||
`eth_getRawTransactionByBlockNumberAndIndex`
|
||||
`eth_getTransactionReceipt`
|
||||
`eth_getLogs`
|
||||
`eth_getUncleCountByBlockHash`
|
||||
`eth_getUncleCountByBlockNumber`
|
||||
`eth_getUncleByBlockHashAndIndex`
|
||||
`eth_getUncleByBlockNumberAndIndex`
|
||||
|
||||
TODO: Add the rest of the standard endpoints and unique endpoints (e.g. getSlice)
|
||||
|
||||
|
37
db/migrations/00018_create_check_if_removed_functions.sql
Normal file
37
db/migrations/00018_create_check_if_removed_functions.sql
Normal file
@ -0,0 +1,37 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
-- returns if a storage node at the provided path was removed in the range > the provided height and <= the provided block hash
|
||||
CREATE OR REPLACE FUNCTION was_storage_removed(path BYTEA, height BIGINT, hash VARCHAR(66)) RETURNS BOOLEAN
|
||||
AS $$
|
||||
SELECT exists(SELECT *
|
||||
FROM eth.storage_cids
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE storage_path = path
|
||||
AND block_number > height
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = hash)
|
||||
AND storage_cids.node_type = 3);
|
||||
$$ LANGUAGE SQL;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose StatementBegin
|
||||
-- returns if a state node at the provided path was removed in the range > the provided height and <= the provided block hash
|
||||
CREATE OR REPLACE FUNCTION was_state_removed(path BYTEA, height BIGINT, hash VARCHAR(66)) RETURNS BOOLEAN
|
||||
AS $$
|
||||
SELECT exists(SELECT *
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE state_path = path
|
||||
AND block_number > height
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = hash)
|
||||
AND state_cids.node_type = 3);
|
||||
$$ LANGUAGE SQL;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
DROP FUNCTION was_storage_removed;
|
||||
DROP FUNCTION was_state_removed;
|
@ -100,6 +100,45 @@ CREATE FUNCTION public.header_weight(hash character varying) RETURNS bigint
|
||||
$$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: was_state_removed(bytea, bigint, character varying); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.was_state_removed(path bytea, height bigint, hash character varying) RETURNS boolean
|
||||
LANGUAGE sql
|
||||
AS $$
|
||||
SELECT exists(SELECT *
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE state_path = path
|
||||
AND block_number > height
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = hash)
|
||||
AND state_cids.node_type = 3);
|
||||
$$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: was_storage_removed(bytea, bigint, character varying); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.was_storage_removed(path bytea, height bigint, hash character varying) RETURNS boolean
|
||||
LANGUAGE sql
|
||||
AS $$
|
||||
SELECT exists(SELECT *
|
||||
FROM eth.storage_cids
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE storage_path = path
|
||||
AND block_number > height
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = hash)
|
||||
AND storage_cids.node_type = 3);
|
||||
$$;
|
||||
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_table_access_method = heap;
|
||||
|
673
pkg/eth/api.go
673
pkg/eth/api.go
@ -29,8 +29,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
@ -66,29 +68,436 @@ func NewPublicEthAPI(b *Backend, client *rpc.Client) *PublicEthAPI {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Headers and blocks
|
||||
|
||||
*/
|
||||
|
||||
// GetHeaderByNumber returns the requested canonical block header.
|
||||
// * When blockNr is -1 the chain head is returned.
|
||||
// * We cannot support pending block calls since we do not have an active miner
|
||||
func (pea *PublicEthAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (map[string]interface{}, error) {
|
||||
header, err := pea.B.HeaderByNumber(ctx, number)
|
||||
if header != nil && err == nil {
|
||||
return pea.rpcMarshalHeader(header)
|
||||
}
|
||||
if pea.ethClient != nil {
|
||||
if header, err := pea.ethClient.HeaderByNumber(ctx, big.NewInt(number.Int64())); header != nil && err == nil {
|
||||
return pea.rpcMarshalHeader(header)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetHeaderByHash returns the requested header by hash.
|
||||
func (pea *PublicEthAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) map[string]interface{} {
|
||||
header, err := pea.B.HeaderByHash(ctx, hash)
|
||||
if header != nil && err == nil {
|
||||
if res, err := pea.rpcMarshalHeader(header); err == nil {
|
||||
return res
|
||||
}
|
||||
}
|
||||
if pea.ethClient != nil {
|
||||
if header, err := pea.ethClient.HeaderByHash(ctx, hash); header != nil && err == nil {
|
||||
if res, err := pea.rpcMarshalHeader(header); err != nil {
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rpcMarshalHeader uses the generalized output filler, then adds the total difficulty field
|
||||
func (pea *PublicEthAPI) rpcMarshalHeader(header *types.Header) (map[string]interface{}, error) {
|
||||
fields := RPCMarshalHeader(header)
|
||||
td, err := pea.B.GetTd(header.Hash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields["totalDifficulty"] = (*hexutil.Big)(td)
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// BlockNumber returns the block number of the chain head.
|
||||
func (pea *PublicEthAPI) BlockNumber() hexutil.Uint64 {
|
||||
number, _ := pea.B.Retriever.RetrieveLastBlockNumber()
|
||||
return hexutil.Uint64(number)
|
||||
}
|
||||
|
||||
// GetBlockByNumber returns the requested canonical block.
|
||||
// * When blockNr is -1 the chain head is returned.
|
||||
// * We cannot support pending block calls since we do not have an active miner
|
||||
// * When fullTx is true all transactions in the block are returned, otherwise
|
||||
// only the transaction hash is returned.
|
||||
func (pea *PublicEthAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
|
||||
block, err := pea.B.BlockByNumber(ctx, number)
|
||||
if block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
if pea.ethClient != nil {
|
||||
if block, err := pea.ethClient.BlockByNumber(ctx, big.NewInt(number.Int64())); block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetBlockByHash 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 (pea *PublicEthAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) {
|
||||
block, err := pea.B.BlockByHash(ctx, hash)
|
||||
if block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
if pea.ethClient != nil {
|
||||
if block, err := pea.ethClient.BlockByHash(ctx, hash); block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Uncles
|
||||
|
||||
*/
|
||||
|
||||
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true
|
||||
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
|
||||
func (pea *PublicEthAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) {
|
||||
block, err := pea.B.BlockByNumber(ctx, blockNr)
|
||||
if block != nil && err == nil {
|
||||
uncles := block.Uncles()
|
||||
if index >= hexutil.Uint(len(uncles)) {
|
||||
logrus.Debugf("uncle with index %s request at block number %d was not found", index.String(), blockNr.Int64())
|
||||
return nil, nil
|
||||
}
|
||||
block = types.NewBlockWithHeader(uncles[index])
|
||||
return pea.rpcMarshalBlock(block, false, false)
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
if uncle, uncleHashes, err := getBlockAndUncleHashes(pea.rpc, ctx, "eth_getUncleByBlockNumberAndIndex", blockNr, index); uncle != nil && err == nil {
|
||||
return pea.rpcMarshalBlockWithUncleHashes(uncle, uncleHashes, false, false)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true
|
||||
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
|
||||
func (pea *PublicEthAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (map[string]interface{}, error) {
|
||||
block, err := pea.B.BlockByHash(ctx, blockHash)
|
||||
if block != nil {
|
||||
uncles := block.Uncles()
|
||||
if index >= hexutil.Uint(len(uncles)) {
|
||||
logrus.Debugf("uncle with index %s request at block hash %s was not found", index.String(), blockHash.Hex())
|
||||
return nil, nil
|
||||
}
|
||||
block = types.NewBlockWithHeader(uncles[index])
|
||||
return pea.rpcMarshalBlock(block, false, false)
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
if uncle, uncleHashes, err := getBlockAndUncleHashes(pea.rpc, ctx, "eth_getUncleByBlockHashAndIndex", blockHash, index); uncle != nil && err == nil {
|
||||
return pea.rpcMarshalBlockWithUncleHashes(uncle, uncleHashes, false, false)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
|
||||
func (pea *PublicEthAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint {
|
||||
if block, err := pea.B.BlockByNumber(ctx, blockNr); block != nil && err == nil {
|
||||
n := hexutil.Uint(len(block.Uncles()))
|
||||
return &n
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var num *hexutil.Uint
|
||||
if err := pea.rpc.CallContext(ctx, &num, "eth_getUncleCountByBlockNumber", blockNr); num != nil && err == nil {
|
||||
return num
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
|
||||
func (pea *PublicEthAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint {
|
||||
if block, err := pea.B.BlockByHash(ctx, blockHash); block != nil && err == nil {
|
||||
n := hexutil.Uint(len(block.Uncles()))
|
||||
return &n
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var num *hexutil.Uint
|
||||
if err := pea.rpc.CallContext(ctx, &num, "eth_getUncleCountByBlockHash", blockHash); num != nil && err == nil {
|
||||
return num
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Transactions
|
||||
|
||||
*/
|
||||
|
||||
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
|
||||
func (pea *PublicEthAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
|
||||
count, err := pea.localGetTransactionCount(ctx, address, blockNrOrHash)
|
||||
if count != nil && err == nil {
|
||||
return count, nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var num *hexutil.Uint64
|
||||
if err := pea.rpc.CallContext(ctx, &num, "eth_getTransactionCount", address, blockNrOrHash); num != nil && err == nil {
|
||||
return num, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) localGetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
|
||||
account, err := pea.B.GetAccountByNumberOrHash(ctx, address, blockNrOrHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce := hexutil.Uint64(account.Nonce)
|
||||
return &nonce, nil
|
||||
}
|
||||
|
||||
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
|
||||
func (pea *PublicEthAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint {
|
||||
if block, _ := pea.B.BlockByNumber(ctx, blockNr); block != nil {
|
||||
n := hexutil.Uint(len(block.Transactions()))
|
||||
return &n
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var num *hexutil.Uint
|
||||
if err := pea.rpc.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", blockNr); num != nil && err == nil {
|
||||
return num
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash.
|
||||
func (pea *PublicEthAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint {
|
||||
if block, _ := pea.B.BlockByHash(ctx, blockHash); block != nil {
|
||||
n := hexutil.Uint(len(block.Transactions()))
|
||||
return &n
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var num *hexutil.Uint
|
||||
if err := pea.rpc.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash); num != nil && err == nil {
|
||||
return num
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
|
||||
func (pea *PublicEthAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction {
|
||||
if block, _ := pea.B.BlockByNumber(ctx, blockNr); block != nil {
|
||||
return newRPCTransactionFromBlockIndex(block, uint64(index))
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var tx *RPCTransaction
|
||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getTransactionByBlockNumberAndIndex", blockNr, index); tx != nil && err == nil {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
|
||||
func (pea *PublicEthAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction {
|
||||
if block, _ := pea.B.BlockByHash(ctx, blockHash); block != nil {
|
||||
return newRPCTransactionFromBlockIndex(block, uint64(index))
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var tx *RPCTransaction
|
||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, index); tx != nil && err == nil {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index.
|
||||
func (pea *PublicEthAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) hexutil.Bytes {
|
||||
if block, _ := pea.B.BlockByNumber(ctx, blockNr); block != nil {
|
||||
return newRPCRawTransactionFromBlockIndex(block, uint64(index))
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var tx hexutil.Bytes
|
||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getRawTransactionByBlockNumberAndIndex", blockNr, index); tx != nil && err == nil {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index.
|
||||
func (pea *PublicEthAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) hexutil.Bytes {
|
||||
if block, _ := pea.B.BlockByHash(ctx, blockHash); block != nil {
|
||||
return newRPCRawTransactionFromBlockIndex(block, uint64(index))
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var tx hexutil.Bytes
|
||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getRawTransactionByBlockHashAndIndex", blockHash, index); tx != nil && err == nil {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTransactionByHash returns the transaction for the given hash
|
||||
// eth ipld-eth-server cannot currently handle pending/tx_pool txs
|
||||
func (pea *PublicEthAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||
tx, blockHash, blockNumber, index, err := pea.B.GetTransaction(ctx, hash)
|
||||
if tx != nil && err == nil {
|
||||
return NewRPCTransaction(tx, blockHash, blockNumber, index), nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var tx *RPCTransaction
|
||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash); tx != nil && err == nil {
|
||||
return tx, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
||||
func (pea *PublicEthAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||
// Retrieve a finalized transaction, or a pooled otherwise
|
||||
tx, _, _, _, err := pea.B.GetTransaction(ctx, hash)
|
||||
if tx != nil && err == nil {
|
||||
return rlp.EncodeToBytes(tx)
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var tx hexutil.Bytes
|
||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getRawTransactionByHash", hash); tx != nil && err == nil {
|
||||
return tx, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Receipts and Logs
|
||||
|
||||
*/
|
||||
|
||||
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
||||
func (pea *PublicEthAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
||||
receipt, err := pea.localGetTransactionReceipt(ctx, hash)
|
||||
if receipt != nil && err == nil {
|
||||
return receipt, nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
if receipt := pea.remoteGetTransactionReceipt(ctx, hash); receipt != nil {
|
||||
return receipt, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) localGetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
||||
// TODO: this can be optimized for Postgres
|
||||
tx, blockHash, blockNumber, index, err := pea.B.GetTransaction(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tx == nil {
|
||||
return nil, nil
|
||||
}
|
||||
receipts, err := pea.B.GetReceipts(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(receipts) <= int(index) {
|
||||
return nil, nil
|
||||
}
|
||||
receipt := receipts[index]
|
||||
|
||||
var signer types.Signer = types.FrontierSigner{}
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
"blockNumber": hexutil.Uint64(blockNumber),
|
||||
"transactionHash": hash,
|
||||
"transactionIndex": hexutil.Uint64(index),
|
||||
"from": from,
|
||||
"to": tx.To(),
|
||||
"gasUsed": hexutil.Uint64(receipt.GasUsed),
|
||||
"cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
|
||||
"contractAddress": nil,
|
||||
"logs": receipt.Logs,
|
||||
"logsBloom": receipt.Bloom,
|
||||
}
|
||||
|
||||
// Assign receipt status or post state.
|
||||
if len(receipt.PostState) > 0 {
|
||||
fields["root"] = hexutil.Bytes(receipt.PostState)
|
||||
} else {
|
||||
fields["status"] = hexutil.Uint(receipt.Status)
|
||||
}
|
||||
if receipt.Logs == nil {
|
||||
fields["logs"] = []*types.Log{}
|
||||
}
|
||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||
if receipt.ContractAddress != (common.Address{}) {
|
||||
fields["contractAddress"] = receipt.ContractAddress
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) remoteGetTransactionReceipt(ctx context.Context, hash common.Hash) map[string]interface{} {
|
||||
var rct *RPCReceipt
|
||||
if err := pea.rpc.CallContext(ctx, &rct, "eth_getTransactionReceipt", hash); rct != nil && err == nil {
|
||||
return map[string]interface{}{
|
||||
"blockHash": rct.BlockHash,
|
||||
"blockNumber": rct.BlockNumber,
|
||||
"transactionHash": rct.TransactionHash,
|
||||
"transactionIndex": rct.TransactionIndex,
|
||||
"from": rct.From,
|
||||
"to": rct.To,
|
||||
"gasUsed": rct.GasUsed,
|
||||
"cumulativeGasUsed": rct.CumulativeGsUsed,
|
||||
"contractAddress": rct.ContractAddress,
|
||||
"logs": rct.Logs,
|
||||
"logsBloom": rct.Bloom,
|
||||
"root": rct.Root,
|
||||
"status": rct.Status,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLogs returns logs matching the given argument that are stored within the state.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
|
||||
func (pea *PublicEthAPI) GetLogs(ctx context.Context, crit ethereum.FilterQuery) ([]*types.Log, error) {
|
||||
logs, err := pea.getLogs(ctx, crit)
|
||||
logs, err := pea.localGetLogs(ctx, crit)
|
||||
if err != nil && pea.rpc != nil {
|
||||
if arg, err := toFilterArg(crit); err == nil {
|
||||
var result []*types.Log
|
||||
if err := pea.rpc.CallContext(ctx, &result, "eth_getLogs", arg); err == nil {
|
||||
return result, nil
|
||||
var res []*types.Log
|
||||
if err := pea.rpc.CallContext(ctx, &res, "eth_getLogs", arg); err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return logs, err
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) getLogs(ctx context.Context, crit ethereum.FilterQuery) ([]*types.Log, error) {
|
||||
func (pea *PublicEthAPI) localGetLogs(ctx context.Context, crit ethereum.FilterQuery) ([]*types.Log, error) {
|
||||
// TODO: this can be optimized away from using the old cid retriever and ipld fetcher interfaces
|
||||
// Convert FilterQuery into ReceiptFilter
|
||||
addrStrs := make([]string, len(crit.Addresses))
|
||||
for i, addr := range crit.Addresses {
|
||||
@ -175,76 +584,131 @@ func (pea *PublicEthAPI) getLogs(ctx context.Context, crit ethereum.FilterQuery)
|
||||
return logs, err // need to return err variable so that we return the err = tx.Commit() assignment in the defer
|
||||
}
|
||||
|
||||
// GetHeaderByNumber returns the requested canonical block header.
|
||||
// * When blockNr is -1 the chain head is returned.
|
||||
// * We cannot support pending block calls since we do not have an active miner
|
||||
func (pea *PublicEthAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (map[string]interface{}, error) {
|
||||
header, err := pea.B.HeaderByNumber(ctx, number)
|
||||
if header != nil && err == nil {
|
||||
return pea.rpcMarshalHeader(header)
|
||||
}
|
||||
if pea.ethClient != nil {
|
||||
if header, err := pea.ethClient.HeaderByNumber(ctx, big.NewInt(number.Int64())); header != nil && err == nil {
|
||||
return pea.rpcMarshalHeader(header)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
/*
|
||||
|
||||
// GetBlockByNumber returns the requested canonical block.
|
||||
// * When blockNr is -1 the chain head is returned.
|
||||
// * We cannot support pending block calls since we do not have an active miner
|
||||
// * When fullTx is true all transactions in the block are returned, otherwise
|
||||
// only the transaction hash is returned.
|
||||
func (pea *PublicEthAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
|
||||
block, err := pea.B.BlockByNumber(ctx, number)
|
||||
if block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
if pea.ethClient != nil {
|
||||
if block, err := pea.ethClient.BlockByNumber(ctx, big.NewInt(number.Int64())); block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
State and Storage
|
||||
|
||||
// GetBlockByHash 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 (pea *PublicEthAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) {
|
||||
block, err := pea.B.BlockByHash(ctx, hash)
|
||||
if block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
if pea.ethClient != nil {
|
||||
if block, err := pea.ethClient.BlockByHash(ctx, hash); block != nil && err == nil {
|
||||
return pea.rpcMarshalBlock(block, true, fullTx)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
*/
|
||||
|
||||
// GetTransactionByHash returns the transaction for the given hash
|
||||
// eth ipld-eth-server cannot currently handle pending/tx_pool txs
|
||||
func (pea *PublicEthAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||
tx, blockHash, blockNumber, index, err := pea.B.GetTransaction(ctx, hash)
|
||||
if tx != nil && err == nil {
|
||||
return NewRPCTransaction(tx, blockHash, blockNumber, index), nil
|
||||
// GetBalance returns the amount of wei for the given address in the state of the
|
||||
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
|
||||
// block numbers are also allowed.
|
||||
func (pea *PublicEthAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
||||
bal, err := pea.localGetBalance(ctx, address, blockNrOrHash)
|
||||
if bal != nil && err == nil {
|
||||
return bal, nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
if tx, err := pea.remoteGetTransactionByHash(ctx, hash); tx != nil && err == nil {
|
||||
return tx, nil
|
||||
var res *hexutil.Big
|
||||
if err := pea.rpc.CallContext(ctx, &res, "eth_getBalance", address, blockNrOrHash); res != nil && err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) remoteGetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||
var tx *RPCTransaction
|
||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash); err != nil {
|
||||
func (pea *PublicEthAPI) localGetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
||||
account, err := pea.B.GetAccountByNumberOrHash(ctx, address, blockNrOrHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx, nil
|
||||
return (*hexutil.Big)(account.Balance), nil
|
||||
}
|
||||
|
||||
// GetStorageAt returns the storage from the state at the given address, key and
|
||||
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
|
||||
// numbers are also allowed.
|
||||
func (pea *PublicEthAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||
storageVal, err := pea.B.GetStorageByNumberOrHash(ctx, address, common.HexToHash(key), blockNrOrHash)
|
||||
if storageVal != nil && err == nil {
|
||||
return storageVal, nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var res hexutil.Bytes
|
||||
if err := pea.rpc.CallContext(ctx, &res, "eth_getStorageAt", address, key, blockNrOrHash); res != nil && err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetCode returns the code stored at the given address in the state for the given block number.
|
||||
func (pea *PublicEthAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||
code, err := pea.B.GetCodeByNumberOrHash(ctx, address, blockNrOrHash)
|
||||
if code != nil && err == nil {
|
||||
return code, nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var res hexutil.Bytes
|
||||
if err := pea.rpc.CallContext(ctx, &res, "eth_getCode", address, blockNrOrHash); res != nil && err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetProof returns the Merkle-proof for a given account and optionally some storage keys.
|
||||
func (pea *PublicEthAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) {
|
||||
proof, err := pea.localGetProof(ctx, address, storageKeys, blockNrOrHash)
|
||||
if proof != nil && err == nil {
|
||||
return proof, nil
|
||||
}
|
||||
if pea.rpc != nil {
|
||||
var res *AccountResult
|
||||
if err := pea.rpc.CallContext(ctx, &res, "eth_getProof", address, storageKeys, blockNrOrHash); res != nil && err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) localGetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) {
|
||||
state, _, err := pea.B.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
if state == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storageTrie := state.StorageTrie(address)
|
||||
storageHash := types.EmptyRootHash
|
||||
codeHash := state.GetCodeHash(address)
|
||||
storageProof := make([]StorageResult, len(storageKeys))
|
||||
|
||||
// if we have a storageTrie, (which means the account exists), we can update the storagehash
|
||||
if storageTrie != nil {
|
||||
storageHash = storageTrie.Hash()
|
||||
} else {
|
||||
// no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray.
|
||||
codeHash = crypto.Keccak256Hash(nil)
|
||||
}
|
||||
|
||||
// create the proof for the storageKeys
|
||||
for i, key := range storageKeys {
|
||||
if storageTrie != nil {
|
||||
proof, storageError := state.GetStorageProof(address, common.HexToHash(key))
|
||||
if storageError != nil {
|
||||
return nil, storageError
|
||||
}
|
||||
storageProof[i] = StorageResult{key, (*hexutil.Big)(state.GetState(address, common.HexToHash(key)).Big()), common.ToHexArray(proof)}
|
||||
} else {
|
||||
storageProof[i] = StorageResult{key, &hexutil.Big{}, []string{}}
|
||||
}
|
||||
}
|
||||
|
||||
// create the accountProof
|
||||
accountProof, proofErr := state.GetProof(address)
|
||||
if proofErr != nil {
|
||||
return nil, proofErr
|
||||
}
|
||||
|
||||
return &AccountResult{
|
||||
Address: address,
|
||||
AccountProof: common.ToHexArray(accountProof),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(address)),
|
||||
CodeHash: codeHash,
|
||||
Nonce: hexutil.Uint64(state.GetNonce(address)),
|
||||
StorageHash: storageHash,
|
||||
StorageProof: storageProof,
|
||||
}, state.Error()
|
||||
}
|
||||
|
||||
// Call executes the given transaction on the state for the given block number.
|
||||
@ -258,48 +722,17 @@ func (pea *PublicEthAPI) Call(ctx context.Context, args CallArgs, blockNrOrHash
|
||||
if overrides != nil {
|
||||
accounts = *overrides
|
||||
}
|
||||
result, _, failed, err := DoCall(ctx, pea.B, args, blockNrOrHash, accounts, 5*time.Second, pea.B.Config.RPCGasCap)
|
||||
res, _, failed, err := DoCall(ctx, pea.B, args, blockNrOrHash, accounts, 5*time.Second, pea.B.Config.RPCGasCap)
|
||||
if (failed || err != nil) && pea.rpc != nil {
|
||||
if res, err := pea.remoteCall(ctx, args, blockNrOrHash, overrides); res != nil && err == nil {
|
||||
return res, nil
|
||||
var hex hexutil.Bytes
|
||||
if err := pea.rpc.CallContext(ctx, &hex, "eth_call", args, blockNrOrHash, overrides); hex != nil && err == nil {
|
||||
return hex, nil
|
||||
}
|
||||
}
|
||||
if failed && err == nil {
|
||||
return nil, errors.New("eth_call failed without error")
|
||||
}
|
||||
return (hexutil.Bytes)(result), err
|
||||
}
|
||||
|
||||
func (pea *PublicEthAPI) remoteCall(ctx context.Context, msg CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]account) (hexutil.Bytes, error) {
|
||||
var hex hexutil.Bytes
|
||||
if err := pea.rpc.CallContext(ctx, &hex, "eth_call", msg, blockNrOrHash, overrides); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hex, nil
|
||||
}
|
||||
|
||||
// CallArgs represents the arguments for a call.
|
||||
type CallArgs struct {
|
||||
From *common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
}
|
||||
|
||||
// account indicates the overriding fields of account during the execution of
|
||||
// a message call.
|
||||
// Note, state and stateDiff can't be specified at the same time. If state is
|
||||
// set, message execution will only use the data in the given state. Otherwise
|
||||
// if statDiff is set, all diff will be applied first and then execute the call
|
||||
// message.
|
||||
type account struct {
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
Code *hexutil.Bytes `json:"code"`
|
||||
Balance **hexutil.Big `json:"balance"`
|
||||
State *map[common.Hash]common.Hash `json:"state"`
|
||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||
return (hexutil.Bytes)(res), err
|
||||
}
|
||||
|
||||
func DoCall(ctx context.Context, b *Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
|
||||
@ -409,30 +842,32 @@ func DoCall(ctx context.Context, b *Backend, args CallArgs, blockNrOrHash rpc.Bl
|
||||
return res, gas, failed, err
|
||||
}
|
||||
|
||||
func toFilterArg(q ethereum.FilterQuery) (interface{}, error) {
|
||||
arg := map[string]interface{}{
|
||||
"address": q.Addresses,
|
||||
"topics": q.Topics,
|
||||
// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field
|
||||
func (pea *PublicEthAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
||||
fields, err := RPCMarshalBlock(b, inclTx, fullTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if q.BlockHash != nil {
|
||||
arg["blockHash"] = *q.BlockHash
|
||||
if q.FromBlock != nil || q.ToBlock != nil {
|
||||
return nil, fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock")
|
||||
if inclTx {
|
||||
td, err := pea.B.GetTd(b.Hash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if q.FromBlock == nil {
|
||||
arg["fromBlock"] = "0x0"
|
||||
} else {
|
||||
arg["fromBlock"] = toBlockNumArg(q.FromBlock)
|
||||
fields["totalDifficulty"] = (*hexutil.Big)(td)
|
||||
}
|
||||
arg["toBlock"] = toBlockNumArg(q.ToBlock)
|
||||
}
|
||||
return arg, nil
|
||||
return fields, err
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
// rpcMarshalBlockWithUncleHashes uses the generalized output filler, then adds the total difficulty field
|
||||
func (pea *PublicEthAPI) rpcMarshalBlockWithUncleHashes(b *types.Block, uncleHashes []common.Hash, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
||||
fields, err := RPCMarshalBlockWithUncleHashes(b, uncleHashes, inclTx, fullTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hexutil.EncodeBig(number)
|
||||
td, err := pea.B.GetTd(b.Hash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields["totalDifficulty"] = (*hexutil.Big)(td)
|
||||
return fields, err
|
||||
}
|
||||
|
@ -24,12 +24,15 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||
shared2 "github.com/vulcanize/ipld-eth-indexer/pkg/shared"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||
@ -37,6 +40,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
|
||||
randomHash = crypto.Keccak256Hash(randomAddr.Bytes())
|
||||
number = rpc.BlockNumber(test_helpers.BlockNumber.Int64())
|
||||
wrongNumber = rpc.BlockNumber(number + 1)
|
||||
blockHash = test_helpers.MockBlock.Header().Hash()
|
||||
ctx = context.Background()
|
||||
expectedBlock = map[string]interface{}{
|
||||
"number": (*hexutil.Big)(test_helpers.MockBlock.Number()),
|
||||
"hash": test_helpers.MockBlock.Hash(),
|
||||
@ -59,7 +68,7 @@ var (
|
||||
}
|
||||
expectedHeader = map[string]interface{}{
|
||||
"number": (*hexutil.Big)(test_helpers.MockBlock.Header().Number),
|
||||
"hash": test_helpers.MockBlock.Header().Hash(),
|
||||
"hash": blockHash,
|
||||
"parentHash": test_helpers.MockBlock.Header().ParentHash,
|
||||
"nonce": test_helpers.MockBlock.Header().Nonce,
|
||||
"mixHash": test_helpers.MockBlock.Header().MixDigest,
|
||||
@ -77,26 +86,115 @@ var (
|
||||
"receiptsRoot": test_helpers.MockBlock.Header().ReceiptHash,
|
||||
"totalDifficulty": (*hexutil.Big)(test_helpers.MockBlock.Header().Difficulty),
|
||||
}
|
||||
expectedUncle1 = map[string]interface{}{
|
||||
"number": (*hexutil.Big)(test_helpers.MockUncles[0].Number),
|
||||
"hash": test_helpers.MockUncles[0].Hash(),
|
||||
"parentHash": test_helpers.MockUncles[0].ParentHash,
|
||||
"nonce": test_helpers.MockUncles[0].Nonce,
|
||||
"mixHash": test_helpers.MockUncles[0].MixDigest,
|
||||
"sha3Uncles": test_helpers.MockUncles[0].UncleHash,
|
||||
"logsBloom": test_helpers.MockUncles[0].Bloom,
|
||||
"stateRoot": test_helpers.MockUncles[0].Root,
|
||||
"miner": test_helpers.MockUncles[0].Coinbase,
|
||||
"difficulty": (*hexutil.Big)(test_helpers.MockUncles[0].Difficulty),
|
||||
"extraData": hexutil.Bytes(test_helpers.MockUncles[0].Extra),
|
||||
"size": hexutil.Uint64(types.NewBlockWithHeader(test_helpers.MockUncles[0]).Size()),
|
||||
"gasLimit": hexutil.Uint64(test_helpers.MockUncles[0].GasLimit),
|
||||
"gasUsed": hexutil.Uint64(test_helpers.MockUncles[0].GasUsed),
|
||||
"timestamp": hexutil.Uint64(test_helpers.MockUncles[0].Time),
|
||||
"transactionsRoot": test_helpers.MockUncles[0].TxHash,
|
||||
"receiptsRoot": test_helpers.MockUncles[0].ReceiptHash,
|
||||
"uncles": []common.Hash{},
|
||||
}
|
||||
expectedUncle2 = map[string]interface{}{
|
||||
"number": (*hexutil.Big)(test_helpers.MockUncles[1].Number),
|
||||
"hash": test_helpers.MockUncles[1].Hash(),
|
||||
"parentHash": test_helpers.MockUncles[1].ParentHash,
|
||||
"nonce": test_helpers.MockUncles[1].Nonce,
|
||||
"mixHash": test_helpers.MockUncles[1].MixDigest,
|
||||
"sha3Uncles": test_helpers.MockUncles[1].UncleHash,
|
||||
"logsBloom": test_helpers.MockUncles[1].Bloom,
|
||||
"stateRoot": test_helpers.MockUncles[1].Root,
|
||||
"miner": test_helpers.MockUncles[1].Coinbase,
|
||||
"difficulty": (*hexutil.Big)(test_helpers.MockUncles[1].Difficulty),
|
||||
"extraData": hexutil.Bytes(test_helpers.MockUncles[1].Extra),
|
||||
"size": hexutil.Uint64(types.NewBlockWithHeader(test_helpers.MockUncles[1]).Size()),
|
||||
"gasLimit": hexutil.Uint64(test_helpers.MockUncles[1].GasLimit),
|
||||
"gasUsed": hexutil.Uint64(test_helpers.MockUncles[1].GasUsed),
|
||||
"timestamp": hexutil.Uint64(test_helpers.MockUncles[1].Time),
|
||||
"transactionsRoot": test_helpers.MockUncles[1].TxHash,
|
||||
"receiptsRoot": test_helpers.MockUncles[1].ReceiptHash,
|
||||
"uncles": []common.Hash{},
|
||||
}
|
||||
expectedTransaction = eth.NewRPCTransaction(test_helpers.MockTransactions[0], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 0)
|
||||
expectedTransaction2 = eth.NewRPCTransaction(test_helpers.MockTransactions[1], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 1)
|
||||
expectedTransaction3 = eth.NewRPCTransaction(test_helpers.MockTransactions[2], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 2)
|
||||
expectRawTx, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[0])
|
||||
expectRawTx2, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[1])
|
||||
expectRawTx3, _ = rlp.EncodeToBytes(test_helpers.MockTransactions[2])
|
||||
expectedReceipt = map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
"blockNumber": hexutil.Uint64(uint64(number.Int64())),
|
||||
"transactionHash": expectedTransaction.Hash,
|
||||
"transactionIndex": hexutil.Uint64(0),
|
||||
"from": expectedTransaction.From,
|
||||
"to": expectedTransaction.To,
|
||||
"gasUsed": hexutil.Uint64(test_helpers.MockReceipts[0].GasUsed),
|
||||
"cumulativeGasUsed": hexutil.Uint64(test_helpers.MockReceipts[0].CumulativeGasUsed),
|
||||
"contractAddress": nil,
|
||||
"logs": test_helpers.MockReceipts[0].Logs,
|
||||
"logsBloom": test_helpers.MockReceipts[0].Bloom,
|
||||
"root": hexutil.Bytes(test_helpers.MockReceipts[0].PostState),
|
||||
}
|
||||
expectedReceipt2 = map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
"blockNumber": hexutil.Uint64(uint64(number.Int64())),
|
||||
"transactionHash": expectedTransaction2.Hash,
|
||||
"transactionIndex": hexutil.Uint64(1),
|
||||
"from": expectedTransaction2.From,
|
||||
"to": expectedTransaction2.To,
|
||||
"gasUsed": hexutil.Uint64(test_helpers.MockReceipts[1].GasUsed),
|
||||
"cumulativeGasUsed": hexutil.Uint64(test_helpers.MockReceipts[1].CumulativeGasUsed),
|
||||
"contractAddress": nil,
|
||||
"logs": test_helpers.MockReceipts[1].Logs,
|
||||
"logsBloom": test_helpers.MockReceipts[1].Bloom,
|
||||
"root": hexutil.Bytes(test_helpers.MockReceipts[1].PostState),
|
||||
}
|
||||
expectedReceipt3 = map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
"blockNumber": hexutil.Uint64(uint64(number.Int64())),
|
||||
"transactionHash": expectedTransaction3.Hash,
|
||||
"transactionIndex": hexutil.Uint64(2),
|
||||
"from": expectedTransaction3.From,
|
||||
"to": expectedTransaction3.To,
|
||||
"gasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].GasUsed),
|
||||
"cumulativeGasUsed": hexutil.Uint64(test_helpers.MockReceipts[2].CumulativeGasUsed),
|
||||
"contractAddress": nil,
|
||||
"logs": test_helpers.MockReceipts[2].Logs,
|
||||
"logsBloom": test_helpers.MockReceipts[2].Bloom,
|
||||
"root": hexutil.Bytes(test_helpers.MockReceipts[2].PostState),
|
||||
}
|
||||
)
|
||||
|
||||
var _ = Describe("API", func() {
|
||||
var (
|
||||
db *postgres.DB
|
||||
indexAndPublisher *eth2.IPLDPublisher
|
||||
backend *eth.Backend
|
||||
api *eth.PublicEthAPI
|
||||
)
|
||||
BeforeEach(func() {
|
||||
// Test db setup, rather than using BeforeEach we only need to setup once since the tests do not mutate the database
|
||||
// Note: if you focus one of the tests be sure to focus this and the defered It()
|
||||
It("test init", func() {
|
||||
var err error
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
indexAndPublisher = eth2.NewIPLDPublisher(db)
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{})
|
||||
indexAndPublisher := eth2.NewIPLDPublisher(db)
|
||||
backend, err := eth.NewEthBackend(db, ð.Config{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api = eth.NewPublicEthAPI(backend, nil)
|
||||
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = publishCode(db, test_helpers.ContractCodeHash, test_helpers.ContractCode)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
uncles := test_helpers.MockBlock.Uncles()
|
||||
uncleHashes := make([]common.Hash, len(uncles))
|
||||
for i, uncle := range uncles {
|
||||
@ -104,69 +202,22 @@ var _ = Describe("API", func() {
|
||||
}
|
||||
expectedBlock["uncles"] = uncleHashes
|
||||
})
|
||||
AfterEach(func() {
|
||||
eth.TearDownDB(db)
|
||||
})
|
||||
Describe("BlockNumber", func() {
|
||||
It("Retrieves the head block number", func() {
|
||||
bn := api.BlockNumber()
|
||||
ubn := (uint64)(bn)
|
||||
subn := strconv.FormatUint(ubn, 10)
|
||||
Expect(subn).To(Equal(test_helpers.BlockNumber.String()))
|
||||
})
|
||||
})
|
||||
// Single test db tear down at end of all tests
|
||||
defer It("test teardown", func() { eth.TearDownDB(db) })
|
||||
/*
|
||||
|
||||
Describe("GetTransactionByHash", func() {
|
||||
It("Retrieves a transaction by hash", func() {
|
||||
hash := test_helpers.MockTransactions[0].Hash()
|
||||
tx, err := api.GetTransactionByHash(context.Background(), hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tx).To(Equal(expectedTransaction))
|
||||
})
|
||||
})
|
||||
Headers and blocks
|
||||
|
||||
Describe("GetBlockByNumber", func() {
|
||||
It("Retrieves a block by number", func() {
|
||||
// without full txs
|
||||
number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
block, err := api.GetBlockByNumber(context.Background(), rpc.BlockNumber(number), false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transactionHashes := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||
for i, trx := range test_helpers.MockBlock.Transactions() {
|
||||
transactionHashes[i] = trx.Hash()
|
||||
}
|
||||
expectedBlock["transactions"] = transactionHashes
|
||||
for key, val := range expectedBlock {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
// with full txs
|
||||
block, err = api.GetBlockByNumber(context.Background(), rpc.BlockNumber(number), true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transactions := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||
for i, trx := range test_helpers.MockBlock.Transactions() {
|
||||
transactions[i] = eth.NewRPCTransactionFromBlockHash(test_helpers.MockBlock, trx.Hash())
|
||||
}
|
||||
expectedBlock["transactions"] = transactions
|
||||
for key, val := range expectedBlock {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetHeaderByNumber", func() {
|
||||
*/
|
||||
Describe("eth_getHeaderByNumber", func() {
|
||||
It("Retrieves a header by number", func() {
|
||||
number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number))
|
||||
header, err := api.GetHeaderByNumber(ctx, number)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(header).To(Equal(expectedHeader))
|
||||
})
|
||||
|
||||
It("Throws an error if a header cannot be found", func() {
|
||||
number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number+1))
|
||||
header, err := api.GetHeaderByNumber(ctx, wrongNumber)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
Expect(header).To(BeNil())
|
||||
@ -175,10 +226,30 @@ var _ = Describe("API", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetBlockByHash", func() {
|
||||
It("Retrieves a block by hash", func() {
|
||||
// without full txs
|
||||
block, err := api.GetBlockByHash(context.Background(), test_helpers.MockBlock.Hash(), false)
|
||||
Describe("eth_getHeaderByHash", func() {
|
||||
It("Retrieves a header by hash", func() {
|
||||
header := api.GetHeaderByHash(ctx, blockHash)
|
||||
Expect(header).To(Equal(expectedHeader))
|
||||
})
|
||||
|
||||
It("Throws an error if a header cannot be found", func() {
|
||||
header := api.GetHeaderByHash(ctx, randomHash)
|
||||
Expect(header).To(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_blockNumber", func() {
|
||||
It("Retrieves the head block number", func() {
|
||||
bn := api.BlockNumber()
|
||||
ubn := (uint64)(bn)
|
||||
subn := strconv.FormatUint(ubn, 10)
|
||||
Expect(subn).To(Equal(test_helpers.BlockNumber.String()))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getBlockByNumber", func() {
|
||||
It("Retrieves a block by number, without full txs", func() {
|
||||
block, err := api.GetBlockByNumber(ctx, number, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transactionHashes := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||
for i, trx := range test_helpers.MockBlock.Transactions() {
|
||||
@ -188,8 +259,9 @@ var _ = Describe("API", func() {
|
||||
for key, val := range expectedBlock {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
// with full txs
|
||||
block, err = api.GetBlockByHash(context.Background(), test_helpers.MockBlock.Hash(), true)
|
||||
})
|
||||
It("Retrieves a block by number, with full txs", func() {
|
||||
block, err := api.GetBlockByNumber(ctx, number, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transactions := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||
for i, trx := range test_helpers.MockBlock.Transactions() {
|
||||
@ -200,9 +272,288 @@ var _ = Describe("API", func() {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
})
|
||||
It("Throws an error if a block cannot be found", func() {
|
||||
_, err := api.GetBlockByNumber(ctx, wrongNumber, false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetLogs", func() {
|
||||
Describe("eth_getBlockByHash", func() {
|
||||
It("Retrieves a block by hash, without full txs", func() {
|
||||
block, err := api.GetBlockByHash(ctx, test_helpers.MockBlock.Hash(), false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transactionHashes := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||
for i, trx := range test_helpers.MockBlock.Transactions() {
|
||||
transactionHashes[i] = trx.Hash()
|
||||
}
|
||||
expectedBlock["transactions"] = transactionHashes
|
||||
for key, val := range expectedBlock {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
})
|
||||
It("Retrieves a block by hash, with full txs", func() {
|
||||
block, err := api.GetBlockByHash(ctx, test_helpers.MockBlock.Hash(), true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transactions := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||
for i, trx := range test_helpers.MockBlock.Transactions() {
|
||||
transactions[i] = eth.NewRPCTransactionFromBlockHash(test_helpers.MockBlock, trx.Hash())
|
||||
}
|
||||
expectedBlock["transactions"] = transactions
|
||||
for key, val := range expectedBlock {
|
||||
Expect(val).To(Equal(block[key]))
|
||||
}
|
||||
})
|
||||
It("Throws an error if a block cannot be found", func() {
|
||||
_, err := api.GetBlockByHash(ctx, randomHash, false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
Uncles
|
||||
|
||||
*/
|
||||
|
||||
Describe("eth_getUncleByBlockNumberAndIndex", func() {
|
||||
It("Retrieves the uncle at the provided index in the canoncial block with the provided hash", func() {
|
||||
uncle1, err := api.GetUncleByBlockNumberAndIndex(ctx, number, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle1).To(Equal(expectedUncle1))
|
||||
uncle2, err := api.GetUncleByBlockNumberAndIndex(ctx, number, 1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle2).To(Equal(expectedUncle2))
|
||||
})
|
||||
It("Throws an error if an block for blocknumber cannot be found", func() {
|
||||
_, err := api.GetUncleByBlockNumberAndIndex(ctx, wrongNumber, 0)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
It("Returns `nil` if an uncle at the provided index does not exist for the block found for the provided block number", func() {
|
||||
uncle, err := api.GetUncleByBlockNumberAndIndex(ctx, number, 2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle).To(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getUncleByBlockHashAndIndex", func() {
|
||||
It("Retrieves the uncle at the provided index in the block with the provided hash", func() {
|
||||
uncle1, err := api.GetUncleByBlockHashAndIndex(ctx, blockHash, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle1).To(Equal(expectedUncle1))
|
||||
uncle2, err := api.GetUncleByBlockHashAndIndex(ctx, blockHash, 1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle2).To(Equal(expectedUncle2))
|
||||
})
|
||||
It("Throws an error if an block for blockhash cannot be found", func() {
|
||||
_, err := api.GetUncleByBlockHashAndIndex(ctx, randomHash, 0)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
It("Returns `nil` if an uncle at the provided index does not exist for the block with the provided hash", func() {
|
||||
uncle, err := api.GetUncleByBlockHashAndIndex(ctx, blockHash, 2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(uncle).To(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getUncleCountByBlockNumber", func() {
|
||||
It("Retrieves the number of uncles for the canonical block with the provided number", func() {
|
||||
count := api.GetUncleCountByBlockNumber(ctx, number)
|
||||
Expect(uint64(*count)).To(Equal(uint64(2)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getUncleCountByBlockHash", func() {
|
||||
It("Retrieves the number of uncles for the block with the provided hash", func() {
|
||||
count := api.GetUncleCountByBlockHash(ctx, blockHash)
|
||||
Expect(uint64(*count)).To(Equal(uint64(2)))
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
Transactions
|
||||
|
||||
*/
|
||||
|
||||
Describe("eth_getTransactionCount", func() {
|
||||
It("Retrieves the number of transactions the given address has sent for the given block number", func() {
|
||||
count, err := api.GetTransactionCount(ctx, test_helpers.ContractAddress, rpc.BlockNumberOrHashWithNumber(number))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(*count).To(Equal(hexutil.Uint64(1)))
|
||||
|
||||
count, err = api.GetTransactionCount(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithNumber(number))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(*count).To(Equal(hexutil.Uint64(0)))
|
||||
})
|
||||
It("Retrieves the number of transactions the given address has sent for the given block hash", func() {
|
||||
count, err := api.GetTransactionCount(ctx, test_helpers.ContractAddress, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(*count).To(Equal(hexutil.Uint64(1)))
|
||||
|
||||
count, err = api.GetTransactionCount(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(*count).To(Equal(hexutil.Uint64(0)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getBlockTransactionCountByNumber", func() {
|
||||
It("Retrieves the number of transactions in the canonical block with the provided number", func() {
|
||||
count := api.GetBlockTransactionCountByNumber(ctx, number)
|
||||
Expect(uint64(*count)).To(Equal(uint64(3)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getBlockTransactionCountByHash", func() {
|
||||
It("Retrieves the number of transactions in the block with the provided hash ", func() {
|
||||
count := api.GetBlockTransactionCountByHash(ctx, blockHash)
|
||||
Expect(uint64(*count)).To(Equal(uint64(3)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getTransactionByBlockNumberAndIndex", func() {
|
||||
It("Retrieves the tx with the provided index in the canonical block with the provided block number", func() {
|
||||
tx := api.GetTransactionByBlockNumberAndIndex(ctx, number, 0)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(expectedTransaction))
|
||||
|
||||
tx = api.GetTransactionByBlockNumberAndIndex(ctx, number, 1)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(expectedTransaction2))
|
||||
|
||||
tx = api.GetTransactionByBlockNumberAndIndex(ctx, number, 2)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(expectedTransaction3))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getTransactionByBlockHashAndIndex", func() {
|
||||
It("Retrieves the tx with the provided index in the block with the provided hash", func() {
|
||||
tx := api.GetTransactionByBlockHashAndIndex(ctx, blockHash, 0)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(expectedTransaction))
|
||||
|
||||
tx = api.GetTransactionByBlockHashAndIndex(ctx, blockHash, 1)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(expectedTransaction2))
|
||||
|
||||
tx = api.GetTransactionByBlockHashAndIndex(ctx, blockHash, 2)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(expectedTransaction3))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getRawTransactionByBlockNumberAndIndex", func() {
|
||||
It("Retrieves the raw tx with the provided index in the canonical block with the provided block number", func() {
|
||||
tx := api.GetRawTransactionByBlockNumberAndIndex(ctx, number, 0)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx)))
|
||||
|
||||
tx = api.GetRawTransactionByBlockNumberAndIndex(ctx, number, 1)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx2)))
|
||||
|
||||
tx = api.GetRawTransactionByBlockNumberAndIndex(ctx, number, 2)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx3)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getRawTransactionByBlockHashAndIndex", func() {
|
||||
It("Retrieves the raw tx with the provided index in the block with the provided hash", func() {
|
||||
tx := api.GetRawTransactionByBlockHashAndIndex(ctx, blockHash, 0)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx)))
|
||||
|
||||
tx = api.GetRawTransactionByBlockHashAndIndex(ctx, blockHash, 1)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx2)))
|
||||
|
||||
tx = api.GetRawTransactionByBlockHashAndIndex(ctx, blockHash, 2)
|
||||
Expect(tx).ToNot(BeNil())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx3)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getTransactionByHash", func() {
|
||||
It("Retrieves a transaction by hash", func() {
|
||||
hash := test_helpers.MockTransactions[0].Hash()
|
||||
tx, err := api.GetTransactionByHash(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tx).To(Equal(expectedTransaction))
|
||||
|
||||
hash = test_helpers.MockTransactions[1].Hash()
|
||||
tx, err = api.GetTransactionByHash(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tx).To(Equal(expectedTransaction2))
|
||||
|
||||
hash = test_helpers.MockTransactions[2].Hash()
|
||||
tx, err = api.GetTransactionByHash(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tx).To(Equal(expectedTransaction3))
|
||||
})
|
||||
It("Throws an error if it cannot find a tx for the provided tx hash", func() {
|
||||
_, err := api.GetTransactionByHash(ctx, randomHash)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getRawTransactionByHash", func() {
|
||||
It("Retrieves a raw transaction by hash", func() {
|
||||
hash := test_helpers.MockTransactions[0].Hash()
|
||||
tx, err := api.GetRawTransactionByHash(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx)))
|
||||
|
||||
hash = test_helpers.MockTransactions[1].Hash()
|
||||
tx, err = api.GetRawTransactionByHash(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx2)))
|
||||
|
||||
hash = test_helpers.MockTransactions[2].Hash()
|
||||
tx, err = api.GetRawTransactionByHash(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tx).To(Equal(hexutil.Bytes(expectRawTx3)))
|
||||
})
|
||||
It("Throws an error if it cannot find a tx for the provided tx hash", func() {
|
||||
_, err := api.GetRawTransactionByHash(ctx, randomHash)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
Receipts and logs
|
||||
|
||||
*/
|
||||
|
||||
Describe("eth_getTransactionReceipt", func() {
|
||||
It("Retrieves a receipt by tx hash", func() {
|
||||
hash := test_helpers.MockTransactions[0].Hash()
|
||||
rct, err := api.GetTransactionReceipt(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(rct).To(Equal(expectedReceipt))
|
||||
|
||||
hash = test_helpers.MockTransactions[1].Hash()
|
||||
rct, err = api.GetTransactionReceipt(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(rct).To(Equal(expectedReceipt2))
|
||||
|
||||
hash = test_helpers.MockTransactions[2].Hash()
|
||||
rct, err = api.GetTransactionReceipt(ctx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(rct).To(Equal(expectedReceipt3))
|
||||
})
|
||||
It("Throws an error if it cannot find a receipt for the provided tx hash", func() {
|
||||
_, err := api.GetTransactionReceipt(ctx, randomHash)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getLogs", func() {
|
||||
It("Retrieves receipt logs that match the provided topics within the provided range", func() {
|
||||
crit := ethereum.FilterQuery{
|
||||
Topics: [][]common.Hash{
|
||||
@ -213,7 +564,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err := api.GetLogs(context.Background(), crit)
|
||||
logs, err := api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||
@ -228,7 +579,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
@ -243,7 +594,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||
@ -260,7 +611,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(0))
|
||||
|
||||
@ -276,7 +627,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||
@ -293,7 +644,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||
@ -311,7 +662,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||
@ -330,7 +681,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
@ -345,7 +696,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||
@ -360,7 +711,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||
@ -370,7 +721,7 @@ var _ = Describe("API", func() {
|
||||
FromBlock: test_helpers.MockBlock.Number(),
|
||||
ToBlock: test_helpers.MockBlock.Number(),
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
@ -387,7 +738,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err := api.GetLogs(context.Background(), crit)
|
||||
logs, err := api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||
@ -403,7 +754,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||
@ -417,7 +768,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||
@ -433,7 +784,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||
@ -449,7 +800,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(0))
|
||||
|
||||
@ -465,7 +816,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||
@ -479,7 +830,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
@ -497,7 +848,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
@ -506,7 +857,7 @@ var _ = Describe("API", func() {
|
||||
BlockHash: &hash,
|
||||
Topics: [][]common.Hash{},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
@ -530,7 +881,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err := api.GetLogs(context.Background(), crit)
|
||||
logs, err := api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(1))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||
@ -553,7 +904,7 @@ var _ = Describe("API", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
@ -566,10 +917,75 @@ var _ = Describe("API", func() {
|
||||
test_helpers.AnotherAddress,
|
||||
},
|
||||
}
|
||||
logs, err = api.GetLogs(context.Background(), crit)
|
||||
logs, err = api.GetLogs(ctx, crit)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(2))
|
||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
State and storage
|
||||
|
||||
*/
|
||||
|
||||
Describe("eth_getBalance", func() {
|
||||
It("Retrieves the eth balance for the provided account address at the block with the provided number", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithNumber(number))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(test_helpers.AccountBalance)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddress, rpc.BlockNumberOrHashWithNumber(number))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
})
|
||||
It("Retrieves the eth balance for the provided account address at the block with the provided hash", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.AccountAddresss, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(test_helpers.AccountBalance)))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddress, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the balance for", func() {
|
||||
_, err := api.GetBalance(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getCode", func() {
|
||||
It("Retrieves the code for the provided contract address at the block with the provided number", func() {
|
||||
code, err := api.GetCode(ctx, test_helpers.ContractAddress, rpc.BlockNumberOrHashWithNumber(number))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
})
|
||||
It("Retrieves the code for the provided contract address at the block with the provided hash", func() {
|
||||
code, err := api.GetCode(ctx, test_helpers.ContractAddress, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the code for", func() {
|
||||
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blockHash, true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func publishCode(db *postgres.DB, codeHash common.Hash, code []byte) error {
|
||||
tx, err := db.Beginx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mhKey, err := shared2.MultihashKeyFromKeccak256(codeHash)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := shared2.PublishDirect(tx, mhKey, code); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
@ -19,27 +19,31 @@ package eth
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"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"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
ipfsethdb "github.com/vulcanize/pg-ipfs-ethdb"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||
shared2 "github.com/vulcanize/ipld-eth-indexer/pkg/shared"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||
)
|
||||
|
||||
@ -61,6 +65,17 @@ const (
|
||||
WHERE blocks.key = transaction_cids.mh_key
|
||||
AND transaction_cids.header_id = header_cids.id
|
||||
AND transaction_cids.tx_hash = $1`
|
||||
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
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = $2)
|
||||
AND header_cids.id = (SELECT canonical_header(block_number))
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveCodeByMhKey = `SELECT data FROM public.blocks WHERE key = $1`
|
||||
)
|
||||
|
||||
type Backend struct {
|
||||
@ -167,18 +182,6 @@ func (b *Backend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Bl
|
||||
return nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||
}
|
||||
|
||||
// rpcMarshalHeader uses the generalized output filler, then adds the total difficulty field, which requires
|
||||
// a `PublicEthAPI`.
|
||||
func (pea *PublicEthAPI) rpcMarshalHeader(header *types.Header) (map[string]interface{}, error) {
|
||||
fields := RPCMarshalHeader(header)
|
||||
td, err := pea.B.GetTd(header.Hash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields["totalDifficulty"] = (*hexutil.Big)(td)
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// GetTd gets the total difficulty at the given block hash
|
||||
func (b *Backend) GetTd(blockHash common.Hash) (*big.Int, error) {
|
||||
var tdStr string
|
||||
@ -431,7 +434,7 @@ func (b *Backend) GetReceipts(ctx context.Context, hash common.Hash) (types.Rece
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rcts := make(types.Receipts, 0, len(receiptBytes))
|
||||
rcts := make(types.Receipts, len(receiptBytes))
|
||||
for i, rctBytes := range receiptBytes {
|
||||
rct := new(types.Receipt)
|
||||
if err := rlp.DecodeBytes(rctBytes, rct); err != nil {
|
||||
@ -459,21 +462,6 @@ func (b *Backend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires
|
||||
// a `PublicBlockchainAPI`.
|
||||
func (pea *PublicEthAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
||||
fields, err := RPCMarshalBlock(b, inclTx, fullTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
td, err := pea.B.GetTd(b.Hash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields["totalDifficulty"] = (*hexutil.Big)(td)
|
||||
return fields, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@ -541,6 +529,113 @@ func (b *Backend) GetEVM(ctx context.Context, msg core.Message, state *state.Sta
|
||||
return vm.NewEVM(c, state, b.Config.ChainConfig, b.Config.VmConfig), nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
hash := b.GetCanonicalHash(number)
|
||||
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) {
|
||||
hash := b.GetCanonicalHash(number)
|
||||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
hash := b.GetCanonicalHash(number)
|
||||
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
|
||||
}
|
||||
|
||||
// Engine satisfied the ChainContext interface
|
||||
func (b *Backend) Engine() consensus.Engine {
|
||||
// TODO: we need to support more than just ethash based engines
|
||||
|
@ -17,12 +17,19 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
||||
)
|
||||
|
||||
@ -86,6 +93,35 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (map[string]i
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// RPCMarshalBlockWithUncleHashes marshals the block with the provided uncle hashes
|
||||
func RPCMarshalBlockWithUncleHashes(block *types.Block, uncleHashes []common.Hash, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
||||
fields := RPCMarshalHeader(block.Header())
|
||||
fields["size"] = hexutil.Uint64(block.Size())
|
||||
|
||||
if inclTx {
|
||||
formatTx := func(tx *types.Transaction) (interface{}, error) {
|
||||
return tx.Hash(), nil
|
||||
}
|
||||
if fullTx {
|
||||
formatTx = func(tx *types.Transaction) (interface{}, error) {
|
||||
return NewRPCTransactionFromBlockHash(block, tx.Hash()), nil
|
||||
}
|
||||
}
|
||||
txs := block.Transactions()
|
||||
transactions := make([]interface{}, len(txs))
|
||||
var err error
|
||||
for i, tx := range txs {
|
||||
if transactions[i], err = formatTx(tx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
fields["transactions"] = transactions
|
||||
}
|
||||
fields["uncles"] = uncleHashes
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// NewRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
|
||||
func NewRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction {
|
||||
for idx, tx := range b.Transactions() {
|
||||
@ -127,6 +163,77 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
||||
return result
|
||||
}
|
||||
|
||||
type rpcBlock struct {
|
||||
Hash common.Hash `json:"hash"`
|
||||
Transactions []rpcTransaction `json:"transactions"`
|
||||
UncleHashes []common.Hash `json:"uncles"`
|
||||
}
|
||||
|
||||
type rpcTransaction struct {
|
||||
tx *types.Transaction
|
||||
txExtraInfo
|
||||
}
|
||||
|
||||
type txExtraInfo struct {
|
||||
BlockNumber *string `json:"blockNumber,omitempty"`
|
||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||
From *common.Address `json:"from,omitempty"`
|
||||
}
|
||||
|
||||
func (tx *rpcTransaction) UnmarshalJSON(msg []byte) error {
|
||||
if err := json.Unmarshal(msg, &tx.tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(msg, &tx.txExtraInfo)
|
||||
}
|
||||
|
||||
func getBlockAndUncleHashes(cli *rpc.Client, ctx context.Context, method string, args ...interface{}) (*types.Block, []common.Hash, error) {
|
||||
var raw json.RawMessage
|
||||
err := cli.CallContext(ctx, &raw, method, args...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
} else if len(raw) == 0 {
|
||||
return nil, nil, ethereum.NotFound
|
||||
}
|
||||
// Decode header and transactions.
|
||||
var head *types.Header
|
||||
var body rpcBlock
|
||||
if err := json.Unmarshal(raw, &head); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := json.Unmarshal(raw, &body); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server.
|
||||
if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 {
|
||||
return nil, nil, fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles")
|
||||
}
|
||||
if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 {
|
||||
return nil, nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles")
|
||||
}
|
||||
if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 {
|
||||
return nil, nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions")
|
||||
}
|
||||
if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 {
|
||||
return nil, nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions")
|
||||
}
|
||||
txs := make([]*types.Transaction, len(body.Transactions))
|
||||
for i, tx := range body.Transactions {
|
||||
txs[i] = tx.tx
|
||||
}
|
||||
return types.NewBlockWithHeader(head).WithBody(txs, nil), body.UncleHashes, nil
|
||||
}
|
||||
|
||||
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
|
||||
func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.Bytes {
|
||||
txs := b.Transactions()
|
||||
if index >= uint64(len(txs)) {
|
||||
return nil
|
||||
}
|
||||
blob, _ := rlp.EncodeToBytes(txs[index])
|
||||
return blob
|
||||
}
|
||||
|
||||
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
|
||||
func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction {
|
||||
txs := b.Transactions()
|
||||
@ -183,3 +290,31 @@ func sliceContainsHash(slice []string, hash common.Hash) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func toFilterArg(q ethereum.FilterQuery) (interface{}, error) {
|
||||
arg := map[string]interface{}{
|
||||
"address": q.Addresses,
|
||||
"topics": q.Topics,
|
||||
}
|
||||
if q.BlockHash != nil {
|
||||
arg["blockHash"] = *q.BlockHash
|
||||
if q.FromBlock != nil || q.ToBlock != nil {
|
||||
return nil, fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock")
|
||||
}
|
||||
} else {
|
||||
if q.FromBlock == nil {
|
||||
arg["fromBlock"] = "0x0"
|
||||
} else {
|
||||
arg["fromBlock"] = toBlockNumArg(q.FromBlock)
|
||||
}
|
||||
arg["toBlock"] = toBlockNumArg(q.ToBlock)
|
||||
}
|
||||
return arg, nil
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
}
|
||||
return hexutil.EncodeBig(number)
|
||||
}
|
||||
|
@ -1,165 +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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package eth_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
parsedABI abi.ABI
|
||||
)
|
||||
|
||||
func init() {
|
||||
// load abi
|
||||
abiBytes, err := ioutil.ReadFile("./test_helpers/abi.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
parsedABI, err = abi.JSON(bytes.NewReader(abiBytes))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("eth_call", func() {
|
||||
var (
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
chain *core.BlockChain
|
||||
db *postgres.DB
|
||||
transformer *eth2.StateDiffTransformer
|
||||
backend *eth.Backend
|
||||
api *eth.PublicEthAPI
|
||||
builder statediff.Builder
|
||||
pams statediff.Params
|
||||
chainConfig = params.TestChainConfig
|
||||
mockTD = big.NewInt(1337)
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
// db and type initializations
|
||||
var err error
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transformer = eth2.NewStateDiffTransformer(chainConfig, db)
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VmConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000),
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api = eth.NewPublicEthAPI(backend, nil)
|
||||
|
||||
// make the test blockchain (and state)
|
||||
blocks, receipts, chain = test_helpers.MakeChain(4, test_helpers.Genesis, test_helpers.TestChainGen)
|
||||
pams = statediff.Params{
|
||||
IntermediateStateNodes: true,
|
||||
IntermediateStorageNodes: true,
|
||||
}
|
||||
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
|
||||
builder = statediff.NewBuilder(chain.StateCache())
|
||||
for i, block := range blocks {
|
||||
var args statediff.Args
|
||||
var rcts types.Receipts
|
||||
if i == 0 {
|
||||
args = statediff.Args{
|
||||
OldStateRoot: common.Hash{},
|
||||
NewStateRoot: block.Root(),
|
||||
BlockNumber: block.Number(),
|
||||
BlockHash: block.Hash(),
|
||||
}
|
||||
} else {
|
||||
args = statediff.Args{
|
||||
OldStateRoot: blocks[i-1].Root(),
|
||||
NewStateRoot: block.Root(),
|
||||
BlockNumber: block.Number(),
|
||||
BlockHash: block.Hash(),
|
||||
}
|
||||
rcts = receipts[i-1]
|
||||
}
|
||||
diff, err := builder.BuildStateDiffObject(args, pams)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
diffRlp, err := rlp.EncodeToBytes(diff)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
blockRlp, err := rlp.EncodeToBytes(block)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
receiptsRlp, err := rlp.EncodeToBytes(rcts)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
payload := statediff.Payload{
|
||||
StateObjectRlp: diffRlp,
|
||||
BlockRlp: blockRlp,
|
||||
ReceiptsRlp: receiptsRlp,
|
||||
TotalDifficulty: mockTD,
|
||||
}
|
||||
_, err = transformer.Transform(0, payload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
})
|
||||
AfterEach(func() {
|
||||
eth.TearDownDB(db)
|
||||
chain.Stop()
|
||||
})
|
||||
Describe("eth_call", func() {
|
||||
It("Applies call args (tx data) on top of state, returning the result (e.g. a Getter method call)", func() {
|
||||
data, err := parsedABI.Pack("data")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
bdata := hexutil.Bytes(data)
|
||||
callArgs := eth.CallArgs{
|
||||
To: &test_helpers.ContractAddr,
|
||||
Data: &bdata,
|
||||
}
|
||||
res, err := api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(2), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes := hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||
Expect(res).To(Equal(expectedRes))
|
||||
|
||||
res, err = api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(3), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
||||
Expect(res).To(Equal(expectedRes))
|
||||
|
||||
res, err = api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(4), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000009"))
|
||||
Expect(res).To(Equal(expectedRes))
|
||||
})
|
||||
})
|
||||
})
|
426
pkg/eth/eth_state_test.go
Normal file
426
pkg/eth/eth_state_test.go
Normal file
@ -0,0 +1,426 @@
|
||||
// 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_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
parsedABI abi.ABI
|
||||
)
|
||||
|
||||
func init() {
|
||||
// load abi
|
||||
abiBytes, err := ioutil.ReadFile("./test_helpers/abi.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
parsedABI, err = abi.JSON(bytes.NewReader(abiBytes))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("eth state reading tests", func() {
|
||||
var (
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
chain *core.BlockChain
|
||||
db *postgres.DB
|
||||
api *eth.PublicEthAPI
|
||||
backend *eth.Backend
|
||||
chainConfig = params.TestChainConfig
|
||||
mockTD = big.NewInt(1337)
|
||||
)
|
||||
It("test init", func() {
|
||||
// db and type initializations
|
||||
var err error
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transformer := eth2.NewStateDiffTransformer(chainConfig, db)
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VmConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000),
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api = eth.NewPublicEthAPI(backend, nil)
|
||||
|
||||
// make the test blockchain (and state)
|
||||
blocks, receipts, chain = test_helpers.MakeChain(5, test_helpers.Genesis, test_helpers.TestChainGen)
|
||||
params := statediff.Params{
|
||||
IntermediateStateNodes: true,
|
||||
IntermediateStorageNodes: true,
|
||||
}
|
||||
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
|
||||
builder := statediff.NewBuilder(chain.StateCache())
|
||||
for i, block := range blocks {
|
||||
var args statediff.Args
|
||||
var rcts types.Receipts
|
||||
if i == 0 {
|
||||
args = statediff.Args{
|
||||
OldStateRoot: common.Hash{},
|
||||
NewStateRoot: block.Root(),
|
||||
BlockNumber: block.Number(),
|
||||
BlockHash: block.Hash(),
|
||||
}
|
||||
} else {
|
||||
args = statediff.Args{
|
||||
OldStateRoot: blocks[i-1].Root(),
|
||||
NewStateRoot: block.Root(),
|
||||
BlockNumber: block.Number(),
|
||||
BlockHash: block.Hash(),
|
||||
}
|
||||
rcts = receipts[i-1]
|
||||
}
|
||||
diff, err := builder.BuildStateDiffObject(args, params)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
diffRlp, err := rlp.EncodeToBytes(diff)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
blockRlp, err := rlp.EncodeToBytes(block)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
receiptsRlp, err := rlp.EncodeToBytes(rcts)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
payload := statediff.Payload{
|
||||
StateObjectRlp: diffRlp,
|
||||
BlockRlp: blockRlp,
|
||||
ReceiptsRlp: receiptsRlp,
|
||||
TotalDifficulty: mockTD,
|
||||
}
|
||||
_, err = transformer.Transform(0, payload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
})
|
||||
defer It("test teardown", func() {
|
||||
eth.TearDownDB(db)
|
||||
chain.Stop()
|
||||
})
|
||||
|
||||
Describe("eth_call", func() {
|
||||
It("Applies call args (tx data) on top of state, returning the result (e.g. a Getter method call)", func() {
|
||||
data, err := parsedABI.Pack("data")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
bdata := hexutil.Bytes(data)
|
||||
callArgs := eth.CallArgs{
|
||||
To: &test_helpers.ContractAddr,
|
||||
Data: &bdata,
|
||||
}
|
||||
// Before contract deployment, returns nil
|
||||
res, err := api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(0), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).To(BeNil())
|
||||
|
||||
res, err = api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(1), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).To(BeNil())
|
||||
|
||||
// After deployment
|
||||
res, err = api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(2), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes := hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||
Expect(res).To(Equal(expectedRes))
|
||||
|
||||
res, err = api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(3), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
||||
Expect(res).To(Equal(expectedRes))
|
||||
|
||||
res, err = api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(4), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000009"))
|
||||
Expect(res).To(Equal(expectedRes))
|
||||
|
||||
res, err = api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(5), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"))
|
||||
Expect(res).To(Equal(expectedRes))
|
||||
})
|
||||
})
|
||||
|
||||
var (
|
||||
expectedContractBalance = (*hexutil.Big)(common.Big0)
|
||||
expectedBankBalanceBlock0 = (*hexutil.Big)(test_helpers.TestBankFunds)
|
||||
|
||||
expectedAcct1BalanceBlock1 = (*hexutil.Big)(big.NewInt(10000))
|
||||
expectedBankBalanceBlock1 = (*hexutil.Big)(new(big.Int).Sub(test_helpers.TestBankFunds, big.NewInt(10000)))
|
||||
|
||||
expectedAcct2BalanceBlock2 = (*hexutil.Big)(big.NewInt(1000))
|
||||
expectedBankBalanceBlock2 = (*hexutil.Big)(new(big.Int).Sub(expectedBankBalanceBlock1.ToInt(), big.NewInt(1000)))
|
||||
|
||||
expectedAcct2BalanceBlock3 = (*hexutil.Big)(new(big.Int).Add(expectedAcct2BalanceBlock2.ToInt(), test_helpers.MiningReward))
|
||||
|
||||
expectedAcct2BalanceBlock4 = (*hexutil.Big)(new(big.Int).Add(expectedAcct2BalanceBlock3.ToInt(), test_helpers.MiningReward))
|
||||
|
||||
expectedAcct1BalanceBlock5 = (*hexutil.Big)(new(big.Int).Add(expectedAcct1BalanceBlock1.ToInt(), test_helpers.MiningReward))
|
||||
)
|
||||
|
||||
Describe("eth_getBalance", func() {
|
||||
It("Retrieves the eth balance for the provided account address at the block with the provided number", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock0))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
})
|
||||
It("Retrieves the eth balance for the provided account address at the block with the provided hash", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock0))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[1].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock1))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock2))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[2].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock3))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock1))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[4].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct1BalanceBlock5))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedAcct2BalanceBlock4))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedContractBalance))
|
||||
bal, err = api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the balance for an account at the provided block number", func() {
|
||||
_, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the balance for an account at the provided block hash", func() {
|
||||
_, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.Account2Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
_, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getCode", func() {
|
||||
It("Retrieves the code for the provided contract address at the block with the provided number", func() {
|
||||
code, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
|
||||
code, err = api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
})
|
||||
It("Retrieves the code for the provided contract address at the block with the provided hash", func() {
|
||||
code, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
|
||||
code, err = api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[5].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
})
|
||||
It("Throws an error for an account it cannot find the code for", func() {
|
||||
_, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("Throws an error for a contract that doesn't exist at this hieght", func() {
|
||||
_, err := api.GetCode(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth_getStorageAt", func() {
|
||||
It("Throws an error if it tries to access a contract which does not exist", func() {
|
||||
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
|
||||
_, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
It("Throws an error if it tries to access a contract slot which does not exist", func() {
|
||||
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||
})
|
||||
It("Retrieves the storage value at the provided contract address and storage leaf key at the block with the provided hash or number", func() {
|
||||
// After deployment
|
||||
val, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes := hexutil.Bytes(common.Hex2Bytes("01"))
|
||||
Expect(val).To(Equal(expectedRes))
|
||||
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("03"))
|
||||
Expect(val).To(Equal(expectedRes))
|
||||
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectedRes = hexutil.Bytes(common.Hex2Bytes("09"))
|
||||
Expect(val).To(Equal(expectedRes))
|
||||
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(val).To(Equal(hexutil.Bytes{}))
|
||||
})
|
||||
})
|
||||
})
|
@ -28,71 +28,132 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
RetrieveHeadersByHashesPgStr = `SELECT cid, data FROM eth.header_cids
|
||||
RetrieveHeadersByHashesPgStr = `SELECT cid, data
|
||||
FROM eth.header_cids
|
||||
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
||||
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
||||
RetrieveHeadersByBlockNumberPgStr = `SELECT cid, data FROM eth.header_cids
|
||||
RetrieveHeadersByBlockNumberPgStr = `SELECT cid, data
|
||||
FROM eth.header_cids
|
||||
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
||||
WHERE block_number = $1`
|
||||
RetrieveHeaderByHashPgStr = `SELECT cid, data FROM eth.header_cids
|
||||
RetrieveHeaderByHashPgStr = `SELECT cid, data
|
||||
FROM eth.header_cids
|
||||
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
||||
WHERE block_hash = $1`
|
||||
RetrieveUnclesByHashesPgStr = `SELECT cid, data FROM eth.uncle_cids
|
||||
RetrieveUnclesByHashesPgStr = `SELECT cid, data
|
||||
FROM eth.uncle_cids
|
||||
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
||||
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
||||
RetrieveUnclesByBlockHashPgStr = `SELECT cid, data FROM eth.uncle_cids, eth.header_cids, public.blocks
|
||||
WHERE uncle_cids.header_id = header_cids.id
|
||||
AND uncle_cids.mh_key = blocks.key
|
||||
AND block_hash = $1`
|
||||
RetrieveUnclesByBlockNumberPgStr = `SELECT cid, data FROM eth.uncle_cids, eth.header_cids, public.blocks
|
||||
WHERE uncle_cids.header_id = header_cids.id
|
||||
AND uncle_cids.mh_key = blocks.key
|
||||
AND block_number = $1`
|
||||
RetrieveUncleByHashPgStr = `SELECT cid, data FROM eth.uncle_cids
|
||||
RetrieveUnclesByBlockHashPgStr = `SELECT uncle_cids.cid, data
|
||||
FROM eth.uncle_cids
|
||||
INNER JOIN eth.header_cids ON (uncle_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
||||
WHERE block_hash = $1`
|
||||
RetrieveTransactionsByHashesPgStr = `SELECT cid, data FROM eth.transaction_cids
|
||||
RetrieveUnclesByBlockNumberPgStr = `SELECT uncle_cids.cid, data
|
||||
FROM eth.uncle_cids
|
||||
INNER JOIN eth.header_cids ON (uncle_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
||||
WHERE block_number = $1`
|
||||
RetrieveUncleByHashPgStr = `SELECT cid, data
|
||||
FROM eth.uncle_cids
|
||||
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
||||
WHERE block_hash = $1`
|
||||
RetrieveTransactionsByHashesPgStr = `SELECT cid, data
|
||||
FROM eth.transaction_cids
|
||||
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
||||
WHERE tx_hash = ANY($1::VARCHAR(66)[])`
|
||||
RetrieveTransactionsByBlockHashPgStr = `SELECT cid, data FROM eth.transaction_cids, eth.header_cids, public.blocks
|
||||
WHERE transaction_cids.header_id = header_cids.id
|
||||
AND transaction_cids.mh_key = blocks.key
|
||||
AND block_hash = $1`
|
||||
RetrieveTransactionsByBlockNumberPgStr = `SELECT cid, data FROM eth.transaction_cids, eth.header_cids, public.blocks
|
||||
WHERE transaction_cids.header_id = header_cids.id
|
||||
AND transaction_cids.mh_key = blocks.key
|
||||
AND block_number = $1`
|
||||
RetrieveTransactionByHashPgStr = `SELECT cid, data FROM eth.transaction_cids
|
||||
RetrieveTransactionsByBlockHashPgStr = `SELECT transaction_cids.cid, data
|
||||
FROM eth.transaction_cids
|
||||
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
||||
WHERE block_hash = $1`
|
||||
RetrieveTransactionsByBlockNumberPgStr = `SELECT transaction_cids.cid, data
|
||||
FROM eth.transaction_cids
|
||||
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
||||
WHERE block_number = $1`
|
||||
RetrieveTransactionByHashPgStr = `SELECT cid, data
|
||||
FROM eth.transaction_cids
|
||||
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
||||
WHERE tx_hash = $1`
|
||||
RetrieveReceiptsByTxHashesPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, public.blocks
|
||||
WHERE receipt_cids.mh_key = blocks.key
|
||||
AND receipt_cids.tx_id = transaction_cids.id
|
||||
AND tx_hash = ANY($1::VARCHAR(66)[])`
|
||||
RetrieveReceiptsByBlockHashPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, eth.header_cids, public.blocks
|
||||
WHERE receipt_cids.tx_id = transaction_cids.id
|
||||
AND transaction_cids.header_id = header_cids.id
|
||||
AND receipt_cids.mh_key = blocks.key
|
||||
AND block_hash = $1`
|
||||
RetrieveReceiptsByBlockNumberPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, eth.header_cids, public.blocks
|
||||
WHERE receipt_cids.tx_id = transaction_cids.id
|
||||
AND transaction_cids.header_id = header_cids.id
|
||||
AND receipt_cids.mh_key = blocks.key
|
||||
AND block_number = $1`
|
||||
RetrieveReceiptByTxHashPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, eth.receipt_cids
|
||||
WHERE receipt_cids.mh_key = blocks.key
|
||||
AND receipt_cids.tx_id = transaction_cids.id
|
||||
AND tx_hash = $1`
|
||||
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT cid, data FROM eth.state_cids, eth.header_cids, public.blocks
|
||||
WHERE state_cids.header_id = header_cids.id
|
||||
AND state_cids.mh_key = blocks.key
|
||||
AND state_leaf_key = $1
|
||||
AND block_hash = $2`
|
||||
RetrieveAccountByLeafKeyAndBlockNumberPgStr = `SELECT cid, data FROM eth.state_cids, eth.header_cids, public.blocks
|
||||
WHERE state_cids.header_id = header_cids.id
|
||||
AND state_cids.mh_key = blocks.key
|
||||
AND state_leaf_key = $1
|
||||
AND block_number = $2`
|
||||
RetrieveReceiptsByTxHashesPgStr = `SELECT receipt_cids.cid, data
|
||||
FROM eth.receipt_cids
|
||||
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||
WHERE tx_hash = ANY($1::VARCHAR(66)[])`
|
||||
RetrieveReceiptsByBlockHashPgStr = `SELECT receipt_cids.cid, data
|
||||
FROM eth.receipt_cids
|
||||
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||
WHERE block_hash = $1`
|
||||
RetrieveReceiptsByBlockNumberPgStr = `SELECT receipt_cids.cid, data
|
||||
FROM eth.receipt_cids
|
||||
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||
WHERE block_number = $1`
|
||||
RetrieveReceiptByTxHashPgStr = `SELECT receipt_cids.cid, data
|
||||
FROM eth.receipt_cids
|
||||
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||
WHERE tx_hash = $1`
|
||||
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.cid,
|
||||
data,
|
||||
was_state_removed(state_path, block_number, $2) AS removed
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (state_cids.mh_key = blocks.key)
|
||||
WHERE state_leaf_key = $1
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = $2)
|
||||
AND header_cids.id = (SELECT canonical_header(block_number))
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveAccountByLeafKeyAndBlockNumberPgStr = `SELECT state_cids.cid,
|
||||
data,
|
||||
was_state_removed(state_path, block_number, (SELECT block_hash
|
||||
FROM eth.header_cids
|
||||
WHERE block_number = $2
|
||||
LIMIT 1)) AS removed
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (state_cids.mh_key = blocks.key)
|
||||
WHERE state_leaf_key = $1
|
||||
AND block_number <= $2
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr = `SELECT storage_cids.cid,
|
||||
data,
|
||||
was_storage_removed(storage_path, block_number, (SELECT block_hash
|
||||
FROM eth.header_cids
|
||||
WHERE block_number = $3
|
||||
LIMIT 1)) AS removed
|
||||
FROM eth.storage_cids
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (storage_cids.mh_key = blocks.key)
|
||||
WHERE state_leaf_key = $1
|
||||
AND storage_leaf_key = $2
|
||||
AND block_number <= $3
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT storage_cids.cid,
|
||||
data,
|
||||
was_storage_removed(storage_path, block_number, $3) AS removed
|
||||
FROM eth.storage_cids
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
INNER JOIN public.blocks ON (storage_cids.mh_key = blocks.key)
|
||||
WHERE state_leaf_key = $1
|
||||
AND storage_leaf_key = $2
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = $3)
|
||||
AND header_cids.id = (SELECT canonical_header(block_number))
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 1`
|
||||
)
|
||||
|
||||
type ipldResult struct {
|
||||
@ -315,13 +376,23 @@ func (r *IPLDRetriever) RetrieveReceiptByHash(hash common.Hash) (string, []byte,
|
||||
return rctResult.CID, rctResult.Data, r.db.Get(rctResult, RetrieveReceiptByTxHashPgStr, hash.Hex())
|
||||
}
|
||||
|
||||
type nodeInfo struct {
|
||||
CID string `db:"cid"`
|
||||
Data []byte `db:"data"`
|
||||
Removed bool `db:"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(ipldResult)
|
||||
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.Removed {
|
||||
return "", []byte{}, nil
|
||||
}
|
||||
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())
|
||||
@ -333,25 +404,64 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockHash(address common.Addr
|
||||
}
|
||||
|
||||
// RetrieveAccountByAddressAndBlockNumber returns the cid and rlp bytes for the account corresponding to the provided address and block number
|
||||
// This can return multiple results if we have two versions of state in the database as the provided height
|
||||
func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Address, number uint64) ([]string, [][]byte, error) {
|
||||
accountResults := make([]ipldResult, 0)
|
||||
// This can return a non-canonical account
|
||||
func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Address, number uint64) (string, []byte, error) {
|
||||
accountResult := new(nodeInfo)
|
||||
leafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
if err := r.db.Get(&accountResults, RetrieveAccountByLeafKeyAndBlockNumberPgStr, leafKey.Hex(), number); err != nil {
|
||||
return nil, nil, err
|
||||
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockNumberPgStr, leafKey.Hex(), number); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
cids := make([]string, len(accountResults))
|
||||
accounts := make([][]byte, len(accountResults))
|
||||
for i, res := range accountResults {
|
||||
cids[i] = res.CID
|
||||
var iface []interface{}
|
||||
if err := rlp.DecodeBytes(res.Data, &iface); err != nil {
|
||||
return nil, nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error())
|
||||
if accountResult.Removed {
|
||||
return "", []byte{}, nil
|
||||
}
|
||||
if len(iface) != 2 {
|
||||
return nil, nil, fmt.Errorf("eth IPLDRetriever expected state leaf node rlp to decode into two elements")
|
||||
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())
|
||||
}
|
||||
accounts[i] = iface[1].([]byte)
|
||||
if len(i) != 2 {
|
||||
return "", nil, fmt.Errorf("eth IPLDRetriever expected state leaf node rlp to decode into two elements")
|
||||
}
|
||||
return cids, accounts, nil
|
||||
return accountResult.CID, i[1].([]byte), nil
|
||||
}
|
||||
|
||||
// RetrieveStorageAtByAddressAndStorageKeyAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage key, and block hash
|
||||
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(address common.Address, storageLeafKey, hash common.Hash) (string, []byte, error) {
|
||||
storageResult := new(nodeInfo)
|
||||
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageLeafKey.Hex(), hash.Hex()); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if storageResult.Removed {
|
||||
return "", []byte{}, nil
|
||||
}
|
||||
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, err
|
||||
}
|
||||
if len(i) != 2 {
|
||||
return "", nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements")
|
||||
}
|
||||
return storageResult.CID, i[1].([]byte), nil
|
||||
}
|
||||
|
||||
// RetrieveStorageAtByAddressAndStorageKeyAndBlockNumber returns the cid and rlp bytes for the storage value corresponding to the provided address, storage key, and block number
|
||||
// This can retrun a non-canonical value
|
||||
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageKeyAndBlockNumber(address common.Address, storageLeafKey common.Hash, number uint64) (string, []byte, error) {
|
||||
storageResult := new(nodeInfo)
|
||||
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr, stateLeafKey.Hex(), storageLeafKey.Hex(), number); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if storageResult.Removed {
|
||||
return "", []byte{}, nil
|
||||
}
|
||||
var i []interface{}
|
||||
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {
|
||||
return "", nil, fmt.Errorf("error decoding storage leaf node rlp: %s", err.Error())
|
||||
}
|
||||
if len(i) != 2 {
|
||||
return "", nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements")
|
||||
}
|
||||
return storageResult.CID, i[1].([]byte), nil
|
||||
}
|
||||
|
@ -1,156 +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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package eth_test
|
||||
|
||||
/*
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/mocks"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||
)
|
||||
|
||||
var _ = Describe("IPLD Retriever", func() {
|
||||
var (
|
||||
db *postgres.DB
|
||||
repo *eth2.IPLDPublisher
|
||||
//retriever *eth.IPLDRetriever
|
||||
)
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
repo = eth2.NewIPLDPublisher(db)
|
||||
//retriever = eth.NewIPLDRetriever(db)
|
||||
err = repo.Publish(mocks.MockConvertedPayload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = repo.Publish(mocks.MockConvertedPayload2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
AfterEach(func() {
|
||||
eth.TearDownDB(db)
|
||||
})
|
||||
|
||||
Describe("RetrieveHeadersByHashes", func() {
|
||||
It("Retrieves all of the headers that correspond to the provided hashes", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveHeadersByBlockNumber", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveHeaderByHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveUnclesByHashes", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveUnclesByBlockHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveUnclesByBlockNumber", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveUncleByHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveTransactionsByHashes", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveTransactionsByBlockHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveTransactionsByBlockNumber", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveTransactionByTxHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveReceiptsByTxHashes", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveReceiptsByBlockHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveReceiptsByBlockNumber", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveReceiptByHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveAccountByAddressAndBlockHash", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("RetrieveAccountByAddressAndBlockNumber", func() {
|
||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
*/
|
@ -41,8 +41,15 @@ var (
|
||||
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
|
||||
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
|
||||
ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600180819055506101e2806100676000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
|
||||
DeploymentTxData = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600180819055506101e2806100676000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
|
||||
ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
|
||||
CodeHash = crypto.Keccak256Hash(ContractCode)
|
||||
ContractAddr common.Address
|
||||
IndexZero = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
IndexOne = "0000000000000000000000000000000000000000000000000000000000000001"
|
||||
ContractSlotPosition = common.FromHex(IndexOne)
|
||||
ContractSlotKeyHash = crypto.Keccak256Hash(ContractSlotPosition)
|
||||
MiningReward = big.NewInt(2000000000000000000)
|
||||
)
|
||||
|
||||
/* test function signatures
|
||||
@ -75,7 +82,7 @@ func TestChainGen(i int, block *core.BlockGen) {
|
||||
nonce := block.TxNonce(Account1Addr)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, Account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, Account1Key)
|
||||
nonce++
|
||||
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), ContractCode), signer, Account1Key)
|
||||
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), DeploymentTxData), signer, Account1Key)
|
||||
ContractAddr = crypto.CreateAddress(Account1Addr, nonce)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
@ -88,8 +95,8 @@ func TestChainGen(i int, block *core.BlockGen) {
|
||||
case 3:
|
||||
block.SetCoinbase(Account2Addr)
|
||||
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000009")
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
|
||||
block.AddTx(tx1)
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
|
||||
block.AddTx(tx)
|
||||
case 4:
|
||||
block.SetCoinbase(Account1Addr)
|
||||
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
@ -57,8 +57,28 @@ var (
|
||||
Extra: []byte{},
|
||||
}
|
||||
MockTransactions, MockReceipts, SenderAddr = createTransactionsAndReceipts()
|
||||
MockUncles = []*types.Header{
|
||||
{
|
||||
Time: 1,
|
||||
Number: new(big.Int).Add(BlockNumber, big.NewInt(1)),
|
||||
Root: common.HexToHash("0x1"),
|
||||
TxHash: common.HexToHash("0x1"),
|
||||
ReceiptHash: common.HexToHash("0x1"),
|
||||
Difficulty: big.NewInt(500001),
|
||||
Extra: []byte{},
|
||||
},
|
||||
{
|
||||
Time: 2,
|
||||
Number: new(big.Int).Add(BlockNumber, big.NewInt(2)),
|
||||
Root: common.HexToHash("0x2"),
|
||||
TxHash: common.HexToHash("0x2"),
|
||||
ReceiptHash: common.HexToHash("0x2"),
|
||||
Difficulty: big.NewInt(500002),
|
||||
Extra: []byte{},
|
||||
},
|
||||
}
|
||||
ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts)
|
||||
MockBlock = types.NewBlock(&MockHeader, MockTransactions, nil, MockReceipts)
|
||||
MockBlock = types.NewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts)
|
||||
MockHeaderRlp, _ = rlp.EncodeToBytes(MockBlock.Header())
|
||||
Address = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592")
|
||||
AnotherAddress = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476593")
|
||||
@ -239,7 +259,7 @@ var (
|
||||
// statediff data
|
||||
storageLocation = common.HexToHash("0")
|
||||
StorageLeafKey = crypto.Keccak256Hash(storageLocation[:]).Bytes()
|
||||
StorageValue = common.Hex2Bytes("01")
|
||||
StorageValue = crypto.Keccak256([]byte{1, 2, 3, 4, 5})
|
||||
StoragePartialPath = common.Hex2Bytes("20290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
||||
StorageLeafNode, _ = rlp.EncodeToBytes([]interface{}{
|
||||
StoragePartialPath,
|
||||
@ -248,7 +268,7 @@ var (
|
||||
|
||||
nonce1 = uint64(1)
|
||||
ContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0"
|
||||
ContractCodeHash = common.HexToHash("0x753f98a8d4328b15636e46f66f2cb4bc860100aa17967cc145fcd17d1d4710ea")
|
||||
ContractCodeHash = crypto.Keccak256Hash(MockContractByteCode)
|
||||
contractPath = common.Bytes2Hex([]byte{'\x06'})
|
||||
ContractLeafKey = testhelpers.AddressToLeafKey(ContractAddress)
|
||||
ContractAccount, _ = rlp.EncodeToBytes(state.Account{
|
||||
@ -264,14 +284,14 @@ var (
|
||||
})
|
||||
|
||||
nonce0 = uint64(0)
|
||||
AccountBalance = big.NewInt(1000)
|
||||
AccountRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
AccountCodeHash = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
|
||||
accountPath = common.Bytes2Hex([]byte{'\x0c'})
|
||||
AccountAddresss = common.HexToAddress("0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e")
|
||||
AccountLeafKey = testhelpers.Account2LeafKey
|
||||
Account, _ = rlp.EncodeToBytes(state.Account{
|
||||
Nonce: nonce0,
|
||||
Balance: big.NewInt(1000),
|
||||
Balance: AccountBalance,
|
||||
CodeHash: AccountCodeHash.Bytes(),
|
||||
Root: common.HexToHash(AccountRoot),
|
||||
})
|
||||
@ -341,7 +361,7 @@ var (
|
||||
CID: HeaderCID.String(),
|
||||
MhKey: HeaderMhKey,
|
||||
TotalDifficulty: MockBlock.Difficulty().String(),
|
||||
Reward: "5000000000000000000",
|
||||
Reward: "5312500000000000000",
|
||||
StateRoot: MockBlock.Root().String(),
|
||||
RctRoot: MockBlock.ReceiptHash().String(),
|
||||
TxRoot: MockBlock.TxHash().String(),
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
||||
@ -44,6 +45,65 @@ type RPCTransaction struct {
|
||||
S *hexutil.Big `json:"s"`
|
||||
}
|
||||
|
||||
// RPCReceipt represents a receipt that will serialize to the RPC representation of a receipt
|
||||
type RPCReceipt struct {
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber"`
|
||||
TransactionHash *common.Hash `json:"transactionHash"`
|
||||
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed"`
|
||||
CumulativeGsUsed hexutil.Uint64 `json:"cumulativeGasUsed"`
|
||||
ContractAddress *common.Address `json:"contractAddress"`
|
||||
Logs []*types.Log `json:"logs"`
|
||||
Bloom types.Bloom `json:"logsBloom"`
|
||||
Root []byte `json:"root"`
|
||||
Status uint64 `json:"status"`
|
||||
}
|
||||
|
||||
// AccountResult struct for GetProof
|
||||
type AccountResult struct {
|
||||
Address common.Address `json:"address"`
|
||||
AccountProof []string `json:"accountProof"`
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
CodeHash common.Hash `json:"codeHash"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
StorageHash common.Hash `json:"storageHash"`
|
||||
StorageProof []StorageResult `json:"storageProof"`
|
||||
}
|
||||
|
||||
// StorageResult for GetProof
|
||||
type StorageResult struct {
|
||||
Key string `json:"key"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Proof []string `json:"proof"`
|
||||
}
|
||||
|
||||
// CallArgs represents the arguments for a call.
|
||||
type CallArgs struct {
|
||||
From *common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
}
|
||||
|
||||
// account indicates the overriding fields of account during the execution of
|
||||
// a message call.
|
||||
// Note, state and stateDiff can't be specified at the same time. If state is
|
||||
// set, message execution will only use the data in the given state. Otherwise
|
||||
// if statDiff is set, all diff will be applied first and then execute the call
|
||||
// message.
|
||||
type account struct {
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
Code *hexutil.Bytes `json:"code"`
|
||||
Balance **hexutil.Big `json:"balance"`
|
||||
State *map[common.Hash]common.Hash `json:"state"`
|
||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||
}
|
||||
|
||||
// IPLDs is used to package raw IPLD block data fetched from IPFS and returned by the server
|
||||
// Returned by IPLDFetcher and ResponseFilterer
|
||||
type IPLDs struct {
|
||||
|
Loading…
Reference in New Issue
Block a user