diff --git a/cmd/coldImport.go b/cmd/coldImport.go index 134d47ca..ddd1048b 100644 --- a/cmd/coldImport.go +++ b/cmd/coldImport.go @@ -84,7 +84,7 @@ func coldImport() { // init cold importer deps blockRepository := repositories.NewBlockRepository(&pgDB) - receiptRepository := repositories.ReceiptRepository{DB: &pgDB} + receiptRepository := repositories.FullSyncReceiptRepository{DB: &pgDB} transactionConverter := cold_db.NewColdDbTransactionConverter() blockConverter := vulcCommon.NewBlockConverter(transactionConverter) diff --git a/cmd/compose.go b/cmd/compose.go index 77e0e7ad..f92db41a 100644 --- a/cmd/compose.go +++ b/cmd/compose.go @@ -1,17 +1,18 @@ -// Copyright © 2019 Vulcanize, Inc -// +// 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 . +// along with this program. If not, see . package cmd diff --git a/cmd/composeAndExecute.go b/cmd/composeAndExecute.go index 680bb0bb..d3e0e0b9 100644 --- a/cmd/composeAndExecute.go +++ b/cmd/composeAndExecute.go @@ -1,17 +1,18 @@ -// Copyright © 2019 Vulcanize, Inc -// +// 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 . +// along with this program. If not, see . package cmd diff --git a/cmd/execute.go b/cmd/execute.go index 337d306c..c6d7bb15 100644 --- a/cmd/execute.go +++ b/cmd/execute.go @@ -1,17 +1,18 @@ -// Copyright © 2019 Vulcanize, Inc -// +// 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 . +// along with this program. If not, see . package cmd diff --git a/db/migrations/00013_add_address_table.sql b/db/migrations/00013_add_address_table.sql new file mode 100644 index 00000000..22ee7b3e --- /dev/null +++ b/db/migrations/00013_add_address_table.sql @@ -0,0 +1,10 @@ +-- +goose Up +CREATE TABLE public.addresses +( + id SERIAL PRIMARY KEY, + address character varying(42), + UNIQUE (address) +); + +-- +goose Down +DROP TABLE public.addresses; \ No newline at end of file diff --git a/db/migrations/00013_create_receipts_table.sql b/db/migrations/00013_create_receipts_table.sql deleted file mode 100644 index 2864f226..00000000 --- a/db/migrations/00013_create_receipts_table.sql +++ /dev/null @@ -1,16 +0,0 @@ --- +goose Up -CREATE TABLE full_sync_receipts -( - id SERIAL PRIMARY KEY, - transaction_id INTEGER NOT NULL REFERENCES full_sync_transactions (id) ON DELETE CASCADE, - contract_address VARCHAR(42), - cumulative_gas_used NUMERIC, - gas_used NUMERIC, - state_root VARCHAR(66), - status INTEGER, - tx_hash VARCHAR(66) -); - - --- +goose Down -DROP TABLE full_sync_receipts; diff --git a/db/migrations/00014_create_receipts_table.sql b/db/migrations/00014_create_receipts_table.sql new file mode 100644 index 00000000..b39e4492 --- /dev/null +++ b/db/migrations/00014_create_receipts_table.sql @@ -0,0 +1,16 @@ +-- +goose Up +CREATE TABLE full_sync_receipts +( + id SERIAL PRIMARY KEY, + transaction_id INTEGER NOT NULL REFERENCES full_sync_transactions (id) ON DELETE CASCADE, + contract_address_id INTEGER NOT NULL REFERENCES addresses (id) ON DELETE CASCADE, + cumulative_gas_used NUMERIC, + gas_used NUMERIC, + state_root VARCHAR(66), + status INTEGER, + tx_hash VARCHAR(66) +); + + +-- +goose Down +DROP TABLE full_sync_receipts; diff --git a/db/migrations/00014_add_transaction_id_index_to_receipts.sql b/db/migrations/00015_add_transaction_id_index_to_receipts.sql similarity index 100% rename from db/migrations/00014_add_transaction_id_index_to_receipts.sql rename to db/migrations/00015_add_transaction_id_index_to_receipts.sql diff --git a/db/migrations/00015_add_receipts_fk_to_logs.sql b/db/migrations/00016_add_receipts_fk_to_logs.sql similarity index 100% rename from db/migrations/00015_add_receipts_fk_to_logs.sql rename to db/migrations/00016_add_receipts_fk_to_logs.sql diff --git a/db/migrations/00016_create_log_filters.sql b/db/migrations/00017_create_log_filters.sql similarity index 100% rename from db/migrations/00016_create_log_filters.sql rename to db/migrations/00017_create_log_filters.sql diff --git a/db/migrations/00017_create_watched_event_logs.sql b/db/migrations/00018_create_watched_event_logs.sql similarity index 100% rename from db/migrations/00017_create_watched_event_logs.sql rename to db/migrations/00018_create_watched_event_logs.sql diff --git a/db/migrations/00018_update_log_filters_to_block_constraint.sql b/db/migrations/00019_update_log_filters_to_block_constraint.sql similarity index 100% rename from db/migrations/00018_update_log_filters_to_block_constraint.sql rename to db/migrations/00019_update_log_filters_to_block_constraint.sql diff --git a/db/migrations/00019_rename_node_table.sql b/db/migrations/00020_rename_node_table.sql similarity index 100% rename from db/migrations/00019_rename_node_table.sql rename to db/migrations/00020_rename_node_table.sql diff --git a/db/migrations/00020_associate_receipts_with_blocks.sql b/db/migrations/00021_associate_receipts_with_blocks.sql similarity index 100% rename from db/migrations/00020_associate_receipts_with_blocks.sql rename to db/migrations/00021_associate_receipts_with_blocks.sql diff --git a/db/migrations/00021_add_eth_node_fingerprint_to_blocks.sql b/db/migrations/00022_add_eth_node_fingerprint_to_blocks.sql similarity index 100% rename from db/migrations/00021_add_eth_node_fingerprint_to_blocks.sql rename to db/migrations/00022_add_eth_node_fingerprint_to_blocks.sql diff --git a/db/migrations/00022_create_headers_table.sql b/db/migrations/00023_create_headers_table.sql similarity index 100% rename from db/migrations/00022_create_headers_table.sql rename to db/migrations/00023_create_headers_table.sql diff --git a/db/migrations/00023_create_checked_headers_table.sql b/db/migrations/00024_create_checked_headers_table.sql similarity index 100% rename from db/migrations/00023_create_checked_headers_table.sql rename to db/migrations/00024_create_checked_headers_table.sql diff --git a/db/migrations/00024_create_queued_storage.sql b/db/migrations/00025_create_queued_storage.sql similarity index 100% rename from db/migrations/00024_create_queued_storage.sql rename to db/migrations/00025_create_queued_storage.sql diff --git a/db/migrations/00026_create_header_sync_receipts_table.sql b/db/migrations/00026_create_header_sync_receipts_table.sql deleted file mode 100644 index ebd0a17b..00000000 --- a/db/migrations/00026_create_header_sync_receipts_table.sql +++ /dev/null @@ -1,18 +0,0 @@ --- +goose Up -CREATE TABLE header_sync_receipts( - id SERIAL PRIMARY KEY, - transaction_id INTEGER NOT NULL REFERENCES header_sync_transactions(id) ON DELETE CASCADE, - header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE, - contract_address VARCHAR(42), - cumulative_gas_used NUMERIC, - gas_used NUMERIC, - state_root VARCHAR(66), - status INTEGER, - tx_hash VARCHAR(66), - rlp BYTEA, - UNIQUE(header_id, transaction_id) -); - - --- +goose Down -DROP TABLE header_sync_receipts; diff --git a/db/migrations/00025_create_header_sync_transactions_table.sql b/db/migrations/00026_create_header_sync_transactions_table.sql similarity index 100% rename from db/migrations/00025_create_header_sync_transactions_table.sql rename to db/migrations/00026_create_header_sync_transactions_table.sql diff --git a/db/migrations/00027_create_header_sync_receipts_table.sql b/db/migrations/00027_create_header_sync_receipts_table.sql new file mode 100644 index 00000000..842527f4 --- /dev/null +++ b/db/migrations/00027_create_header_sync_receipts_table.sql @@ -0,0 +1,19 @@ +-- +goose Up +CREATE TABLE header_sync_receipts +( + id SERIAL PRIMARY KEY, + transaction_id INTEGER NOT NULL REFERENCES header_sync_transactions (id) ON DELETE CASCADE, + header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, + contract_address_id INTEGER NOT NULL REFERENCES addresses (id) ON DELETE CASCADE, + cumulative_gas_used NUMERIC, + gas_used NUMERIC, + state_root VARCHAR(66), + status INTEGER, + tx_hash VARCHAR(66), + rlp BYTEA, + UNIQUE (header_id, transaction_id) +); + + +-- +goose Down +DROP TABLE header_sync_receipts; diff --git a/db/migrations/00027_create_uncles_table.sql b/db/migrations/00028_create_uncles_table.sql similarity index 100% rename from db/migrations/00027_create_uncles_table.sql rename to db/migrations/00028_create_uncles_table.sql diff --git a/db/schema.sql b/db/schema.sql index a9fd68af..5470a107 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -20,6 +20,36 @@ SET default_tablespace = ''; SET default_with_oids = false; +-- +-- Name: addresses; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addresses ( + id integer NOT NULL, + address character varying(42) +); + + +-- +-- Name: addresses_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.addresses_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: addresses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.addresses_id_seq OWNED BY public.addresses.id; + + -- -- Name: logs; Type: TABLE; Schema: public; Owner: - -- @@ -144,7 +174,7 @@ CREATE TABLE public.eth_nodes ( CREATE TABLE public.full_sync_receipts ( id integer NOT NULL, - contract_address character varying(42), + contract_address_id integer NOT NULL, cumulative_gas_used numeric, gas_used numeric, state_root character varying(66), @@ -254,7 +284,7 @@ CREATE TABLE public.header_sync_receipts ( id integer NOT NULL, transaction_id integer NOT NULL, header_id integer NOT NULL, - contract_address character varying(42), + contract_address_id integer NOT NULL, cumulative_gas_used numeric, gas_used numeric, state_root character varying(66), @@ -564,6 +594,13 @@ CREATE VIEW public.watched_event_logs AS WHERE ((((log_filters.topic0)::text = (logs.topic0)::text) OR (log_filters.topic0 IS NULL)) AND (((log_filters.topic1)::text = (logs.topic1)::text) OR (log_filters.topic1 IS NULL)) AND (((log_filters.topic2)::text = (logs.topic2)::text) OR (log_filters.topic2 IS NULL)) AND (((log_filters.topic3)::text = (logs.topic3)::text) OR (log_filters.topic3 IS NULL))); +-- +-- Name: addresses id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addresses ALTER COLUMN id SET DEFAULT nextval('public.addresses_id_seq'::regclass); + + -- -- Name: blocks id; Type: DEFAULT; Schema: public; Owner: - -- @@ -662,6 +699,22 @@ ALTER TABLE ONLY public.uncles ALTER COLUMN id SET DEFAULT nextval('public.uncle ALTER TABLE ONLY public.watched_contracts ALTER COLUMN contract_id SET DEFAULT nextval('public.watched_contracts_contract_id_seq'::regclass); +-- +-- Name: addresses addresses_address_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addresses + ADD CONSTRAINT addresses_address_key UNIQUE (address); + + +-- +-- Name: addresses addresses_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addresses + ADD CONSTRAINT addresses_pkey PRIMARY KEY (id); + + -- -- Name: blocks blocks_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -896,6 +949,14 @@ ALTER TABLE ONLY public.checked_headers ADD CONSTRAINT checked_headers_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; +-- +-- Name: full_sync_receipts full_sync_receipts_contract_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.full_sync_receipts + ADD CONSTRAINT full_sync_receipts_contract_address_id_fkey FOREIGN KEY (contract_address_id) REFERENCES public.addresses(id) ON DELETE CASCADE; + + -- -- Name: full_sync_transactions full_sync_transactions_block_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -904,6 +965,14 @@ ALTER TABLE ONLY public.full_sync_transactions ADD CONSTRAINT full_sync_transactions_block_id_fkey FOREIGN KEY (block_id) REFERENCES public.blocks(id) ON DELETE CASCADE; +-- +-- Name: header_sync_receipts header_sync_receipts_contract_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.header_sync_receipts + ADD CONSTRAINT header_sync_receipts_contract_address_id_fkey FOREIGN KEY (contract_address_id) REFERENCES public.addresses(id) ON DELETE CASCADE; + + -- -- Name: header_sync_receipts header_sync_receipts_header_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- diff --git a/integration_test/contract_watcher_full_transformer_test.go b/integration_test/contract_watcher_full_transformer_test.go index e972d067..4e0cce01 100644 --- a/integration_test/contract_watcher_full_transformer_test.go +++ b/integration_test/contract_watcher_full_transformer_test.go @@ -1,3 +1,19 @@ +// 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 integration import ( diff --git a/integration_test/contract_watcher_header_sync_transformer_test.go b/integration_test/contract_watcher_header_sync_transformer_test.go index 1c8507f2..3b127e84 100644 --- a/integration_test/contract_watcher_header_sync_transformer_test.go +++ b/integration_test/contract_watcher_header_sync_transformer_test.go @@ -1,3 +1,19 @@ +// 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 integration import ( diff --git a/pkg/contract_watcher/full/retriever/block_retriever.go b/pkg/contract_watcher/full/retriever/block_retriever.go index 2f595cb2..80f330ad 100644 --- a/pkg/contract_watcher/full/retriever/block_retriever.go +++ b/pkg/contract_watcher/full/retriever/block_retriever.go @@ -17,7 +17,9 @@ package retriever import ( + "database/sql" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" ) // Block retriever is used to retrieve the first block for a given contract and the most recent block @@ -41,7 +43,10 @@ func NewBlockRetriever(db *postgres.DB) (r *blockRetriever) { func (r *blockRetriever) RetrieveFirstBlock(contractAddr string) (int64, error) { i, err := r.retrieveFirstBlockFromReceipts(contractAddr) if err != nil { - i, err = r.retrieveFirstBlockFromLogs(contractAddr) + if err == sql.ErrNoRows { + i, err = r.retrieveFirstBlockFromLogs(contractAddr) + } + return i, err } return i, err @@ -49,18 +54,26 @@ func (r *blockRetriever) RetrieveFirstBlock(contractAddr string) (int64, error) // For some contracts the contract creation transaction receipt doesn't have the contract address so this doesn't work (e.g. Sai) func (r *blockRetriever) retrieveFirstBlockFromReceipts(contractAddr string) (int64, error) { - var firstBlock int + var firstBlock int64 + addressId, getAddressErr := addressRepository().GetOrCreateAddress(r.db, contractAddr) + if getAddressErr != nil { + return firstBlock, getAddressErr + } err := r.db.Get( &firstBlock, `SELECT number FROM blocks WHERE id = (SELECT block_id FROM full_sync_receipts - WHERE lower(contract_address) = $1 + WHERE contract_address_id = $1 ORDER BY block_id ASC LIMIT 1)`, - contractAddr, + addressId, ) - return int64(firstBlock), err + return firstBlock, err +} + +func addressRepository() repositories.AddressRepository { + return repositories.AddressRepository{} } // In which case this servers as a heuristic to find the first block by finding the first contract event log diff --git a/pkg/contract_watcher/shared/helpers/test_helpers/database.go b/pkg/contract_watcher/shared/helpers/test_helpers/database.go index fb3f2e70..294a74c6 100644 --- a/pkg/contract_watcher/shared/helpers/test_helpers/database.go +++ b/pkg/contract_watcher/shared/helpers/test_helpers/database.go @@ -137,7 +137,7 @@ func SetupTusdRepo(vulcanizeLogId *int64, wantedEvents, wantedMethods []string) }, core.Node{}) Expect(err).NotTo(HaveOccurred()) - receiptRepository := repositories.ReceiptRepository{DB: db} + receiptRepository := repositories.FullSyncReceiptRepository{DB: db} logRepository := repositories.LogRepository{DB: db} blockRepository := *repositories.NewBlockRepository(db) @@ -183,7 +183,7 @@ func SetupENSRepo(vulcanizeLogId *int64, wantedEvents, wantedMethods []string) ( }, core.Node{}) Expect(err).NotTo(HaveOccurred()) - receiptRepository := repositories.ReceiptRepository{DB: db} + receiptRepository := repositories.FullSyncReceiptRepository{DB: db} logRepository := repositories.LogRepository{DB: db} blockRepository := *repositories.NewBlockRepository(db) @@ -225,6 +225,9 @@ func TearDown(db *postgres.DB) { tx, err := db.Beginx() Expect(err).NotTo(HaveOccurred()) + _, err = tx.Exec(`DELETE FROM addresses`) + Expect(err).NotTo(HaveOccurred()) + _, err = tx.Exec(`DELETE FROM blocks`) Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/datastore/postgres/repositories/address_repository.go b/pkg/datastore/postgres/repositories/address_repository.go new file mode 100644 index 00000000..7cf709d2 --- /dev/null +++ b/pkg/datastore/postgres/repositories/address_repository.go @@ -0,0 +1,62 @@ +// 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 repositories + +import ( + "database/sql" + + "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" + + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" +) + +type AddressRepository struct{} + +func (AddressRepository) GetOrCreateAddress(db *postgres.DB, address string) (int64, error) { + stringAddressToCommonAddress := common.HexToAddress(address) + hexAddress := stringAddressToCommonAddress.Hex() + + var addressId int64 + getErr := db.Get(&addressId, `SELECT id FROM public.addresses WHERE address = $1`, hexAddress) + if getErr == sql.ErrNoRows { + insertErr := db.QueryRow(`INSERT INTO public.addresses (address) VALUES($1) RETURNING id`, hexAddress).Scan(&addressId) + return addressId, insertErr + } + + return addressId, getErr +} + +func (AddressRepository) GetOrCreateAddressInTransaction(tx *sqlx.Tx, address string) (int64, error) { + stringAddressToCommonAddress := common.HexToAddress(address) + hexAddress := stringAddressToCommonAddress.Hex() + + var addressId int64 + getErr := tx.Get(&addressId, `SELECT id FROM public.addresses WHERE address = $1`, hexAddress) + if getErr == sql.ErrNoRows { + insertErr := tx.QueryRow(`INSERT INTO public.addresses (address) VALUES($1) RETURNING id`, hexAddress).Scan(&addressId) + return addressId, insertErr + } + + return addressId, getErr +} + +func (AddressRepository) GetAddressById(db *postgres.DB, id int64) (string, error) { + var address string + getErr := db.Get(&address, `SELECT address FROM public.addresses WHERE id = $1`, id) + return address, getErr +} diff --git a/pkg/datastore/postgres/repositories/address_repository_test.go b/pkg/datastore/postgres/repositories/address_repository_test.go new file mode 100644 index 00000000..1b2b788f --- /dev/null +++ b/pkg/datastore/postgres/repositories/address_repository_test.go @@ -0,0 +1,181 @@ +// 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 repositories_test + +import ( + "strings" + + "github.com/jmoiron/sqlx" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("address lookup", func() { + var ( + db *postgres.DB + repo repositories.AddressRepository + address = fakes.FakeAddress.Hex() + ) + BeforeEach(func() { + db = test_config.NewTestDB(test_config.NewTestNode()) + test_config.CleanTestDB(db) + repo = repositories.AddressRepository{} + }) + + AfterEach(func() { + test_config.CleanTestDB(db) + }) + + type dbAddress struct { + Id int64 + Address string + } + + Describe("GetOrCreateAddress", func() { + It("creates an address record", func() { + addressId, createErr := repo.GetOrCreateAddress(db, address) + Expect(createErr).NotTo(HaveOccurred()) + + var actualAddress dbAddress + getErr := db.Get(&actualAddress, `SELECT id, address FROM public.addresses LIMIT 1`) + Expect(getErr).NotTo(HaveOccurred()) + expectedAddress := dbAddress{Id: addressId, Address: address} + Expect(actualAddress).To(Equal(expectedAddress)) + }) + + It("returns the existing record id if the address already exists", func() { + createId, createErr := repo.GetOrCreateAddress(db, address) + Expect(createErr).NotTo(HaveOccurred()) + + getId, getErr := repo.GetOrCreateAddress(db, address) + Expect(getErr).NotTo(HaveOccurred()) + + var addressCount int + addressErr := db.Get(&addressCount, `SELECT count(*) FROM public.addresses`) + Expect(addressErr).NotTo(HaveOccurred()) + Expect(addressCount).To(Equal(1)) + Expect(createId).To(Equal(getId)) + }) + + It("gets upper-cased addresses", func() { + upperAddress := strings.ToUpper(address) + upperAddressId, createErr := repo.GetOrCreateAddress(db, upperAddress) + Expect(createErr).NotTo(HaveOccurred()) + + mixedCaseAddressId, getErr := repo.GetOrCreateAddress(db, address) + Expect(getErr).NotTo(HaveOccurred()) + Expect(upperAddressId).To(Equal(mixedCaseAddressId)) + }) + + It("gets lower-cased addresses", func() { + lowerAddress := strings.ToLower(address) + upperAddressId, createErr := repo.GetOrCreateAddress(db, lowerAddress) + Expect(createErr).NotTo(HaveOccurred()) + + mixedCaseAddressId, getErr := repo.GetOrCreateAddress(db, address) + Expect(getErr).NotTo(HaveOccurred()) + Expect(upperAddressId).To(Equal(mixedCaseAddressId)) + }) + }) + + Describe("GetOrCreateAddressInTransaction", func() { + var ( + tx *sqlx.Tx + txErr error + ) + BeforeEach(func() { + tx, txErr = db.Beginx() + Expect(txErr).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + tx.Rollback() + }) + + It("creates an address record", func() { + addressId, createErr := repo.GetOrCreateAddressInTransaction(tx, address) + Expect(createErr).NotTo(HaveOccurred()) + commitErr := tx.Commit() + Expect(commitErr).NotTo(HaveOccurred()) + + var actualAddress dbAddress + getErr := db.Get(&actualAddress, `SELECT id, address FROM public.addresses LIMIT 1`) + Expect(getErr).NotTo(HaveOccurred()) + expectedAddress := dbAddress{Id: addressId, Address: address} + Expect(actualAddress).To(Equal(expectedAddress)) + }) + + It("returns the existing record id if the address already exists", func() { + _, createErr := repo.GetOrCreateAddressInTransaction(tx, address) + Expect(createErr).NotTo(HaveOccurred()) + + _, getErr := repo.GetOrCreateAddressInTransaction(tx, address) + Expect(getErr).NotTo(HaveOccurred()) + tx.Commit() + + var addressCount int + addressErr := db.Get(&addressCount, `SELECT count(*) FROM public.addresses`) + Expect(addressErr).NotTo(HaveOccurred()) + }) + + It("gets upper-cased addresses", func() { + upperAddress := strings.ToUpper(address) + upperAddressId, createErr := repo.GetOrCreateAddressInTransaction(tx, upperAddress) + Expect(createErr).NotTo(HaveOccurred()) + + mixedCaseAddressId, getErr := repo.GetOrCreateAddressInTransaction(tx, address) + Expect(getErr).NotTo(HaveOccurred()) + tx.Commit() + + Expect(upperAddressId).To(Equal(mixedCaseAddressId)) + }) + + It("gets lower-cased addresses", func() { + lowerAddress := strings.ToLower(address) + upperAddressId, createErr := repo.GetOrCreateAddressInTransaction(tx, lowerAddress) + Expect(createErr).NotTo(HaveOccurred()) + + mixedCaseAddressId, getErr := repo.GetOrCreateAddressInTransaction(tx, address) + Expect(getErr).NotTo(HaveOccurred()) + tx.Commit() + + Expect(upperAddressId).To(Equal(mixedCaseAddressId)) + }) + }) + + Describe("GetAddressById", func() { + It("gets and address by it's id", func() { + addressId, createErr := repo.GetOrCreateAddress(db, address) + Expect(createErr).NotTo(HaveOccurred()) + + actualAddress, getErr := repo.GetAddressById(db, addressId) + Expect(getErr).NotTo(HaveOccurred()) + Expect(actualAddress).To(Equal(address)) + }) + + It("returns an error if the id doesn't exist", func() { + _, getErr := repo.GetAddressById(db, 0) + Expect(getErr).To(HaveOccurred()) + Expect(getErr).To(MatchError("sql: no rows in result set")) + }) + }) +}) diff --git a/pkg/datastore/postgres/repositories/block_repository.go b/pkg/datastore/postgres/repositories/block_repository.go index 979bd633..2242efef 100644 --- a/pkg/datastore/postgres/repositories/block_repository.go +++ b/pkg/datastore/postgres/repositories/block_repository.go @@ -234,7 +234,8 @@ func (blockRepository BlockRepository) createTransaction(tx *sqlx.Tx, blockId in return err } if hasReceipt(transaction) { - receiptId, err := blockRepository.createReceipt(tx, blockId, transaction.Receipt) + receiptRepo := FullSyncReceiptRepository{} + receiptId, err := receiptRepo.CreateFullSyncReceiptInTx(blockId, transaction.Receipt, tx) if err != nil { return err } @@ -256,22 +257,6 @@ func hasReceipt(transaction core.TransactionModel) bool { return transaction.Receipt.TxHash != "" } -func (blockRepository BlockRepository) createReceipt(tx *sqlx.Tx, blockId int64, receipt core.Receipt) (int, error) { - //Not currently persisting log bloom filters - var receiptId int - err := tx.QueryRow( - `INSERT INTO full_sync_receipts - (contract_address, tx_hash, cumulative_gas_used, gas_used, state_root, status, block_id) - VALUES ($1, $2, $3, $4, $5, $6, $7) - RETURNING id`, - receipt.ContractAddress, receipt.TxHash, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.StateRoot, receipt.Status, blockId).Scan(&receiptId) - if err != nil { - log.Error("createReceipt: error inserting receipt: ", err) - return receiptId, err - } - return receiptId, nil -} - func (blockRepository BlockRepository) getBlockHash(block core.Block) (string, bool) { var retrievedBlockHash string // TODO: handle possible error @@ -283,7 +268,7 @@ func (blockRepository BlockRepository) getBlockHash(block core.Block) (string, b return retrievedBlockHash, blockExists(retrievedBlockHash) } -func (blockRepository BlockRepository) createLogs(tx *sqlx.Tx, logs []core.Log, receiptId int) error { +func (blockRepository BlockRepository) createLogs(tx *sqlx.Tx, logs []core.Log, receiptId int64) error { for _, tlog := range logs { _, err := tx.Exec( `INSERT INTO logs (block_number, address, tx_hash, index, topic0, topic1, topic2, topic3, data, receipt_id) diff --git a/pkg/datastore/postgres/repositories/receipt_repository.go b/pkg/datastore/postgres/repositories/full_sync_receipt_repository.go similarity index 70% rename from pkg/datastore/postgres/repositories/receipt_repository.go rename to pkg/datastore/postgres/repositories/full_sync_receipt_repository.go index 01e38f09..71a345f6 100644 --- a/pkg/datastore/postgres/repositories/receipt_repository.go +++ b/pkg/datastore/postgres/repositories/full_sync_receipt_repository.go @@ -26,17 +26,17 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" ) -type ReceiptRepository struct { +type FullSyncReceiptRepository struct { *postgres.DB } -func (receiptRepository ReceiptRepository) CreateReceiptsAndLogs(blockId int64, receipts []core.Receipt) error { +func (receiptRepository FullSyncReceiptRepository) CreateReceiptsAndLogs(blockId int64, receipts []core.Receipt) error { tx, err := receiptRepository.DB.Beginx() if err != nil { return err } for _, receipt := range receipts { - receiptId, err := createReceipt(receipt, blockId, tx) + receiptId, err := receiptRepository.CreateFullSyncReceiptInTx(blockId, receipt, tx) if err != nil { tx.Rollback() return err @@ -53,21 +53,6 @@ func (receiptRepository ReceiptRepository) CreateReceiptsAndLogs(blockId int64, return nil } -func createReceipt(receipt core.Receipt, blockId int64, tx *sqlx.Tx) (int64, error) { - var receiptId int64 - err := tx.QueryRow( - `INSERT INTO full_sync_receipts - (contract_address, tx_hash, cumulative_gas_used, gas_used, state_root, status, block_id) - VALUES ($1, $2, $3, $4, $5, $6, $7) - RETURNING id`, - receipt.ContractAddress, receipt.TxHash, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.StateRoot, receipt.Status, blockId, - ).Scan(&receiptId) - if err != nil { - logrus.Error("createReceipt: Error inserting: ", err) - } - return receiptId, err -} - func createLogs(logs []core.Log, receiptId int64, tx *sqlx.Tx) error { for _, log := range logs { _, err := tx.Exec( @@ -83,27 +68,30 @@ func createLogs(logs []core.Log, receiptId int64, tx *sqlx.Tx) error { return nil } -func (receiptRepository ReceiptRepository) CreateReceipt(blockId int64, receipt core.Receipt) (int64, error) { - tx, _ := receiptRepository.DB.Beginx() +func (FullSyncReceiptRepository) CreateFullSyncReceiptInTx(blockId int64, receipt core.Receipt, tx *sqlx.Tx) (int64, error) { var receiptId int64 + addressId, getAddressErr := AddressRepository{}.GetOrCreateAddressInTransaction(tx, receipt.ContractAddress) + if getAddressErr != nil { + logrus.Error("createReceipt: Error getting address id: ", getAddressErr) + return receiptId, getAddressErr + } err := tx.QueryRow( `INSERT INTO full_sync_receipts - (contract_address, tx_hash, cumulative_gas_used, gas_used, state_root, status, block_id) + (contract_address_id, tx_hash, cumulative_gas_used, gas_used, state_root, status, block_id) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id`, - receipt.ContractAddress, receipt.TxHash, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.StateRoot, receipt.Status, blockId).Scan(&receiptId) + addressId, receipt.TxHash, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.StateRoot, receipt.Status, blockId).Scan(&receiptId) if err != nil { tx.Rollback() logrus.Warning("CreateReceipt: error inserting receipt: ", err) return receiptId, err } - tx.Commit() return receiptId, nil } -func (receiptRepository ReceiptRepository) GetReceipt(txHash string) (core.Receipt, error) { +func (receiptRepository FullSyncReceiptRepository) GetFullSyncReceipt(txHash string) (core.Receipt, error) { row := receiptRepository.DB.QueryRow( - `SELECT contract_address, + `SELECT contract_address_id, tx_hash, cumulative_gas_used, gas_used, diff --git a/pkg/datastore/postgres/repositories/receipt_repository_test.go b/pkg/datastore/postgres/repositories/full_sync_receipt_repository_test.go similarity index 90% rename from pkg/datastore/postgres/repositories/receipt_repository_test.go rename to pkg/datastore/postgres/repositories/full_sync_receipt_repository_test.go index 76d1516b..0232cc99 100644 --- a/pkg/datastore/postgres/repositories/receipt_repository_test.go +++ b/pkg/datastore/postgres/repositories/full_sync_receipt_repository_test.go @@ -30,7 +30,7 @@ import ( var _ = Describe("Receipt Repository", func() { var blockRepository datastore.BlockRepository var logRepository datastore.LogRepository - var receiptRepository datastore.ReceiptRepository + var receiptRepository datastore.FullSyncReceiptRepository var db *postgres.DB var node core.Node BeforeEach(func() { @@ -44,7 +44,7 @@ var _ = Describe("Receipt Repository", func() { test_config.CleanTestDB(db) blockRepository = repositories.NewBlockRepository(db) logRepository = repositories.LogRepository{DB: db} - receiptRepository = repositories.ReceiptRepository{DB: db} + receiptRepository = repositories.FullSyncReceiptRepository{DB: db} }) Describe("Saving multiple receipts", func() { @@ -84,12 +84,12 @@ var _ = Describe("Receipt Repository", func() { Expect(err).NotTo(HaveOccurred()) - persistedReceiptOne, err := receiptRepository.GetReceipt(txHashOne) + persistedReceiptOne, err := receiptRepository.GetFullSyncReceipt(txHashOne) Expect(err).NotTo(HaveOccurred()) Expect(persistedReceiptOne).NotTo(BeNil()) Expect(persistedReceiptOne.TxHash).To(Equal(txHashOne)) - persistedReceiptTwo, err := receiptRepository.GetReceipt(txHashTwo) + persistedReceiptTwo, err := receiptRepository.GetFullSyncReceipt(txHashTwo) Expect(err).NotTo(HaveOccurred()) Expect(persistedReceiptTwo).NotTo(BeNil()) Expect(persistedReceiptTwo.TxHash).To(Equal(txHashTwo)) @@ -124,7 +124,7 @@ var _ = Describe("Receipt Repository", func() { _, err := blockRepository.CreateOrUpdateBlock(block) Expect(err).NotTo(HaveOccurred()) - receipt, err := receiptRepository.GetReceipt("0xe340558980f89d5f86045ac11e5cc34e4bcec20f9f1e2a427aa39d87114e8223") + receipt, err := receiptRepository.GetFullSyncReceipt("0xe340558980f89d5f86045ac11e5cc34e4bcec20f9f1e2a427aa39d87114e8223") Expect(err).ToNot(HaveOccurred()) //Not currently serializing bloom logs Expect(receipt.Bloom).To(Equal(core.Receipt{}.Bloom)) @@ -136,7 +136,7 @@ var _ = Describe("Receipt Repository", func() { }) It("returns ErrReceiptDoesNotExist when receipt does not exist", func() { - receipt, err := receiptRepository.GetReceipt("DOES NOT EXIST") + receipt, err := receiptRepository.GetFullSyncReceipt("DOES NOT EXIST") Expect(err).To(HaveOccurred()) Expect(receipt).To(BeZero()) }) @@ -154,7 +154,7 @@ var _ = Describe("Receipt Repository", func() { _, err := blockRepository.CreateOrUpdateBlock(block) Expect(err).NotTo(HaveOccurred()) - _, err = receiptRepository.GetReceipt(receipt.TxHash) + _, err = receiptRepository.GetFullSyncReceipt(receipt.TxHash) Expect(err).To(Not(HaveOccurred())) }) }) diff --git a/pkg/datastore/postgres/repositories/header_repository.go b/pkg/datastore/postgres/repositories/header_repository.go index 62ff7ecd..dc0baa0f 100644 --- a/pkg/datastore/postgres/repositories/header_repository.go +++ b/pkg/datastore/postgres/repositories/header_repository.go @@ -85,22 +85,6 @@ func (repository HeaderRepository) CreateTransactionInTx(tx *sqlx.Tx, headerID i return txId, err } -func (repository HeaderRepository) CreateReceiptInTx(tx *sqlx.Tx, headerID, transactionID int64, receipt core.Receipt) (int64, error) { - var receiptId int64 - err := tx.QueryRowx(`INSERT INTO public.header_sync_receipts - (header_id, transaction_id, contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ON CONFLICT (header_id, transaction_id) DO UPDATE - SET (contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp) = ($3, $4::NUMERIC, $5::NUMERIC, $6, $7, $8, $9) - RETURNING id`, - headerID, transactionID, receipt.ContractAddress, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.StateRoot, receipt.Status, receipt.TxHash, receipt.Rlp).Scan(&receiptId) - if err != nil { - log.Error("header_repository: error inserting receipt: ", err) - return receiptId, err - } - return receiptId, err -} - func (repository HeaderRepository) GetHeader(blockNumber int64) (core.Header, error) { var header core.Header err := repository.database.Get(&header, `SELECT id, block_number, hash, raw, block_timestamp FROM headers WHERE block_number = $1 AND eth_node_fingerprint = $2`, diff --git a/pkg/datastore/postgres/repositories/header_repository_test.go b/pkg/datastore/postgres/repositories/header_repository_test.go index 68d20aa2..dba6da09 100644 --- a/pkg/datastore/postgres/repositories/header_repository_test.go +++ b/pkg/datastore/postgres/repositories/header_repository_test.go @@ -216,26 +216,40 @@ var _ = Describe("Block header repository", func() { Rlp: []byte{1, 2, 3}, } - _, receiptErr := repo.CreateReceiptInTx(tx, headerID, txId, receipt) + receiptRepo := repositories.HeaderSyncReceiptRepository{} + _, receiptErr := receiptRepo.CreateHeaderSyncReceiptInTx(headerID, txId, receipt, tx) + Expect(receiptErr).ToNot(HaveOccurred()) commitErr := tx.Commit() Expect(commitErr).ToNot(HaveOccurred()) - Expect(receiptErr).ToNot(HaveOccurred()) type idModel struct { - TransactionId int64 `db:"transaction_id"` - core.Receipt + TransactionId int64 `db:"transaction_id"` + ContractAddressId int64 `db:"contract_address_id"` + CumulativeGasUsed uint64 `db:"cumulative_gas_used"` + GasUsed uint64 `db:"gas_used"` + StateRoot string `db:"state_root"` + Status int + TxHash string `db:"tx_hash"` + Rlp []byte `db:"rlp"` } + + var addressId int64 + getAddressErr := db.Get(&addressId, `SELECT id FROM addresses WHERE address = $1`, contractAddr.Hex()) + Expect(getAddressErr).NotTo(HaveOccurred()) + var dbReceipt idModel - err = db.Get(&dbReceipt, - `SELECT transaction_id, contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp + getReceiptErr := db.Get(&dbReceipt, + `SELECT transaction_id, contract_address_id, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp FROM public.header_sync_receipts WHERE header_id = $1`, headerID) - Expect(err).NotTo(HaveOccurred()) + Expect(getReceiptErr).NotTo(HaveOccurred()) + Expect(dbReceipt.TransactionId).To(Equal(txId)) Expect(dbReceipt.TxHash).To(Equal(txHash.Hex())) - Expect(dbReceipt.ContractAddress).To(Equal(contractAddr.Hex())) + Expect(dbReceipt.ContractAddressId).To(Equal(addressId)) Expect(dbReceipt.CumulativeGasUsed).To(Equal(uint64(100))) Expect(dbReceipt.GasUsed).To(Equal(uint64(10))) Expect(dbReceipt.StateRoot).To(Equal(stateRoot.Hex())) + Expect(dbReceipt.Status).To(Equal(0)) Expect(dbReceipt.Rlp).To(Equal([]byte{1, 2, 3})) }) }) diff --git a/pkg/datastore/postgres/repositories/header_sync_receipt_repository.go b/pkg/datastore/postgres/repositories/header_sync_receipt_repository.go new file mode 100644 index 00000000..16b87644 --- /dev/null +++ b/pkg/datastore/postgres/repositories/header_sync_receipt_repository.go @@ -0,0 +1,46 @@ +// 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 repositories + +import ( + "github.com/ethereum/go-ethereum/log" + "github.com/jmoiron/sqlx" + "github.com/vulcanize/vulcanizedb/pkg/core" +) + +type HeaderSyncReceiptRepository struct{} + +func (HeaderSyncReceiptRepository) CreateHeaderSyncReceiptInTx(headerID, transactionID int64, receipt core.Receipt, tx *sqlx.Tx) (int64, error) { + var receiptId int64 + addressId, getAddressErr := AddressRepository{}.GetOrCreateAddressInTransaction(tx, receipt.ContractAddress) + if getAddressErr != nil { + log.Error("createReceipt: Error getting address id: ", getAddressErr) + return receiptId, getAddressErr + } + err := tx.QueryRowx(`INSERT INTO public.header_sync_receipts + (header_id, transaction_id, contract_address_id, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + ON CONFLICT (header_id, transaction_id) DO UPDATE + SET (contract_address_id, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp) = ($3, $4::NUMERIC, $5::NUMERIC, $6, $7, $8, $9) + RETURNING id`, + headerID, transactionID, addressId, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.StateRoot, receipt.Status, receipt.TxHash, receipt.Rlp).Scan(&receiptId) + if err != nil { + log.Error("header_repository: error inserting receipt: ", err) + return receiptId, err + } + return receiptId, err +} diff --git a/pkg/datastore/postgres/repositories/header_sync_receipt_repository_test.go b/pkg/datastore/postgres/repositories/header_sync_receipt_repository_test.go new file mode 100644 index 00000000..f88abdc0 --- /dev/null +++ b/pkg/datastore/postgres/repositories/header_sync_receipt_repository_test.go @@ -0,0 +1,133 @@ +// 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 repositories_test + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("Header Sync Receipt Repo", func() { + var ( + rawHeader []byte + err error + timestamp string + db *postgres.DB + receiptRepo repositories.HeaderSyncReceiptRepository + headerRepo repositories.HeaderRepository + header core.Header + ) + + BeforeEach(func() { + rawHeader, err = json.Marshal(types.Header{}) + Expect(err).NotTo(HaveOccurred()) + timestamp = big.NewInt(123456789).String() + + db = test_config.NewTestDB(test_config.NewTestNode()) + test_config.CleanTestDB(db) + receiptRepo = repositories.HeaderSyncReceiptRepository{} + headerRepo = repositories.NewHeaderRepository(db) + header = core.Header{ + BlockNumber: 100, + Hash: common.BytesToHash([]byte{1, 2, 3, 4, 5}).Hex(), + Raw: rawHeader, + Timestamp: timestamp, + } + }) + Describe("creating a receipt", func() { + It("adds a receipt in a tx", func() { + headerID, err := headerRepo.CreateOrUpdateHeader(header) + Expect(err).NotTo(HaveOccurred()) + fromAddress := common.HexToAddress("0x1234") + toAddress := common.HexToAddress("0x5678") + txHash := common.HexToHash("0x9876") + txIndex := big.NewInt(123) + transaction := core.TransactionModel{ + Data: []byte{}, + From: fromAddress.Hex(), + GasLimit: 0, + GasPrice: 0, + Hash: txHash.Hex(), + Nonce: 0, + Raw: []byte{}, + To: toAddress.Hex(), + TxIndex: txIndex.Int64(), + Value: "0", + } + tx, err := db.Beginx() + Expect(err).ToNot(HaveOccurred()) + txId, txErr := headerRepo.CreateTransactionInTx(tx, headerID, transaction) + Expect(txErr).ToNot(HaveOccurred()) + + contractAddr := common.HexToAddress("0x1234") + stateRoot := common.HexToHash("0x5678") + receipt := core.Receipt{ + ContractAddress: contractAddr.Hex(), + TxHash: txHash.Hex(), + GasUsed: 10, + CumulativeGasUsed: 100, + StateRoot: stateRoot.Hex(), + Rlp: []byte{1, 2, 3}, + } + + _, receiptErr := receiptRepo.CreateHeaderSyncReceiptInTx(headerID, txId, receipt, tx) + Expect(receiptErr).ToNot(HaveOccurred()) + commitErr := tx.Commit() + Expect(commitErr).ToNot(HaveOccurred()) + + type idModel struct { + TransactionId int64 `db:"transaction_id"` + ContractAddressId int64 `db:"contract_address_id"` + CumulativeGasUsed uint64 `db:"cumulative_gas_used"` + GasUsed uint64 `db:"gas_used"` + StateRoot string `db:"state_root"` + Status int + TxHash string `db:"tx_hash"` + Rlp []byte `db:"rlp"` + } + + var addressId int64 + getAddressErr := db.Get(&addressId, `SELECT id FROM addresses WHERE address = $1`, contractAddr.Hex()) + Expect(getAddressErr).NotTo(HaveOccurred()) + + var dbReceipt idModel + getReceiptErr := db.Get(&dbReceipt, + `SELECT transaction_id, contract_address_id, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp + FROM public.header_sync_receipts WHERE header_id = $1`, headerID) + Expect(getReceiptErr).NotTo(HaveOccurred()) + + Expect(dbReceipt.TransactionId).To(Equal(txId)) + Expect(dbReceipt.TxHash).To(Equal(txHash.Hex())) + Expect(dbReceipt.ContractAddressId).To(Equal(addressId)) + Expect(dbReceipt.CumulativeGasUsed).To(Equal(uint64(100))) + Expect(dbReceipt.GasUsed).To(Equal(uint64(10))) + Expect(dbReceipt.StateRoot).To(Equal(stateRoot.Hex())) + Expect(dbReceipt.Status).To(Equal(0)) + Expect(dbReceipt.Rlp).To(Equal([]byte{1, 2, 3})) + }) + }) +}) diff --git a/pkg/datastore/postgres/repositories/logs_repository_test.go b/pkg/datastore/postgres/repositories/logs_repository_test.go index fe5d069d..c6544aa4 100644 --- a/pkg/datastore/postgres/repositories/logs_repository_test.go +++ b/pkg/datastore/postgres/repositories/logs_repository_test.go @@ -34,7 +34,7 @@ var _ = Describe("Logs Repository", func() { var db *postgres.DB var blockRepository datastore.BlockRepository var logsRepository datastore.LogRepository - var receiptRepository datastore.ReceiptRepository + var receiptRepository datastore.FullSyncReceiptRepository var node core.Node BeforeEach(func() { @@ -48,14 +48,16 @@ var _ = Describe("Logs Repository", func() { test_config.CleanTestDB(db) blockRepository = repositories.NewBlockRepository(db) logsRepository = repositories.LogRepository{DB: db} - receiptRepository = repositories.ReceiptRepository{DB: db} + receiptRepository = repositories.FullSyncReceiptRepository{DB: db} }) It("returns the log when it exists", func() { blockNumber := int64(12345) blockId, err := blockRepository.CreateOrUpdateBlock(core.Block{Number: blockNumber}) Expect(err).NotTo(HaveOccurred()) - receiptId, err := receiptRepository.CreateReceipt(blockId, core.Receipt{}) + tx, _ := db.Beginx() + receiptId, err := receiptRepository.CreateFullSyncReceiptInTx(blockId, core.Receipt{}, tx) + tx.Commit() Expect(err).NotTo(HaveOccurred()) err = logsRepository.CreateLogs([]core.Log{{ BlockNumber: blockNumber, @@ -91,7 +93,9 @@ var _ = Describe("Logs Repository", func() { blockNumber := int64(12345) blockId, err := blockRepository.CreateOrUpdateBlock(core.Block{Number: blockNumber}) Expect(err).NotTo(HaveOccurred()) - receiptId, err := receiptRepository.CreateReceipt(blockId, core.Receipt{}) + tx, _ := db.Beginx() + receiptId, err := receiptRepository.CreateFullSyncReceiptInTx(blockId, core.Receipt{}, tx) + tx.Commit() Expect(err).NotTo(HaveOccurred()) err = logsRepository.CreateLogs([]core.Log{{ diff --git a/pkg/datastore/postgres/repositories/watched_events_repository_test.go b/pkg/datastore/postgres/repositories/watched_events_repository_test.go index 957d3c33..23ff8af6 100644 --- a/pkg/datastore/postgres/repositories/watched_events_repository_test.go +++ b/pkg/datastore/postgres/repositories/watched_events_repository_test.go @@ -19,6 +19,7 @@ package repositories_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/datastore" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" @@ -32,7 +33,7 @@ var _ = Describe("Watched Events Repository", func() { var blocksRepository datastore.BlockRepository var filterRepository datastore.FilterRepository var logRepository datastore.LogRepository - var receiptRepository datastore.ReceiptRepository + var receiptRepository datastore.FullSyncReceiptRepository var watchedEventRepository datastore.WatchedEventRepository BeforeEach(func() { @@ -41,7 +42,7 @@ var _ = Describe("Watched Events Repository", func() { blocksRepository = repositories.NewBlockRepository(db) filterRepository = repositories.FilterRepository{DB: db} logRepository = repositories.LogRepository{DB: db} - receiptRepository = repositories.ReceiptRepository{DB: db} + receiptRepository = repositories.FullSyncReceiptRepository{DB: db} watchedEventRepository = repositories.WatchedEventRepository{DB: db} }) @@ -79,7 +80,10 @@ var _ = Describe("Watched Events Repository", func() { Expect(err).ToNot(HaveOccurred()) blockId, err := blocksRepository.CreateOrUpdateBlock(core.Block{}) Expect(err).NotTo(HaveOccurred()) - receiptId, err := receiptRepository.CreateReceipt(blockId, core.Receipt{}) + tx, txBeginErr := db.Beginx() + Expect(txBeginErr).NotTo(HaveOccurred()) + receiptId, err := receiptRepository.CreateFullSyncReceiptInTx(blockId, core.Receipt{}, tx) + tx.Commit() Expect(err).NotTo(HaveOccurred()) err = logRepository.CreateLogs(logs, receiptId) Expect(err).ToNot(HaveOccurred()) @@ -136,7 +140,9 @@ var _ = Describe("Watched Events Repository", func() { Expect(err).ToNot(HaveOccurred()) blockId, err := blocksRepository.CreateOrUpdateBlock(core.Block{Hash: "Ox123"}) Expect(err).NotTo(HaveOccurred()) - receiptId, err := receiptRepository.CreateReceipt(blockId, core.Receipt{TxHash: "0x123"}) + tx, _ := db.Beginx() + receiptId, err := receiptRepository.CreateFullSyncReceiptInTx(blockId, core.Receipt{}, tx) + tx.Commit() Expect(err).NotTo(HaveOccurred()) err = logRepository.CreateLogs(logs, receiptId) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/datastore/repository.go b/pkg/datastore/repository.go index db0c220b..fe8bad9c 100644 --- a/pkg/datastore/repository.go +++ b/pkg/datastore/repository.go @@ -17,10 +17,15 @@ package datastore import ( + "github.com/jmoiron/sqlx" "github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/filters" ) +type AddressRepository interface { + GetOrCreateAddress(address string) (int, error) +} + type BlockRepository interface { CreateOrUpdateBlock(block core.Block) (int64, error) GetBlock(blockNumber int64) (core.Block, error) @@ -51,10 +56,14 @@ type LogRepository interface { GetLogs(address string, blockNumber int64) ([]core.Log, error) } -type ReceiptRepository interface { +type FullSyncReceiptRepository interface { CreateReceiptsAndLogs(blockId int64, receipts []core.Receipt) error - CreateReceipt(blockId int64, receipt core.Receipt) (int64, error) - GetReceipt(txHash string) (core.Receipt, error) + CreateFullSyncReceiptInTx(blockId int64, receipt core.Receipt, tx *sqlx.Tx) (int64, error) + GetFullSyncReceipt(txHash string) (core.Receipt, error) +} + +type HeaderSyncReceiptRepository interface { + CreateFullSyncReceiptInTx(blockId int64, receipt core.Receipt, tx *sqlx.Tx) (int64, error) } type WatchedEventRepository interface { diff --git a/pkg/fakes/mock_receipt_repository.go b/pkg/fakes/mock_receipt_repository.go index 63a0d24d..80236393 100644 --- a/pkg/fakes/mock_receipt_repository.go +++ b/pkg/fakes/mock_receipt_repository.go @@ -17,6 +17,7 @@ package fakes import ( + "github.com/jmoiron/sqlx" . "github.com/onsi/gomega" "github.com/vulcanize/vulcanizedb/pkg/core" @@ -49,11 +50,11 @@ func (mrr *MockReceiptRepository) CreateReceiptsAndLogs(blockId int64, receipts return mrr.createReceiptsAndLogsReturnErr } -func (mrr *MockReceiptRepository) CreateReceipt(blockId int64, receipt core.Receipt) (int64, error) { +func (mrr *MockReceiptRepository) CreateFullSyncReceiptInTx(blockId int64, receipt core.Receipt, tx *sqlx.Tx) (int64, error) { panic("implement me") } -func (mrr *MockReceiptRepository) GetReceipt(txHash string) (core.Receipt, error) { +func (mrr *MockReceiptRepository) GetFullSyncReceipt(txHash string) (core.Receipt, error) { panic("implement me") } diff --git a/pkg/geth/cold_import/importer.go b/pkg/geth/cold_import/importer.go index e29bfd22..280ed15a 100644 --- a/pkg/geth/cold_import/importer.go +++ b/pkg/geth/cold_import/importer.go @@ -26,10 +26,10 @@ type ColdImporter struct { blockRepository datastore.BlockRepository converter common.BlockConverter ethDB ethereum.Database - receiptRepository datastore.ReceiptRepository + receiptRepository datastore.FullSyncReceiptRepository } -func NewColdImporter(ethDB ethereum.Database, blockRepository datastore.BlockRepository, receiptRepository datastore.ReceiptRepository, converter common.BlockConverter) *ColdImporter { +func NewColdImporter(ethDB ethereum.Database, blockRepository datastore.BlockRepository, receiptRepository datastore.FullSyncReceiptRepository, converter common.BlockConverter) *ColdImporter { return &ColdImporter{ blockRepository: blockRepository, converter: converter, diff --git a/pkg/geth/converters/rpc/transaction_converter_test.go b/pkg/geth/converters/rpc/transaction_converter_test.go index 21047c92..bcd7b725 100644 --- a/pkg/geth/converters/rpc/transaction_converter_test.go +++ b/pkg/geth/converters/rpc/transaction_converter_test.go @@ -1,3 +1,19 @@ +// 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 rpc_test import ( diff --git a/test_config/test_config.go b/test_config/test_config.go index ac8acff1..33f6b5bc 100644 --- a/test_config/test_config.go +++ b/test_config/test_config.go @@ -105,6 +105,7 @@ func NewTestDB(node core.Node) *postgres.DB { } func CleanTestDB(db *postgres.DB) { + db.MustExec("DELETE FROM addresses") db.MustExec("DELETE FROM blocks") db.MustExec("DELETE FROM checked_headers") // can't delete from eth_nodes since this function is called after the required eth_node is persisted