From b664aee6218f2f9dba57c94b1e85910e847307da Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Thu, 29 Oct 2020 22:08:26 -0500 Subject: [PATCH] unit tests for GetTransactionCount, GetTransactionReceipt, GetBalance, and GetCode --- pkg/eth/api_test.go | 169 ++++++++++++++++++++++++++---- pkg/eth/backend.go | 7 +- pkg/eth/eth_call_test.go | 19 +++- pkg/eth/ipld_retriever.go | 11 +- pkg/eth/ipld_retriever_test.go | 156 --------------------------- pkg/eth/test_helpers/test_data.go | 7 +- 6 files changed, 186 insertions(+), 183 deletions(-) delete mode 100644 pkg/eth/ipld_retriever_test.go diff --git a/pkg/eth/api_test.go b/pkg/eth/api_test.go index ba632773..f7d052dd 100644 --- a/pkg/eth/api_test.go +++ b/pkg/eth/api_test.go @@ -20,18 +20,19 @@ import ( "context" "strconv" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth" "github.com/vulcanize/ipld-eth-indexer/pkg/postgres" + shared2 "github.com/vulcanize/ipld-eth-indexer/pkg/shared" "github.com/vulcanize/ipld-eth-server/pkg/eth" "github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers" @@ -39,6 +40,8 @@ import ( ) var ( + randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f") + randomHash = crypto.Keccak256Hash(randomAddr.Bytes()) number = rpc.BlockNumber(test_helpers.BlockNumber.Int64()) blockHash = test_helpers.MockBlock.Header().Hash() ctx = context.Background() @@ -128,6 +131,48 @@ var ( 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() { @@ -137,7 +182,8 @@ var _ = Describe("API", func() { 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 + It("", func() { var err error db, err = shared.SetupDB() Expect(err).ToNot(HaveOccurred()) @@ -147,6 +193,8 @@ var _ = Describe("API", func() { api = eth.NewPublicEthAPI(backend, nil) err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload) Expect(err).ToNot(HaveOccurred()) + err = publishCode(db, test_helpers.ContractCodeHash, test_helpers.ContractCode) + Expect(err).ToNot(HaveOccurred()) uncles := test_helpers.MockBlock.Uncles() uncleHashes := make([]common.Hash, len(uncles)) for i, uncle := range uncles { @@ -154,21 +202,13 @@ var _ = Describe("API", func() { } expectedBlock["uncles"] = uncleHashes }) - AfterEach(func() { - eth.TearDownDB(db) - }) + // Single test db tear down at end of all tests + defer It("", func() { eth.TearDownDB(db) }) /* Headers and blocks */ - Describe("GetHeaderByHash", func() { - It("Retrieves a header by hash", func() { - header := api.GetHeaderByHash(ctx, blockHash) - Expect(header).To(Equal(expectedHeader)) - }) - }) - Describe("GetHeaderByNumber", func() { It("Retrieves a header by number", func() { header, err := api.GetHeaderByNumber(ctx, number) @@ -298,8 +338,23 @@ var _ = Describe("API", func() { */ Describe("GetTransactionCount", func() { - It("Retrieves the number of transactions the given address has sent for the given block number or block hash", 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))) }) }) @@ -398,6 +453,10 @@ var _ = Describe("API", func() { 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("GetRawTransactionByHash", func() { @@ -417,6 +476,10 @@ var _ = Describe("API", func() { 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()) + }) }) /* @@ -427,7 +490,24 @@ var _ = Describe("API", func() { Describe("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()) }) }) @@ -809,21 +889,56 @@ var _ = Describe("API", func() { */ Describe("GetBalance", func() { - It("Retrieves the eth balance for the provided account address at the block with the provided hash or number", 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("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() { - + /* + val, err := api.GetStorageAt(ctx, test_helpers.ContractAddress, common.Bytes2Hex(test_helpers.StorageLeafKey), rpc.BlockNumberOrHashWithNumber(number)) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal((hexutil.Bytes)(test_helpers.StorageValue))) + */ }) }) Describe("GetCode", func() { - It("Retrieves the code for the provided contract address at the block with the provied hash or number", 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()) + }) + }) Describe("GetProof", func() { @@ -831,5 +946,21 @@ var _ = Describe("API", func() { }) }) - }) + +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() +} diff --git a/pkg/eth/backend.go b/pkg/eth/backend.go index f711b0cf..dc68cd32 100644 --- a/pkg/eth/backend.go +++ b/pkg/eth/backend.go @@ -67,7 +67,12 @@ const ( WHERE state_accounts.state_id = state_cids.id AND state_cids.header_id = header_cids.id AND state_leaf_key = $1 - AND block_hash = $2` + 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` ) diff --git a/pkg/eth/eth_call_test.go b/pkg/eth/eth_call_test.go index 0ad599df..c5360d16 100644 --- a/pkg/eth/eth_call_test.go +++ b/pkg/eth/eth_call_test.go @@ -89,7 +89,7 @@ var _ = Describe("eth_call", func() { api = eth.NewPublicEthAPI(backend, nil) // make the test blockchain (and state) - blocks, receipts, chain = test_helpers.MakeChain(4, test_helpers.Genesis, test_helpers.TestChainGen) + blocks, receipts, chain = test_helpers.MakeChain(5, test_helpers.Genesis, test_helpers.TestChainGen) pams = statediff.Params{ IntermediateStateNodes: true, IntermediateStorageNodes: true, @@ -146,7 +146,17 @@ var _ = Describe("eth_call", func() { To: &test_helpers.ContractAddr, Data: &bdata, } - res, err := api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(2), nil) + // Before contract deployment + 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)) @@ -160,6 +170,11 @@ var _ = Describe("eth_call", func() { 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)) }) }) }) diff --git a/pkg/eth/ipld_retriever.go b/pkg/eth/ipld_retriever.go index 4d00712f..a5c2a1aa 100644 --- a/pkg/eth/ipld_retriever.go +++ b/pkg/eth/ipld_retriever.go @@ -87,12 +87,19 @@ const ( WHERE state_cids.header_id = header_cids.id AND state_cids.mh_key = blocks.key AND state_leaf_key = $1 - AND block_hash = $2` + 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 FROM eth.state_cids, eth.header_cids, public.blocks WHERE state_cids.header_id = header_cids.id AND state_cids.mh_key = blocks.key AND state_leaf_key = $1 - AND block_number = $2` + AND block_number <= $2 + ORDER BY block_number DESC + LIMIT 1` ) type ipldResult struct { diff --git a/pkg/eth/ipld_retriever_test.go b/pkg/eth/ipld_retriever_test.go deleted file mode 100644 index 544b16d6..00000000 --- a/pkg/eth/ipld_retriever_test.go +++ /dev/null @@ -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 . - -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() { - - }) - }) - -}) -*/ diff --git a/pkg/eth/test_helpers/test_data.go b/pkg/eth/test_helpers/test_data.go index b90e43fd..c86854d7 100644 --- a/pkg/eth/test_helpers/test_data.go +++ b/pkg/eth/test_helpers/test_data.go @@ -259,7 +259,7 @@ var ( // statediff data storageLocation = common.HexToHash("0") StorageLeafKey = crypto.Keccak256Hash(storageLocation[:]).Bytes() - StorageValue = common.Hex2Bytes("01") + StorageValue = crypto.Keccak256([]byte{1, 2, 3, 4, 5}) StoragePartialPath = common.Hex2Bytes("20290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") StorageLeafNode, _ = rlp.EncodeToBytes([]interface{}{ StoragePartialPath, @@ -268,7 +268,7 @@ var ( nonce1 = uint64(1) ContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0" - ContractCodeHash = common.HexToHash("0x753f98a8d4328b15636e46f66f2cb4bc860100aa17967cc145fcd17d1d4710ea") + ContractCodeHash = crypto.Keccak256Hash(MockContractByteCode) contractPath = common.Bytes2Hex([]byte{'\x06'}) ContractLeafKey = testhelpers.AddressToLeafKey(ContractAddress) ContractAccount, _ = rlp.EncodeToBytes(state.Account{ @@ -284,13 +284,14 @@ var ( }) nonce0 = uint64(0) + AccountBalance = big.NewInt(1000) AccountRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" AccountCodeHash = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") AccountAddresss = common.HexToAddress("0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e") AccountLeafKey = testhelpers.Account2LeafKey Account, _ = rlp.EncodeToBytes(state.Account{ Nonce: nonce0, - Balance: big.NewInt(1000), + Balance: AccountBalance, CodeHash: AccountCodeHash.Bytes(), Root: common.HexToHash(AccountRoot), })