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:
|
The currently supported standard endpoints are:
|
||||||
`eth_call`
|
`eth_call`
|
||||||
|
`eth_getBalance`
|
||||||
|
`eth_getStorageAt`
|
||||||
|
`eth_getCode`
|
||||||
|
`eth_getProof`
|
||||||
`eth_blockNumber`
|
`eth_blockNumber`
|
||||||
`eth_getLogs`
|
|
||||||
`eth_getHeaderByNumber`
|
`eth_getHeaderByNumber`
|
||||||
|
`eth_getHeaderByHash`
|
||||||
`eth_getBlockByNumber`
|
`eth_getBlockByNumber`
|
||||||
`eth_getBlockByHash`
|
`eth_getBlockByHash`
|
||||||
|
`eth_getTransactionCount`
|
||||||
|
`eth_getBlockTransactionCountByHash`
|
||||||
|
`eth_getBlockTransactionCountByNumber`
|
||||||
`eth_getTransactionByHash`
|
`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)
|
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_tablespace = '';
|
||||||
|
|
||||||
SET default_table_access_method = heap;
|
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/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/sirupsen/logrus"
|
"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.
|
// BlockNumber returns the block number of the chain head.
|
||||||
func (pea *PublicEthAPI) BlockNumber() hexutil.Uint64 {
|
func (pea *PublicEthAPI) BlockNumber() hexutil.Uint64 {
|
||||||
number, _ := pea.B.Retriever.RetrieveLastBlockNumber()
|
number, _ := pea.B.Retriever.RetrieveLastBlockNumber()
|
||||||
return hexutil.Uint64(number)
|
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.
|
// GetLogs returns logs matching the given argument that are stored within the state.
|
||||||
//
|
//
|
||||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
|
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
|
||||||
func (pea *PublicEthAPI) GetLogs(ctx context.Context, crit ethereum.FilterQuery) ([]*types.Log, error) {
|
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 err != nil && pea.rpc != nil {
|
||||||
if arg, err := toFilterArg(crit); err == nil {
|
if arg, err := toFilterArg(crit); err == nil {
|
||||||
var result []*types.Log
|
var res []*types.Log
|
||||||
if err := pea.rpc.CallContext(ctx, &result, "eth_getLogs", arg); err == nil {
|
if err := pea.rpc.CallContext(ctx, &res, "eth_getLogs", arg); err == nil {
|
||||||
return result, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return logs, err
|
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
|
// Convert FilterQuery into ReceiptFilter
|
||||||
addrStrs := make([]string, len(crit.Addresses))
|
addrStrs := make([]string, len(crit.Addresses))
|
||||||
for i, addr := range 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
|
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.
|
State and Storage
|
||||||
// * 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTransactionByHash returns the transaction for the given hash
|
// GetBalance returns the amount of wei for the given address in the state of the
|
||||||
// eth ipld-eth-server cannot currently handle pending/tx_pool txs
|
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
|
||||||
func (pea *PublicEthAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
// block numbers are also allowed.
|
||||||
tx, blockHash, blockNumber, index, err := pea.B.GetTransaction(ctx, hash)
|
func (pea *PublicEthAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
||||||
if tx != nil && err == nil {
|
bal, err := pea.localGetBalance(ctx, address, blockNrOrHash)
|
||||||
return NewRPCTransaction(tx, blockHash, blockNumber, index), nil
|
if bal != nil && err == nil {
|
||||||
|
return bal, nil
|
||||||
}
|
}
|
||||||
if pea.rpc != nil {
|
if pea.rpc != nil {
|
||||||
if tx, err := pea.remoteGetTransactionByHash(ctx, hash); tx != nil && err == nil {
|
var res *hexutil.Big
|
||||||
return tx, nil
|
if err := pea.rpc.CallContext(ctx, &res, "eth_getBalance", address, blockNrOrHash); res != nil && err == nil {
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pea *PublicEthAPI) remoteGetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
func (pea *PublicEthAPI) localGetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
||||||
var tx *RPCTransaction
|
account, err := pea.B.GetAccountByNumberOrHash(ctx, address, blockNrOrHash)
|
||||||
if err := pea.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// 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 {
|
if overrides != nil {
|
||||||
accounts = *overrides
|
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 (failed || err != nil) && pea.rpc != nil {
|
||||||
if res, err := pea.remoteCall(ctx, args, blockNrOrHash, overrides); res != nil && err == nil {
|
var hex hexutil.Bytes
|
||||||
return res, nil
|
if err := pea.rpc.CallContext(ctx, &hex, "eth_call", args, blockNrOrHash, overrides); hex != nil && err == nil {
|
||||||
|
return hex, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if failed && err == nil {
|
if failed && err == nil {
|
||||||
return nil, errors.New("eth_call failed without error")
|
return nil, errors.New("eth_call failed without error")
|
||||||
}
|
}
|
||||||
return (hexutil.Bytes)(result), err
|
return (hexutil.Bytes)(res), 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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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
|
return res, gas, failed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func toFilterArg(q ethereum.FilterQuery) (interface{}, error) {
|
// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field
|
||||||
arg := map[string]interface{}{
|
func (pea *PublicEthAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
||||||
"address": q.Addresses,
|
fields, err := RPCMarshalBlock(b, inclTx, fullTx)
|
||||||
"topics": q.Topics,
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if q.BlockHash != nil {
|
if inclTx {
|
||||||
arg["blockHash"] = *q.BlockHash
|
td, err := pea.B.GetTd(b.Hash())
|
||||||
if q.FromBlock != nil || q.ToBlock != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock")
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
fields["totalDifficulty"] = (*hexutil.Big)(td)
|
||||||
if q.FromBlock == nil {
|
|
||||||
arg["fromBlock"] = "0x0"
|
|
||||||
} else {
|
|
||||||
arg["fromBlock"] = toBlockNumArg(q.FromBlock)
|
|
||||||
}
|
|
||||||
arg["toBlock"] = toBlockNumArg(q.ToBlock)
|
|
||||||
}
|
}
|
||||||
return arg, nil
|
return fields, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func toBlockNumArg(number *big.Int) string {
|
// rpcMarshalBlockWithUncleHashes uses the generalized output filler, then adds the total difficulty field
|
||||||
if number == nil {
|
func (pea *PublicEthAPI) rpcMarshalBlockWithUncleHashes(b *types.Block, uncleHashes []common.Hash, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
||||||
return "latest"
|
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"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/ethereum/go-ethereum/rpc"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
"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"
|
||||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||||
@ -37,6 +40,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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{}{
|
expectedBlock = map[string]interface{}{
|
||||||
"number": (*hexutil.Big)(test_helpers.MockBlock.Number()),
|
"number": (*hexutil.Big)(test_helpers.MockBlock.Number()),
|
||||||
"hash": test_helpers.MockBlock.Hash(),
|
"hash": test_helpers.MockBlock.Hash(),
|
||||||
@ -59,7 +68,7 @@ var (
|
|||||||
}
|
}
|
||||||
expectedHeader = map[string]interface{}{
|
expectedHeader = map[string]interface{}{
|
||||||
"number": (*hexutil.Big)(test_helpers.MockBlock.Header().Number),
|
"number": (*hexutil.Big)(test_helpers.MockBlock.Header().Number),
|
||||||
"hash": test_helpers.MockBlock.Header().Hash(),
|
"hash": blockHash,
|
||||||
"parentHash": test_helpers.MockBlock.Header().ParentHash,
|
"parentHash": test_helpers.MockBlock.Header().ParentHash,
|
||||||
"nonce": test_helpers.MockBlock.Header().Nonce,
|
"nonce": test_helpers.MockBlock.Header().Nonce,
|
||||||
"mixHash": test_helpers.MockBlock.Header().MixDigest,
|
"mixHash": test_helpers.MockBlock.Header().MixDigest,
|
||||||
@ -77,26 +86,115 @@ var (
|
|||||||
"receiptsRoot": test_helpers.MockBlock.Header().ReceiptHash,
|
"receiptsRoot": test_helpers.MockBlock.Header().ReceiptHash,
|
||||||
"totalDifficulty": (*hexutil.Big)(test_helpers.MockBlock.Header().Difficulty),
|
"totalDifficulty": (*hexutil.Big)(test_helpers.MockBlock.Header().Difficulty),
|
||||||
}
|
}
|
||||||
expectedTransaction = eth.NewRPCTransaction(test_helpers.MockTransactions[0], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 0)
|
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 _ = Describe("API", func() {
|
||||||
var (
|
var (
|
||||||
db *postgres.DB
|
db *postgres.DB
|
||||||
indexAndPublisher *eth2.IPLDPublisher
|
api *eth.PublicEthAPI
|
||||||
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
|
var err error
|
||||||
db, err = shared.SetupDB()
|
db, err = shared.SetupDB()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
indexAndPublisher = eth2.NewIPLDPublisher(db)
|
indexAndPublisher := eth2.NewIPLDPublisher(db)
|
||||||
backend, err = eth.NewEthBackend(db, ð.Config{})
|
backend, err := eth.NewEthBackend(db, ð.Config{})
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
api = eth.NewPublicEthAPI(backend, nil)
|
api = eth.NewPublicEthAPI(backend, nil)
|
||||||
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
|
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
err = publishCode(db, test_helpers.ContractCodeHash, test_helpers.ContractCode)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
uncles := test_helpers.MockBlock.Uncles()
|
uncles := test_helpers.MockBlock.Uncles()
|
||||||
uncleHashes := make([]common.Hash, len(uncles))
|
uncleHashes := make([]common.Hash, len(uncles))
|
||||||
for i, uncle := range uncles {
|
for i, uncle := range uncles {
|
||||||
@ -104,69 +202,22 @@ var _ = Describe("API", func() {
|
|||||||
}
|
}
|
||||||
expectedBlock["uncles"] = uncleHashes
|
expectedBlock["uncles"] = uncleHashes
|
||||||
})
|
})
|
||||||
AfterEach(func() {
|
// Single test db tear down at end of all tests
|
||||||
eth.TearDownDB(db)
|
defer It("test teardown", 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()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Describe("GetTransactionByHash", func() {
|
Headers and blocks
|
||||||
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))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Describe("GetBlockByNumber", func() {
|
*/
|
||||||
It("Retrieves a block by number", func() {
|
Describe("eth_getHeaderByNumber", 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() {
|
|
||||||
It("Retrieves a header by number", func() {
|
It("Retrieves a header by number", func() {
|
||||||
number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64)
|
header, err := api.GetHeaderByNumber(ctx, number)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number))
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(header).To(Equal(expectedHeader))
|
Expect(header).To(Equal(expectedHeader))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Throws an error if a header cannot be found", func() {
|
It("Throws an error if a header cannot be found", func() {
|
||||||
number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64)
|
header, err := api.GetHeaderByNumber(ctx, wrongNumber)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number+1))
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
Expect(err.Error()).To(ContainSubstring("sql: no rows in result set"))
|
||||||
Expect(header).To(BeNil())
|
Expect(header).To(BeNil())
|
||||||
@ -175,10 +226,30 @@ var _ = Describe("API", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("GetBlockByHash", func() {
|
Describe("eth_getHeaderByHash", func() {
|
||||||
It("Retrieves a block by hash", func() {
|
It("Retrieves a header by hash", func() {
|
||||||
// without full txs
|
header := api.GetHeaderByHash(ctx, blockHash)
|
||||||
block, err := api.GetBlockByHash(context.Background(), test_helpers.MockBlock.Hash(), false)
|
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())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
transactionHashes := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
transactionHashes := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||||
for i, trx := range 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 {
|
for key, val := range expectedBlock {
|
||||||
Expect(val).To(Equal(block[key]))
|
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())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
transactions := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
transactions := make([]interface{}, len(test_helpers.MockBlock.Transactions()))
|
||||||
for i, trx := range 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]))
|
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() {
|
It("Retrieves receipt logs that match the provided topics within the provided range", func() {
|
||||||
crit := ethereum.FilterQuery{
|
crit := ethereum.FilterQuery{
|
||||||
Topics: [][]common.Hash{
|
Topics: [][]common.Hash{
|
||||||
@ -213,7 +564,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||||
@ -228,7 +579,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
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(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||||
@ -260,7 +611,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(0))
|
Expect(len(logs)).To(Equal(0))
|
||||||
|
|
||||||
@ -276,7 +627,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||||
@ -293,7 +644,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||||
@ -311,7 +662,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||||
@ -330,7 +681,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
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(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
||||||
@ -360,7 +711,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
||||||
@ -370,7 +721,7 @@ var _ = Describe("API", func() {
|
|||||||
FromBlock: test_helpers.MockBlock.Number(),
|
FromBlock: test_helpers.MockBlock.Number(),
|
||||||
ToBlock: 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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(0))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog2}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||||
@ -506,7 +857,7 @@ var _ = Describe("API", func() {
|
|||||||
BlockHash: &hash,
|
BlockHash: &hash,
|
||||||
Topics: [][]common.Hash{},
|
Topics: [][]common.Hash{},
|
||||||
}
|
}
|
||||||
logs, err = api.GetLogs(context.Background(), crit)
|
logs, err = api.GetLogs(ctx, crit)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(1))
|
Expect(len(logs)).To(Equal(1))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1}))
|
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(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
||||||
@ -566,10 +917,75 @@ var _ = Describe("API", func() {
|
|||||||
test_helpers.AnotherAddress,
|
test_helpers.AnotherAddress,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
logs, err = api.GetLogs(context.Background(), crit)
|
logs, err = api.GetLogs(ctx, crit)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(len(logs)).To(Equal(2))
|
Expect(len(logs)).To(Equal(2))
|
||||||
Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2}))
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/common/math"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"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/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
ipfsethdb "github.com/vulcanize/pg-ipfs-ethdb"
|
ipfsethdb "github.com/vulcanize/pg-ipfs-ethdb"
|
||||||
|
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
"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"
|
"github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,6 +65,17 @@ const (
|
|||||||
WHERE blocks.key = transaction_cids.mh_key
|
WHERE blocks.key = transaction_cids.mh_key
|
||||||
AND transaction_cids.header_id = header_cids.id
|
AND transaction_cids.header_id = header_cids.id
|
||||||
AND transaction_cids.tx_hash = $1`
|
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 {
|
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")
|
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
|
// GetTd gets the total difficulty at the given block hash
|
||||||
func (b *Backend) GetTd(blockHash common.Hash) (*big.Int, error) {
|
func (b *Backend) GetTd(blockHash common.Hash) (*big.Int, error) {
|
||||||
var tdStr string
|
var tdStr string
|
||||||
@ -431,7 +434,7 @@ func (b *Backend) GetReceipts(ctx context.Context, hash common.Hash) (types.Rece
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rcts := make(types.Receipts, 0, len(receiptBytes))
|
rcts := make(types.Receipts, len(receiptBytes))
|
||||||
for i, rctBytes := range receiptBytes {
|
for i, rctBytes := range receiptBytes {
|
||||||
rct := new(types.Receipt)
|
rct := new(types.Receipt)
|
||||||
if err := rlp.DecodeBytes(rctBytes, rct); err != nil {
|
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
|
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
|
// 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) {
|
func (b *Backend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
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
|
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
|
// Engine satisfied the ChainContext interface
|
||||||
func (b *Backend) Engine() consensus.Engine {
|
func (b *Backend) Engine() consensus.Engine {
|
||||||
// TODO: we need to support more than just ethash based engines
|
// TODO: we need to support more than just ethash based engines
|
||||||
|
@ -17,12 +17,19 @@
|
|||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
"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
|
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.
|
// NewRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
|
||||||
func NewRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction {
|
func NewRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction {
|
||||||
for idx, tx := range b.Transactions() {
|
for idx, tx := range b.Transactions() {
|
||||||
@ -127,6 +163,77 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
|||||||
return result
|
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.
|
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
|
||||||
func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction {
|
func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction {
|
||||||
txs := b.Transactions()
|
txs := b.Transactions()
|
||||||
@ -183,3 +290,31 @@ func sliceContainsHash(slice []string, hash common.Hash) int {
|
|||||||
}
|
}
|
||||||
return 0
|
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 (
|
const (
|
||||||
RetrieveHeadersByHashesPgStr = `SELECT cid, data FROM eth.header_cids
|
RetrieveHeadersByHashesPgStr = `SELECT cid, data
|
||||||
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
FROM eth.header_cids
|
||||||
|
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
||||||
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
||||||
RetrieveHeadersByBlockNumberPgStr = `SELECT cid, data FROM eth.header_cids
|
RetrieveHeadersByBlockNumberPgStr = `SELECT cid, data
|
||||||
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
FROM eth.header_cids
|
||||||
|
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
||||||
WHERE block_number = $1`
|
WHERE block_number = $1`
|
||||||
RetrieveHeaderByHashPgStr = `SELECT cid, data FROM eth.header_cids
|
RetrieveHeaderByHashPgStr = `SELECT cid, data
|
||||||
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
FROM eth.header_cids
|
||||||
|
INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key)
|
||||||
WHERE block_hash = $1`
|
WHERE block_hash = $1`
|
||||||
RetrieveUnclesByHashesPgStr = `SELECT cid, data FROM eth.uncle_cids
|
RetrieveUnclesByHashesPgStr = `SELECT cid, data
|
||||||
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
FROM eth.uncle_cids
|
||||||
|
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
||||||
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
||||||
RetrieveUnclesByBlockHashPgStr = `SELECT cid, data FROM eth.uncle_cids, eth.header_cids, public.blocks
|
RetrieveUnclesByBlockHashPgStr = `SELECT uncle_cids.cid, data
|
||||||
WHERE uncle_cids.header_id = header_cids.id
|
FROM eth.uncle_cids
|
||||||
AND uncle_cids.mh_key = blocks.key
|
INNER JOIN eth.header_cids ON (uncle_cids.header_id = header_cids.id)
|
||||||
AND block_hash = $1`
|
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
||||||
RetrieveUnclesByBlockNumberPgStr = `SELECT cid, data FROM eth.uncle_cids, eth.header_cids, public.blocks
|
WHERE block_hash = $1`
|
||||||
WHERE uncle_cids.header_id = header_cids.id
|
RetrieveUnclesByBlockNumberPgStr = `SELECT uncle_cids.cid, data
|
||||||
AND uncle_cids.mh_key = blocks.key
|
FROM eth.uncle_cids
|
||||||
AND block_number = $1`
|
INNER JOIN eth.header_cids ON (uncle_cids.header_id = header_cids.id)
|
||||||
RetrieveUncleByHashPgStr = `SELECT cid, data FROM eth.uncle_cids
|
INNER JOIN public.blocks ON (uncle_cids.mh_key = blocks.key)
|
||||||
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`
|
WHERE block_hash = $1`
|
||||||
RetrieveTransactionsByHashesPgStr = `SELECT cid, data FROM eth.transaction_cids
|
RetrieveTransactionsByHashesPgStr = `SELECT cid, data
|
||||||
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
FROM eth.transaction_cids
|
||||||
|
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
||||||
WHERE tx_hash = ANY($1::VARCHAR(66)[])`
|
WHERE tx_hash = ANY($1::VARCHAR(66)[])`
|
||||||
RetrieveTransactionsByBlockHashPgStr = `SELECT cid, data FROM eth.transaction_cids, eth.header_cids, public.blocks
|
RetrieveTransactionsByBlockHashPgStr = `SELECT transaction_cids.cid, data
|
||||||
WHERE transaction_cids.header_id = header_cids.id
|
FROM eth.transaction_cids
|
||||||
AND transaction_cids.mh_key = blocks.key
|
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||||
AND block_hash = $1`
|
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
||||||
RetrieveTransactionsByBlockNumberPgStr = `SELECT cid, data FROM eth.transaction_cids, eth.header_cids, public.blocks
|
WHERE block_hash = $1`
|
||||||
WHERE transaction_cids.header_id = header_cids.id
|
RetrieveTransactionsByBlockNumberPgStr = `SELECT transaction_cids.cid, data
|
||||||
AND transaction_cids.mh_key = blocks.key
|
FROM eth.transaction_cids
|
||||||
AND block_number = $1`
|
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||||
RetrieveTransactionByHashPgStr = `SELECT cid, data FROM eth.transaction_cids
|
INNER JOIN public.blocks ON (transaction_cids.mh_key = blocks.key)
|
||||||
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`
|
WHERE tx_hash = $1`
|
||||||
RetrieveReceiptsByTxHashesPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, public.blocks
|
RetrieveReceiptsByTxHashesPgStr = `SELECT receipt_cids.cid, data
|
||||||
WHERE receipt_cids.mh_key = blocks.key
|
FROM eth.receipt_cids
|
||||||
AND receipt_cids.tx_id = transaction_cids.id
|
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||||
AND tx_hash = ANY($1::VARCHAR(66)[])`
|
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||||
RetrieveReceiptsByBlockHashPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, eth.header_cids, public.blocks
|
WHERE tx_hash = ANY($1::VARCHAR(66)[])`
|
||||||
WHERE receipt_cids.tx_id = transaction_cids.id
|
RetrieveReceiptsByBlockHashPgStr = `SELECT receipt_cids.cid, data
|
||||||
AND transaction_cids.header_id = header_cids.id
|
FROM eth.receipt_cids
|
||||||
AND receipt_cids.mh_key = blocks.key
|
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||||
AND block_hash = $1`
|
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||||
RetrieveReceiptsByBlockNumberPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, eth.header_cids, public.blocks
|
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||||
WHERE receipt_cids.tx_id = transaction_cids.id
|
WHERE block_hash = $1`
|
||||||
AND transaction_cids.header_id = header_cids.id
|
RetrieveReceiptsByBlockNumberPgStr = `SELECT receipt_cids.cid, data
|
||||||
AND receipt_cids.mh_key = blocks.key
|
FROM eth.receipt_cids
|
||||||
AND block_number = $1`
|
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||||
RetrieveReceiptByTxHashPgStr = `SELECT cid, data FROM eth.receipt_cids, eth.transaction_cids, eth.receipt_cids
|
INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||||
WHERE receipt_cids.mh_key = blocks.key
|
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||||
AND receipt_cids.tx_id = transaction_cids.id
|
WHERE block_number = $1`
|
||||||
AND tx_hash = $1`
|
RetrieveReceiptByTxHashPgStr = `SELECT receipt_cids.cid, data
|
||||||
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT cid, data FROM eth.state_cids, eth.header_cids, public.blocks
|
FROM eth.receipt_cids
|
||||||
WHERE state_cids.header_id = header_cids.id
|
INNER JOIN eth.transaction_cids ON (receipt_cids.tx_id = transaction_cids.id)
|
||||||
AND state_cids.mh_key = blocks.key
|
INNER JOIN public.blocks ON (receipt_cids.mh_key = blocks.key)
|
||||||
AND state_leaf_key = $1
|
WHERE tx_hash = $1`
|
||||||
AND block_hash = $2`
|
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.cid,
|
||||||
RetrieveAccountByLeafKeyAndBlockNumberPgStr = `SELECT cid, data FROM eth.state_cids, eth.header_cids, public.blocks
|
data,
|
||||||
WHERE state_cids.header_id = header_cids.id
|
was_state_removed(state_path, block_number, $2) AS removed
|
||||||
AND state_cids.mh_key = blocks.key
|
FROM eth.state_cids
|
||||||
AND state_leaf_key = $1
|
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||||
AND block_number = $2`
|
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 {
|
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())
|
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
|
// 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) {
|
func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockHash(address common.Address, hash common.Hash) (string, []byte, error) {
|
||||||
accountResult := new(ipldResult)
|
accountResult := new(nodeInfo)
|
||||||
leafKey := crypto.Keccak256Hash(address.Bytes())
|
leafKey := crypto.Keccak256Hash(address.Bytes())
|
||||||
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil {
|
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
if accountResult.Removed {
|
||||||
|
return "", []byte{}, nil
|
||||||
|
}
|
||||||
var i []interface{}
|
var i []interface{}
|
||||||
if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil {
|
if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil {
|
||||||
return "", nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error())
|
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
|
// 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
|
// This can return a non-canonical account
|
||||||
func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Address, number uint64) ([]string, [][]byte, error) {
|
func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Address, number uint64) (string, []byte, error) {
|
||||||
accountResults := make([]ipldResult, 0)
|
accountResult := new(nodeInfo)
|
||||||
leafKey := crypto.Keccak256Hash(address.Bytes())
|
leafKey := crypto.Keccak256Hash(address.Bytes())
|
||||||
if err := r.db.Get(&accountResults, RetrieveAccountByLeafKeyAndBlockNumberPgStr, leafKey.Hex(), number); err != nil {
|
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockNumberPgStr, leafKey.Hex(), number); err != nil {
|
||||||
return nil, nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
cids := make([]string, len(accountResults))
|
if accountResult.Removed {
|
||||||
accounts := make([][]byte, len(accountResults))
|
return "", []byte{}, nil
|
||||||
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 len(iface) != 2 {
|
|
||||||
return nil, nil, fmt.Errorf("eth IPLDRetriever expected state leaf node rlp to decode into two elements")
|
|
||||||
}
|
|
||||||
accounts[i] = iface[1].([]byte)
|
|
||||||
}
|
}
|
||||||
return cids, accounts, 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())
|
||||||
|
}
|
||||||
|
if len(i) != 2 {
|
||||||
|
return "", nil, fmt.Errorf("eth IPLDRetriever expected state leaf node rlp to decode into two elements")
|
||||||
|
}
|
||||||
|
return accountResult.CID, i[1].([]byte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
|
||||||
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
*/
|
|
@ -37,12 +37,19 @@ var (
|
|||||||
TestBankFunds = big.NewInt(100000000)
|
TestBankFunds = big.NewInt(100000000)
|
||||||
Genesis = core.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds)
|
Genesis = core.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds)
|
||||||
|
|
||||||
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||||
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||||
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
|
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
|
||||||
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
|
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
|
||||||
ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600180819055506101e2806100676000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
|
DeploymentTxData = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600180819055506101e2806100676000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
|
||||||
ContractAddr common.Address
|
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
|
/* test function signatures
|
||||||
@ -75,7 +82,7 @@ func TestChainGen(i int, block *core.BlockGen) {
|
|||||||
nonce := block.TxNonce(Account1Addr)
|
nonce := block.TxNonce(Account1Addr)
|
||||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, Account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, Account1Key)
|
tx2, _ := types.SignTx(types.NewTransaction(nonce, Account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, Account1Key)
|
||||||
nonce++
|
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)
|
ContractAddr = crypto.CreateAddress(Account1Addr, nonce)
|
||||||
block.AddTx(tx1)
|
block.AddTx(tx1)
|
||||||
block.AddTx(tx2)
|
block.AddTx(tx2)
|
||||||
@ -88,8 +95,8 @@ func TestChainGen(i int, block *core.BlockGen) {
|
|||||||
case 3:
|
case 3:
|
||||||
block.SetCoinbase(Account2Addr)
|
block.SetCoinbase(Account2Addr)
|
||||||
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000009")
|
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000009")
|
||||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
|
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
|
||||||
block.AddTx(tx1)
|
block.AddTx(tx)
|
||||||
case 4:
|
case 4:
|
||||||
block.SetCoinbase(Account1Addr)
|
block.SetCoinbase(Account1Addr)
|
||||||
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000000")
|
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000000")
|
||||||
|
@ -57,19 +57,39 @@ var (
|
|||||||
Extra: []byte{},
|
Extra: []byte{},
|
||||||
}
|
}
|
||||||
MockTransactions, MockReceipts, SenderAddr = createTransactionsAndReceipts()
|
MockTransactions, MockReceipts, SenderAddr = createTransactionsAndReceipts()
|
||||||
ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts)
|
MockUncles = []*types.Header{
|
||||||
MockBlock = types.NewBlock(&MockHeader, MockTransactions, nil, MockReceipts)
|
{
|
||||||
MockHeaderRlp, _ = rlp.EncodeToBytes(MockBlock.Header())
|
Time: 1,
|
||||||
Address = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592")
|
Number: new(big.Int).Add(BlockNumber, big.NewInt(1)),
|
||||||
AnotherAddress = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476593")
|
Root: common.HexToHash("0x1"),
|
||||||
ContractAddress = crypto.CreateAddress(SenderAddr, MockTransactions[2].Nonce())
|
TxHash: common.HexToHash("0x1"),
|
||||||
ContractHash = crypto.Keccak256Hash(ContractAddress.Bytes()).String()
|
ReceiptHash: common.HexToHash("0x1"),
|
||||||
MockContractByteCode = []byte{0, 1, 2, 3, 4, 5}
|
Difficulty: big.NewInt(500001),
|
||||||
mockTopic11 = common.HexToHash("0x04")
|
Extra: []byte{},
|
||||||
mockTopic12 = common.HexToHash("0x06")
|
},
|
||||||
mockTopic21 = common.HexToHash("0x05")
|
{
|
||||||
mockTopic22 = common.HexToHash("0x07")
|
Time: 2,
|
||||||
MockLog1 = &types.Log{
|
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, MockUncles, MockReceipts)
|
||||||
|
MockHeaderRlp, _ = rlp.EncodeToBytes(MockBlock.Header())
|
||||||
|
Address = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592")
|
||||||
|
AnotherAddress = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476593")
|
||||||
|
ContractAddress = crypto.CreateAddress(SenderAddr, MockTransactions[2].Nonce())
|
||||||
|
ContractHash = crypto.Keccak256Hash(ContractAddress.Bytes()).String()
|
||||||
|
MockContractByteCode = []byte{0, 1, 2, 3, 4, 5}
|
||||||
|
mockTopic11 = common.HexToHash("0x04")
|
||||||
|
mockTopic12 = common.HexToHash("0x06")
|
||||||
|
mockTopic21 = common.HexToHash("0x05")
|
||||||
|
mockTopic22 = common.HexToHash("0x07")
|
||||||
|
MockLog1 = &types.Log{
|
||||||
Address: Address,
|
Address: Address,
|
||||||
Topics: []common.Hash{mockTopic11, mockTopic12},
|
Topics: []common.Hash{mockTopic11, mockTopic12},
|
||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
@ -239,7 +259,7 @@ var (
|
|||||||
// statediff data
|
// statediff data
|
||||||
storageLocation = common.HexToHash("0")
|
storageLocation = common.HexToHash("0")
|
||||||
StorageLeafKey = crypto.Keccak256Hash(storageLocation[:]).Bytes()
|
StorageLeafKey = crypto.Keccak256Hash(storageLocation[:]).Bytes()
|
||||||
StorageValue = common.Hex2Bytes("01")
|
StorageValue = crypto.Keccak256([]byte{1, 2, 3, 4, 5})
|
||||||
StoragePartialPath = common.Hex2Bytes("20290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
StoragePartialPath = common.Hex2Bytes("20290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
||||||
StorageLeafNode, _ = rlp.EncodeToBytes([]interface{}{
|
StorageLeafNode, _ = rlp.EncodeToBytes([]interface{}{
|
||||||
StoragePartialPath,
|
StoragePartialPath,
|
||||||
@ -248,7 +268,7 @@ var (
|
|||||||
|
|
||||||
nonce1 = uint64(1)
|
nonce1 = uint64(1)
|
||||||
ContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0"
|
ContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0"
|
||||||
ContractCodeHash = common.HexToHash("0x753f98a8d4328b15636e46f66f2cb4bc860100aa17967cc145fcd17d1d4710ea")
|
ContractCodeHash = crypto.Keccak256Hash(MockContractByteCode)
|
||||||
contractPath = common.Bytes2Hex([]byte{'\x06'})
|
contractPath = common.Bytes2Hex([]byte{'\x06'})
|
||||||
ContractLeafKey = testhelpers.AddressToLeafKey(ContractAddress)
|
ContractLeafKey = testhelpers.AddressToLeafKey(ContractAddress)
|
||||||
ContractAccount, _ = rlp.EncodeToBytes(state.Account{
|
ContractAccount, _ = rlp.EncodeToBytes(state.Account{
|
||||||
@ -264,14 +284,14 @@ var (
|
|||||||
})
|
})
|
||||||
|
|
||||||
nonce0 = uint64(0)
|
nonce0 = uint64(0)
|
||||||
|
AccountBalance = big.NewInt(1000)
|
||||||
AccountRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
AccountRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||||
AccountCodeHash = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
|
AccountCodeHash = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
|
||||||
accountPath = common.Bytes2Hex([]byte{'\x0c'})
|
|
||||||
AccountAddresss = common.HexToAddress("0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e")
|
AccountAddresss = common.HexToAddress("0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e")
|
||||||
AccountLeafKey = testhelpers.Account2LeafKey
|
AccountLeafKey = testhelpers.Account2LeafKey
|
||||||
Account, _ = rlp.EncodeToBytes(state.Account{
|
Account, _ = rlp.EncodeToBytes(state.Account{
|
||||||
Nonce: nonce0,
|
Nonce: nonce0,
|
||||||
Balance: big.NewInt(1000),
|
Balance: AccountBalance,
|
||||||
CodeHash: AccountCodeHash.Bytes(),
|
CodeHash: AccountCodeHash.Bytes(),
|
||||||
Root: common.HexToHash(AccountRoot),
|
Root: common.HexToHash(AccountRoot),
|
||||||
})
|
})
|
||||||
@ -341,7 +361,7 @@ var (
|
|||||||
CID: HeaderCID.String(),
|
CID: HeaderCID.String(),
|
||||||
MhKey: HeaderMhKey,
|
MhKey: HeaderMhKey,
|
||||||
TotalDifficulty: MockBlock.Difficulty().String(),
|
TotalDifficulty: MockBlock.Difficulty().String(),
|
||||||
Reward: "5000000000000000000",
|
Reward: "5312500000000000000",
|
||||||
StateRoot: MockBlock.Root().String(),
|
StateRoot: MockBlock.Root().String(),
|
||||||
RctRoot: MockBlock.ReceiptHash().String(),
|
RctRoot: MockBlock.ReceiptHash().String(),
|
||||||
TxRoot: MockBlock.TxHash().String(),
|
TxRoot: MockBlock.TxHash().String(),
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/statediff"
|
"github.com/ethereum/go-ethereum/statediff"
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||||
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
"github.com/vulcanize/ipld-eth-indexer/pkg/ipfs"
|
||||||
@ -44,6 +45,65 @@ type RPCTransaction struct {
|
|||||||
S *hexutil.Big `json:"s"`
|
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
|
// IPLDs is used to package raw IPLD block data fetched from IPFS and returned by the server
|
||||||
// Returned by IPLDFetcher and ResponseFilterer
|
// Returned by IPLDFetcher and ResponseFilterer
|
||||||
type IPLDs struct {
|
type IPLDs struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user