Skip to content

Commit

Permalink
R4R: add batch query methods (#115)
Browse files Browse the repository at this point in the history
* add batch query method

* GetTransactionDataAndReceipt

* add new rpc to goclient

* fix web3 console

* rename tx_data to txData
  • Loading branch information
HaoyangLiu authored Mar 19, 2021
1 parent f16d8e0 commit 0e2c471
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 24 deletions.
6 changes: 6 additions & 0 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ const (
ReceiptStatusSuccessful = uint64(1)
)

// Receipt represents the results of a transaction.
type OriginalDataAndReceipt struct {
Receipt Receipt `json:"receipt"`
TxData Transaction `json:"txData"`
}

// Receipt represents the results of a transaction.
type Receipt struct {
// Consensus fields: These fields are defined by the Yellow Paper
Expand Down
38 changes: 38 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,44 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
return json.tx, err
}

// TransactionInBlock returns a single transaction at index in the given block.
func (ec *Client) TransactionsInBlock(ctx context.Context, number *big.Int) ([]*types.Transaction, error) {
var rpcTxs []*rpcTransaction
err := ec.c.CallContext(ctx, &rpcTxs, "eth_getTransactionsByBlockNumber", toBlockNumArg(number))
if err != nil {
return nil, err
}
fmt.Println(len(rpcTxs))
txs := make([]*types.Transaction, 0, len(rpcTxs))
for _, tx := range rpcTxs {
txs = append(txs, tx.tx)
}
return txs, err
}

// TransactionInBlock returns a single transaction at index in the given block.
func (ec *Client) TransactionRecipientsInBlock(ctx context.Context, number *big.Int) ([]*types.Receipt, error) {
var rs []*types.Receipt
err := ec.c.CallContext(ctx, &rs, "eth_getTransactionReceiptsByBlockNumber", toBlockNumArg(number))
if err != nil {
return nil, err
}
return rs, err
}

// TransactionDataAndReceipt returns the original data and receipt of a transaction by transaction hash.
// Note that the receipt is not available for pending transactions.
func (ec *Client) TransactionDataAndReceipt(ctx context.Context, txHash common.Hash) (*types.OriginalDataAndReceipt, error) {
var r *types.OriginalDataAndReceipt
err := ec.c.CallContext(ctx, &r, "eth_getTransactionDataAndReceipt", txHash)
if err == nil {
if r == nil {
return nil, ethereum.NotFound
}
}
return r, err
}

// TransactionReceipt returns the receipt of a transaction by transaction hash.
// Note that the receipt is not available for pending transactions.
func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
Expand Down
154 changes: 154 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,17 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
return newRPCTransaction(tx, common.Hash{}, 0, 0)
}

// newRPCTransactionsFromBlockIndex returns transactions that will serialize to the RPC representation.
func newRPCTransactionsFromBlockIndex(b *types.Block) []*RPCTransaction {
txs := b.Transactions()
result := make([]*RPCTransaction, 0, len(txs))

for idx, tx := range txs {
result = append(result, newRPCTransaction(tx, b.Hash(), b.NumberU64(), uint64(idx)))
}
return result
}

// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction {
txs := b.Transactions()
Expand Down Expand Up @@ -1227,6 +1238,14 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Co
return nil
}

// GetTransactionsByBlockNumber returns all the transactions for the given block number.
func (s *PublicTransactionPoolAPI) GetTransactionsByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) []*RPCTransaction {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil {
return newRPCTransactionsFromBlockIndex(block)
}
return nil
}

// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil {
Expand Down Expand Up @@ -1314,6 +1333,141 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context,
return rlp.EncodeToBytes(tx)
}

// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func (s *PublicTransactionPoolAPI) GetTransactionReceiptsByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) ([]map[string]interface{}, error) {
blockNumber := uint64(blockNr.Int64())
blockHash := rawdb.ReadCanonicalHash(s.b.ChainDb(), blockNumber)

receipts, err := s.b.GetReceipts(ctx, blockHash)
if err != nil {
return nil, err
}
block, err := s.b.BlockByHash(ctx, blockHash)
if err != nil {
return nil, err
}
txs := block.Transactions()
if len(txs) != len(receipts) {
return nil, fmt.Errorf("txs length doesn't equal to receipts' length")
}

txReceipts := make([]map[string]interface{}, 0, len(txs))
for idx, receipt := range receipts {
tx := txs[idx]
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": tx.Hash(),
"transactionIndex": hexutil.Uint64(idx),
"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
}

txReceipts = append(txReceipts, fields)
}

return txReceipts, nil
}

// GetTransactionDataAndReceipt returns the original transaction data and transaction receipt for the given transaction hash.
func (s *PublicTransactionPoolAPI) GetTransactionDataAndReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash)
if tx == nil {
return nil, nil
}
receipts, err := s.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)

rpcTransaction := newRPCTransaction(tx, blockHash, blockNumber, index)

txData := map[string]interface{}{
"blockHash": rpcTransaction.BlockHash.String(),
"blockNumber": rpcTransaction.BlockNumber.String(),
"from": rpcTransaction.From.String(),
"gas": rpcTransaction.Gas.String(),
"gasPrice": rpcTransaction.GasPrice.String(),
"hash": rpcTransaction.Hash.String(),
"input": rpcTransaction.Input.String(),
"nonce": rpcTransaction.Nonce.String(),
"to": rpcTransaction.To.String(),
"transactionIndex": rpcTransaction.TransactionIndex.String(),
"value": rpcTransaction.Value.String(),
"v": rpcTransaction.V.String(),
"r": rpcTransaction.R.String(),
"s": rpcTransaction.S.String(),
}

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
}
result := map[string]interface{}{
"txData": txData,
"receipt": fields,
}
return result, nil
}

// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash)
Expand Down
37 changes: 13 additions & 24 deletions internal/jsre/deps/bindata.go

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions internal/jsre/deps/web3.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0e2c471

Please sign in to comment.