From e1026d52615877a3218e2b09da1b0b52aae72c51 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Thu, 29 Oct 2020 14:59:09 -0500 Subject: [PATCH] remaining block endpoint unit test; uncle endpoints unit tests --- pkg/eth/api.go | 71 ++++---- pkg/eth/api_test.go | 258 ++++++++++++++++++++++++++---- pkg/eth/test_helpers/test_data.go | 49 ++++-- 3 files changed, 304 insertions(+), 74 deletions(-) diff --git a/pkg/eth/api.go b/pkg/eth/api.go index 81a5606c..f6e62018 100644 --- a/pkg/eth/api.go +++ b/pkg/eth/api.go @@ -94,7 +94,7 @@ func (pea *PublicEthAPI) GetHeaderByNumber(ctx context.Context, number rpc.Block 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 { + if res, err := pea.rpcMarshalHeader(header); err == nil { return res } } @@ -158,6 +158,12 @@ func (pea *PublicEthAPI) GetBlockByHash(ctx context.Context, hash common.Hash, f 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) { @@ -382,11 +388,21 @@ 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) { tx, blockHash, blockNumber, index, err := pea.B.GetTransaction(ctx, hash) if err != nil { - if rct := pea.remoteGetTransactionReceipt(ctx, hash); rct != nil { - return rct, nil - } return nil, err } if tx == nil { @@ -394,9 +410,6 @@ func (pea *PublicEthAPI) GetTransactionReceipt(ctx context.Context, hash common. } receipts, err := pea.B.GetReceipts(ctx, blockHash) if err != nil { - if rct := pea.remoteGetTransactionReceipt(ctx, hash); rct != nil { - return rct, nil - } return nil, err } if len(receipts) <= int(index) { @@ -441,24 +454,22 @@ func (pea *PublicEthAPI) GetTransactionReceipt(ctx context.Context, hash common. } func (pea *PublicEthAPI) remoteGetTransactionReceipt(ctx context.Context, hash common.Hash) map[string]interface{} { - if pea.rpc != nil { - 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, - } + 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 @@ -835,11 +846,13 @@ func (pea *PublicEthAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx boo if err != nil { return nil, err } - td, err := pea.B.GetTd(b.Hash()) - if err != nil { - return nil, err + if inclTx { + td, err := pea.B.GetTd(b.Hash()) + if err != nil { + return nil, err + } + fields["totalDifficulty"] = (*hexutil.Big)(td) } - fields["totalDifficulty"] = (*hexutil.Big)(td) return fields, err } diff --git a/pkg/eth/api_test.go b/pkg/eth/api_test.go index 8211ebf2..69066980 100644 --- a/pkg/eth/api_test.go +++ b/pkg/eth/api_test.go @@ -77,6 +77,46 @@ var ( "receiptsRoot": test_helpers.MockBlock.Header().ReceiptHash, "totalDifficulty": (*hexutil.Big)(test_helpers.MockBlock.Header().Difficulty), } + expectedUncle1 = map[string]interface{}{ + "number": (*hexutil.Big)(test_helpers.MockUncles[0].Number), + "hash": test_helpers.MockUncles[0].Hash(), + "parentHash": test_helpers.MockUncles[0].ParentHash, + "nonce": test_helpers.MockUncles[0].Nonce, + "mixHash": test_helpers.MockUncles[0].MixDigest, + "sha3Uncles": test_helpers.MockUncles[0].UncleHash, + "logsBloom": test_helpers.MockUncles[0].Bloom, + "stateRoot": test_helpers.MockUncles[0].Root, + "miner": test_helpers.MockUncles[0].Coinbase, + "difficulty": (*hexutil.Big)(test_helpers.MockUncles[0].Difficulty), + "extraData": hexutil.Bytes(test_helpers.MockUncles[0].Extra), + "size": hexutil.Uint64(types.NewBlockWithHeader(test_helpers.MockUncles[0]).Size()), + "gasLimit": hexutil.Uint64(test_helpers.MockUncles[0].GasLimit), + "gasUsed": hexutil.Uint64(test_helpers.MockUncles[0].GasUsed), + "timestamp": hexutil.Uint64(test_helpers.MockUncles[0].Time), + "transactionsRoot": test_helpers.MockUncles[0].TxHash, + "receiptsRoot": test_helpers.MockUncles[0].ReceiptHash, + "uncles": []common.Hash{}, + } + expectedUncle2 = map[string]interface{}{ + "number": (*hexutil.Big)(test_helpers.MockUncles[1].Number), + "hash": test_helpers.MockUncles[1].Hash(), + "parentHash": test_helpers.MockUncles[1].ParentHash, + "nonce": test_helpers.MockUncles[1].Nonce, + "mixHash": test_helpers.MockUncles[1].MixDigest, + "sha3Uncles": test_helpers.MockUncles[1].UncleHash, + "logsBloom": test_helpers.MockUncles[1].Bloom, + "stateRoot": test_helpers.MockUncles[1].Root, + "miner": test_helpers.MockUncles[1].Coinbase, + "difficulty": (*hexutil.Big)(test_helpers.MockUncles[1].Difficulty), + "extraData": hexutil.Bytes(test_helpers.MockUncles[1].Extra), + "size": hexutil.Uint64(types.NewBlockWithHeader(test_helpers.MockUncles[1]).Size()), + "gasLimit": hexutil.Uint64(test_helpers.MockUncles[1].GasLimit), + "gasUsed": hexutil.Uint64(test_helpers.MockUncles[1].GasUsed), + "timestamp": hexutil.Uint64(test_helpers.MockUncles[1].Time), + "transactionsRoot": test_helpers.MockUncles[1].TxHash, + "receiptsRoot": test_helpers.MockUncles[1].ReceiptHash, + "uncles": []common.Hash{}, + } expectedTransaction = eth.NewRPCTransaction(test_helpers.MockTransactions[0], test_helpers.MockBlock.Hash(), test_helpers.MockBlock.NumberU64(), 0) ) @@ -107,6 +147,40 @@ var _ = Describe("API", func() { AfterEach(func() { eth.TearDownDB(db) }) + /* + + Headers and blocks + + */ + Describe("GetHeaderByHash", func() { + It("Retrieves a header by hash", func() { + hash := test_helpers.MockBlock.Header().Hash() + header := api.GetHeaderByHash(context.Background(), hash) + Expect(header).To(Equal(expectedHeader)) + }) + }) + + Describe("GetHeaderByNumber", func() { + It("Retrieves a header by number", func() { + number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64) + Expect(err).ToNot(HaveOccurred()) + header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number)) + Expect(err).ToNot(HaveOccurred()) + Expect(header).To(Equal(expectedHeader)) + }) + + It("Throws an error if a header cannot be found", func() { + number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64) + Expect(err).ToNot(HaveOccurred()) + header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number+1)) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("sql: no rows in result set")) + Expect(header).To(BeNil()) + _, err = api.B.DB.Beginx() + Expect(err).ToNot(HaveOccurred()) + }) + }) + Describe("BlockNumber", func() { It("Retrieves the head block number", func() { bn := api.BlockNumber() @@ -116,15 +190,6 @@ var _ = Describe("API", func() { }) }) - Describe("GetTransactionByHash", func() { - It("Retrieves a transaction by hash", func() { - hash := test_helpers.MockTransactions[0].Hash() - tx, err := api.GetTransactionByHash(context.Background(), hash) - Expect(err).ToNot(HaveOccurred()) - Expect(tx).To(Equal(expectedTransaction)) - }) - }) - Describe("GetBlockByNumber", func() { It("Retrieves a block by number", func() { // without full txs @@ -154,27 +219,6 @@ var _ = Describe("API", func() { }) }) - Describe("GetHeaderByNumber", func() { - It("Retrieves a header by number", func() { - number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64) - Expect(err).ToNot(HaveOccurred()) - header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number)) - Expect(err).ToNot(HaveOccurred()) - Expect(header).To(Equal(expectedHeader)) - }) - - It("Throws an error if a header cannot be found", func() { - number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64) - Expect(err).ToNot(HaveOccurred()) - header, err := api.GetHeaderByNumber(context.Background(), rpc.BlockNumber(number+1)) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("sql: no rows in result set")) - Expect(header).To(BeNil()) - _, err = api.B.DB.Beginx() - Expect(err).ToNot(HaveOccurred()) - }) - }) - Describe("GetBlockByHash", func() { It("Retrieves a block by hash", func() { // without full txs @@ -202,6 +246,129 @@ var _ = Describe("API", func() { }) }) + /* + + Uncles + + */ + + Describe("GetUncleByBlockNumberAndIndex", func() { + It("Retrieves the uncle at the provided index in the canoncial block with the provided hash", func() { + number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64) + Expect(err).ToNot(HaveOccurred()) + uncle1, err := api.GetUncleByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(number), 0) + Expect(err).ToNot(HaveOccurred()) + Expect(uncle1).To(Equal(expectedUncle1)) + uncle2, err := api.GetUncleByBlockNumberAndIndex(context.Background(), rpc.BlockNumber(number), 1) + Expect(err).ToNot(HaveOccurred()) + Expect(uncle2).To(Equal(expectedUncle2)) + }) + }) + + Describe("GetUncleByBlockHashAndIndex", func() { + It("Retrieves the uncle at the provided index in the block with the provided hash", func() { + hash := test_helpers.MockBlock.Header().Hash() + uncle1, err := api.GetUncleByBlockHashAndIndex(context.Background(), hash, 0) + Expect(err).ToNot(HaveOccurred()) + Expect(uncle1).To(Equal(expectedUncle1)) + uncle2, err := api.GetUncleByBlockHashAndIndex(context.Background(), hash, 1) + Expect(err).ToNot(HaveOccurred()) + Expect(uncle2).To(Equal(expectedUncle2)) + }) + }) + + Describe("GetUncleCountByBlockNumber", func() { + It("Retrieves the number of uncles for the canonical block with the provided number", func() { + number, err := strconv.ParseInt(test_helpers.BlockNumber.String(), 10, 64) + Expect(err).ToNot(HaveOccurred()) + count := api.GetUncleCountByBlockNumber(context.Background(), rpc.BlockNumber(number)) + Expect(uint64(*count)).To(Equal(uint64(2))) + }) + }) + + Describe("GetUncleCountByBlockHash", func() { + It("Retrieves the number of uncles for the block with the provided hash", func() { + hash := test_helpers.MockBlock.Header().Hash() + count := api.GetUncleCountByBlockHash(context.Background(), hash) + Expect(uint64(*count)).To(Equal(uint64(2))) + }) + }) + + /* + + Transactions + + */ + + Describe("GetTransactionCount", func() { + It("Retrieves the number of transactions the given address has sent for the given block number or block hash", func() { + + }) + }) + + Describe("GetBlockTransactionCountByNumber", func() { + It("Retrieves the number of transactions in the canonical block with the provided number", func() { + + }) + }) + + Describe("GetBlockTransactionCountByHash", func() { + It("Retrieves the number of transactions in the block with the provided hash ", func() { + + }) + }) + + Describe("GetTransactionByBlockNumberAndIndex", func() { + It("Retrieves the tx with the provided index in the canonical block with the provided block number", func() { + + }) + }) + + Describe("GetTransactionByBlockHashAndIndex", func() { + It("Retrieves the tx with the provided index in the block with the provided hash", func() { + + }) + }) + + Describe("GetRawTransactionByBlockNumberAndIndex", func() { + It("Retrieves the raw tx with the provided index in the canonical block with the provided block number", func() { + + }) + }) + + Describe("GetRawTransactionByBlockHashAndIndex", func() { + It("Retrieves the raw tx with the provided index in the block with the provided hash", func() { + + }) + }) + + Describe("GetTransactionByHash", func() { + It("Retrieves a transaction by hash", func() { + hash := test_helpers.MockTransactions[0].Hash() + tx, err := api.GetTransactionByHash(context.Background(), hash) + Expect(err).ToNot(HaveOccurred()) + Expect(tx).To(Equal(expectedTransaction)) + }) + }) + + Describe("GetRawTransactionByHash", func() { + It("Retrieves a raw transaction by hash", func() { + + }) + }) + + /* + + Receipts and logs + + */ + + Describe("GetTransactionReceipt", func() { + It("Retrieves a receipt by tx hash", func() { + + }) + }) + Describe("GetLogs", func() { It("Retrieves receipt logs that match the provided topics within the provided range", func() { crit := ethereum.FilterQuery{ @@ -572,4 +739,35 @@ var _ = Describe("API", func() { Expect(logs).To(Equal([]*types.Log{test_helpers.MockLog1, test_helpers.MockLog2})) }) }) + + /* + + State and storage + + */ + + Describe("GetBalance", func() { + It("Retrieves the eth balance for the provided account address at the block with the provided hash or number", func() { + + }) + }) + + Describe("GetStorageAt", func() { + It("Retrieves the storage value at the provided contract address and storage leaf key at the block with the provided hash or number", func() { + + }) + }) + + Describe("GetCode", func() { + It("Retrieves the code for the provided contract address at the block with the provied hash or number", func() { + + }) + }) + + Describe("GetProof", func() { + It("Retrieves the Merkle-proof for a given account and optionally some storage keys at the block with the provided hash or number", func() { + + }) + }) + }) diff --git a/pkg/eth/test_helpers/test_data.go b/pkg/eth/test_helpers/test_data.go index 08f57c34..b90e43fd 100644 --- a/pkg/eth/test_helpers/test_data.go +++ b/pkg/eth/test_helpers/test_data.go @@ -57,19 +57,39 @@ var ( Extra: []byte{}, } MockTransactions, MockReceipts, SenderAddr = createTransactionsAndReceipts() - ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts) - MockBlock = types.NewBlock(&MockHeader, MockTransactions, nil, 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{ + MockUncles = []*types.Header{ + { + Time: 1, + Number: new(big.Int).Add(BlockNumber, big.NewInt(1)), + Root: common.HexToHash("0x1"), + TxHash: common.HexToHash("0x1"), + ReceiptHash: common.HexToHash("0x1"), + Difficulty: big.NewInt(500001), + Extra: []byte{}, + }, + { + Time: 2, + Number: new(big.Int).Add(BlockNumber, big.NewInt(2)), + Root: common.HexToHash("0x2"), + TxHash: common.HexToHash("0x2"), + ReceiptHash: common.HexToHash("0x2"), + Difficulty: big.NewInt(500002), + Extra: []byte{}, + }, + } + ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts) + MockBlock = types.NewBlock(&MockHeader, MockTransactions, 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, Topics: []common.Hash{mockTopic11, mockTopic12}, Data: []byte{}, @@ -266,7 +286,6 @@ var ( nonce0 = uint64(0) AccountRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" AccountCodeHash = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") - accountPath = common.Bytes2Hex([]byte{'\x0c'}) AccountAddresss = common.HexToAddress("0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e") AccountLeafKey = testhelpers.Account2LeafKey Account, _ = rlp.EncodeToBytes(state.Account{ @@ -341,7 +360,7 @@ var ( CID: HeaderCID.String(), MhKey: HeaderMhKey, TotalDifficulty: MockBlock.Difficulty().String(), - Reward: "5000000000000000000", + Reward: "5312500000000000000", StateRoot: MockBlock.Root().String(), RctRoot: MockBlock.ReceiptHash().String(), TxRoot: MockBlock.TxHash().String(),