Merge pull request #46 from vulcanize/vdb-425-transactions
(VDB-486) transactions
This commit is contained in:
commit
426556ceda
@ -1,16 +1,18 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE transactions (
|
CREATE TABLE full_sync_transactions (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
block_id INTEGER NOT NULL REFERENCES blocks(id) ON DELETE CASCADE,
|
block_id INTEGER NOT NULL REFERENCES blocks(id) ON DELETE CASCADE,
|
||||||
input_data VARCHAR,
|
|
||||||
tx_from VARCHAR(66),
|
|
||||||
gaslimit NUMERIC,
|
gaslimit NUMERIC,
|
||||||
gasprice NUMERIC,
|
gasprice NUMERIC,
|
||||||
hash VARCHAR(66),
|
hash VARCHAR(66),
|
||||||
|
input_data BYTEA,
|
||||||
nonce NUMERIC,
|
nonce NUMERIC,
|
||||||
|
raw BYTEA,
|
||||||
|
tx_from VARCHAR(66),
|
||||||
|
tx_index INTEGER,
|
||||||
tx_to VARCHAR(66),
|
tx_to VARCHAR(66),
|
||||||
"value" NUMERIC
|
"value" NUMERIC
|
||||||
);
|
);
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
DROP TABLE transactions;
|
DROP TABLE full_sync_transactions;
|
@ -0,0 +1,5 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE INDEX block_id_index ON full_sync_transactions (block_id);
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP INDEX block_id_index;
|
@ -1,5 +0,0 @@
|
|||||||
-- +goose Up
|
|
||||||
CREATE INDEX block_id_index ON transactions (block_id);
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
DROP INDEX block_id_index;
|
|
@ -0,0 +1,5 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE INDEX tx_to_index ON full_sync_transactions(tx_to);
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP INDEX tx_to_index;
|
@ -1,5 +0,0 @@
|
|||||||
-- +goose Up
|
|
||||||
CREATE INDEX tx_to_index ON transactions(tx_to);
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
DROP INDEX tx_to_index;
|
|
@ -0,0 +1,5 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE INDEX tx_from_index ON full_sync_transactions(tx_from);
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP INDEX tx_from_index;
|
@ -1,5 +0,0 @@
|
|||||||
-- +goose Up
|
|
||||||
CREATE INDEX tx_from_index ON transactions(tx_from);
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
DROP INDEX tx_from_index;
|
|
@ -2,16 +2,13 @@
|
|||||||
CREATE TABLE receipts
|
CREATE TABLE receipts
|
||||||
(
|
(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
transaction_id INTEGER NOT NULL,
|
transaction_id INTEGER NOT NULL REFERENCES full_sync_transactions (id) ON DELETE CASCADE,
|
||||||
contract_address VARCHAR(42),
|
contract_address VARCHAR(42),
|
||||||
cumulative_gas_used NUMERIC,
|
cumulative_gas_used NUMERIC,
|
||||||
gas_used NUMERIC,
|
gas_used NUMERIC,
|
||||||
state_root VARCHAR(66),
|
state_root VARCHAR(66),
|
||||||
status INTEGER,
|
status INTEGER,
|
||||||
tx_hash VARCHAR(66),
|
tx_hash VARCHAR(66)
|
||||||
CONSTRAINT transaction_fk FOREIGN KEY (transaction_id)
|
|
||||||
REFERENCES transactions (id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ ALTER TABLE receipts
|
|||||||
|
|
||||||
UPDATE receipts
|
UPDATE receipts
|
||||||
SET block_id = (
|
SET block_id = (
|
||||||
SELECT block_id FROM transactions WHERE transactions.id = receipts.transaction_id
|
SELECT block_id FROM full_sync_transactions WHERE full_sync_transactions.id = receipts.transaction_id
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE receipts
|
ALTER TABLE receipts
|
||||||
@ -28,7 +28,7 @@ CREATE INDEX transaction_id_index ON receipts (transaction_id);
|
|||||||
|
|
||||||
UPDATE receipts
|
UPDATE receipts
|
||||||
SET transaction_id = (
|
SET transaction_id = (
|
||||||
SELECT id FROM transactions WHERE transactions.hash = receipts.tx_hash
|
SELECT id FROM full_sync_transactions WHERE full_sync_transactions.hash = receipts.tx_hash
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE receipts
|
ALTER TABLE receipts
|
||||||
@ -37,7 +37,7 @@ ALTER TABLE receipts
|
|||||||
ALTER TABLE receipts
|
ALTER TABLE receipts
|
||||||
ADD CONSTRAINT transaction_fk
|
ADD CONSTRAINT transaction_fk
|
||||||
FOREIGN KEY (transaction_id)
|
FOREIGN KEY (transaction_id)
|
||||||
REFERENCES transactions (id)
|
REFERENCES full_sync_transactions (id)
|
||||||
ON DELETE CASCADE;
|
ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE receipts
|
ALTER TABLE receipts
|
||||||
|
19
db/migrations/00025_create_light_sync_transactions_table.sql
Normal file
19
db/migrations/00025_create_light_sync_transactions_table.sql
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE TABLE light_sync_transactions (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE,
|
||||||
|
hash TEXT,
|
||||||
|
gaslimit NUMERIC,
|
||||||
|
gasprice NUMERIC,
|
||||||
|
input_data BYTEA,
|
||||||
|
nonce NUMERIC,
|
||||||
|
raw BYTEA,
|
||||||
|
tx_from TEXT,
|
||||||
|
tx_index INTEGER,
|
||||||
|
tx_to TEXT,
|
||||||
|
"value" NUMERIC,
|
||||||
|
UNIQUE (header_id, hash)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP TABLE light_sync_transactions;
|
201
db/schema.sql
201
db/schema.sql
@ -151,6 +151,46 @@ CREATE TABLE public.eth_nodes (
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: full_sync_transactions; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.full_sync_transactions (
|
||||||
|
id integer NOT NULL,
|
||||||
|
block_id integer NOT NULL,
|
||||||
|
gaslimit numeric,
|
||||||
|
gasprice numeric,
|
||||||
|
hash character varying(66),
|
||||||
|
input_data bytea,
|
||||||
|
nonce numeric,
|
||||||
|
raw bytea,
|
||||||
|
tx_from character varying(66),
|
||||||
|
tx_index integer,
|
||||||
|
tx_to character varying(66),
|
||||||
|
value numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: full_sync_transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.full_sync_transactions_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: full_sync_transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.full_sync_transactions_id_seq OWNED BY public.full_sync_transactions.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: goose_db_version; Type: TABLE; Schema: public; Owner: -
|
-- Name: goose_db_version; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -218,6 +258,46 @@ CREATE SEQUENCE public.headers_id_seq
|
|||||||
ALTER SEQUENCE public.headers_id_seq OWNED BY public.headers.id;
|
ALTER SEQUENCE public.headers_id_seq OWNED BY public.headers.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: light_sync_transactions; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.light_sync_transactions (
|
||||||
|
id integer NOT NULL,
|
||||||
|
header_id integer NOT NULL,
|
||||||
|
hash text,
|
||||||
|
gaslimit numeric,
|
||||||
|
gasprice numeric,
|
||||||
|
input_data bytea,
|
||||||
|
nonce numeric,
|
||||||
|
raw bytea,
|
||||||
|
tx_from text,
|
||||||
|
tx_index integer,
|
||||||
|
tx_to text,
|
||||||
|
value numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: light_sync_transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.light_sync_transactions_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: light_sync_transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.light_sync_transactions_id_seq OWNED BY public.light_sync_transactions.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: log_filters; Type: TABLE; Schema: public; Owner: -
|
-- Name: log_filters; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -368,44 +448,6 @@ CREATE SEQUENCE public.receipts_id_seq
|
|||||||
ALTER SEQUENCE public.receipts_id_seq OWNED BY public.receipts.id;
|
ALTER SEQUENCE public.receipts_id_seq OWNED BY public.receipts.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: transactions; Type: TABLE; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.transactions (
|
|
||||||
id integer NOT NULL,
|
|
||||||
block_id integer NOT NULL,
|
|
||||||
input_data character varying,
|
|
||||||
tx_from character varying(66),
|
|
||||||
gaslimit numeric,
|
|
||||||
gasprice numeric,
|
|
||||||
hash character varying(66),
|
|
||||||
nonce numeric,
|
|
||||||
tx_to character varying(66),
|
|
||||||
value numeric
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.transactions_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.transactions_id_seq OWNED BY public.transactions.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: watched_contracts; Type: TABLE; Schema: public; Owner: -
|
-- Name: watched_contracts; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -481,6 +523,13 @@ ALTER TABLE ONLY public.checked_headers ALTER COLUMN id SET DEFAULT nextval('pub
|
|||||||
ALTER TABLE ONLY public.eth_nodes ALTER COLUMN id SET DEFAULT nextval('public.nodes_id_seq'::regclass);
|
ALTER TABLE ONLY public.eth_nodes ALTER COLUMN id SET DEFAULT nextval('public.nodes_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: full_sync_transactions id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.full_sync_transactions ALTER COLUMN id SET DEFAULT nextval('public.full_sync_transactions_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: goose_db_version id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: goose_db_version id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -495,6 +544,13 @@ ALTER TABLE ONLY public.goose_db_version ALTER COLUMN id SET DEFAULT nextval('pu
|
|||||||
ALTER TABLE ONLY public.headers ALTER COLUMN id SET DEFAULT nextval('public.headers_id_seq'::regclass);
|
ALTER TABLE ONLY public.headers ALTER COLUMN id SET DEFAULT nextval('public.headers_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: light_sync_transactions id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.light_sync_transactions ALTER COLUMN id SET DEFAULT nextval('public.light_sync_transactions_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: log_filters id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: log_filters id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -523,13 +579,6 @@ ALTER TABLE ONLY public.queued_storage ALTER COLUMN id SET DEFAULT nextval('publ
|
|||||||
ALTER TABLE ONLY public.receipts ALTER COLUMN id SET DEFAULT nextval('public.receipts_id_seq'::regclass);
|
ALTER TABLE ONLY public.receipts ALTER COLUMN id SET DEFAULT nextval('public.receipts_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: transactions id; Type: DEFAULT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.transactions ALTER COLUMN id SET DEFAULT nextval('public.transactions_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: watched_contracts contract_id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: watched_contracts contract_id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -577,6 +626,14 @@ ALTER TABLE ONLY public.eth_nodes
|
|||||||
ADD CONSTRAINT eth_node_uc UNIQUE (genesis_block, network_id, eth_node_id);
|
ADD CONSTRAINT eth_node_uc UNIQUE (genesis_block, network_id, eth_node_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: full_sync_transactions full_sync_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.full_sync_transactions
|
||||||
|
ADD CONSTRAINT full_sync_transactions_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: goose_db_version goose_db_version_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: goose_db_version goose_db_version_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -593,6 +650,22 @@ ALTER TABLE ONLY public.headers
|
|||||||
ADD CONSTRAINT headers_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT headers_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: light_sync_transactions light_sync_transactions_header_id_hash_key; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.light_sync_transactions
|
||||||
|
ADD CONSTRAINT light_sync_transactions_header_id_hash_key UNIQUE (header_id, hash);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: light_sync_transactions light_sync_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.light_sync_transactions
|
||||||
|
ADD CONSTRAINT light_sync_transactions_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: logs logs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: logs logs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -633,14 +706,6 @@ ALTER TABLE ONLY public.receipts
|
|||||||
ADD CONSTRAINT receipts_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT receipts_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: transactions transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.transactions
|
|
||||||
ADD CONSTRAINT transactions_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: watched_contracts watched_contracts_contract_hash_key; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: watched_contracts watched_contracts_contract_hash_key; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -661,7 +726,7 @@ ALTER TABLE ONLY public.watched_contracts
|
|||||||
-- Name: block_id_index; Type: INDEX; Schema: public; Owner: -
|
-- Name: block_id_index; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE INDEX block_id_index ON public.transactions USING btree (block_id);
|
CREATE INDEX block_id_index ON public.full_sync_transactions USING btree (block_id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -689,14 +754,14 @@ CREATE INDEX number_index ON public.blocks USING btree (number);
|
|||||||
-- Name: tx_from_index; Type: INDEX; Schema: public; Owner: -
|
-- Name: tx_from_index; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE INDEX tx_from_index ON public.transactions USING btree (tx_from);
|
CREATE INDEX tx_from_index ON public.full_sync_transactions USING btree (tx_from);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: tx_to_index; Type: INDEX; Schema: public; Owner: -
|
-- Name: tx_to_index; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE INDEX tx_to_index ON public.transactions USING btree (tx_to);
|
CREATE INDEX tx_to_index ON public.full_sync_transactions USING btree (tx_to);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -723,6 +788,22 @@ ALTER TABLE ONLY public.headers
|
|||||||
ADD CONSTRAINT eth_nodes_fk FOREIGN KEY (eth_node_id) REFERENCES public.eth_nodes(id) ON DELETE CASCADE;
|
ADD CONSTRAINT eth_nodes_fk FOREIGN KEY (eth_node_id) REFERENCES public.eth_nodes(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: full_sync_transactions full_sync_transactions_block_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
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: light_sync_transactions light_sync_transactions_header_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.light_sync_transactions
|
||||||
|
ADD CONSTRAINT light_sync_transactions_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: blocks node_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
-- Name: blocks node_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -739,14 +820,6 @@ ALTER TABLE ONLY public.logs
|
|||||||
ADD CONSTRAINT receipts_fk FOREIGN KEY (receipt_id) REFERENCES public.receipts(id) ON DELETE CASCADE;
|
ADD CONSTRAINT receipts_fk FOREIGN KEY (receipt_id) REFERENCES public.receipts(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: transactions transactions_block_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.transactions
|
|
||||||
ADD CONSTRAINT transactions_block_id_fkey FOREIGN KEY (block_id) REFERENCES public.blocks(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- PostgreSQL database dump complete
|
-- PostgreSQL database dump complete
|
||||||
--
|
--
|
||||||
|
@ -40,8 +40,10 @@ var _ = Describe("contractWatcher full transformer", func() {
|
|||||||
|
|
||||||
Describe("Init", func() {
|
Describe("Init", func() {
|
||||||
It("Initializes transformer's contract objects", func() {
|
It("Initializes transformer's contract objects", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||||
err = t.Init()
|
err = t.Init()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
@ -63,8 +65,10 @@ var _ = Describe("contractWatcher full transformer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Does nothing if watched events are unset", func() {
|
It("Does nothing if watched events are unset", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
var testConf config.ContractConfig
|
var testConf config.ContractConfig
|
||||||
testConf = test_helpers.TusdConfig
|
testConf = test_helpers.TusdConfig
|
||||||
testConf.Events = nil
|
testConf.Events = nil
|
||||||
@ -80,8 +84,10 @@ var _ = Describe("contractWatcher full transformer", func() {
|
|||||||
|
|
||||||
Describe("Execute", func() {
|
Describe("Execute", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Transforms watched contract data into custom repositories", func() {
|
It("Transforms watched contract data into custom repositories", func() {
|
||||||
@ -181,8 +187,10 @@ var _ = Describe("contractWatcher full transformer", func() {
|
|||||||
|
|
||||||
Describe("Execute- against ENS registry contract", func() {
|
Describe("Execute- against ENS registry contract", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock1)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock1)
|
||||||
blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock2)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Transforms watched contract data into custom repositories", func() {
|
It("Transforms watched contract data into custom repositories", func() {
|
||||||
|
@ -38,8 +38,10 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
|
|
||||||
Describe("Init", func() {
|
Describe("Init", func() {
|
||||||
It("Initializes transformer's contract objects", func() {
|
It("Initializes transformer's contract objects", func() {
|
||||||
headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
|
_, insertErr := headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
|
||||||
headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||||
err = t.Init()
|
err = t.Init()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
@ -61,8 +63,10 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Does nothing if nothing if no addresses are configured", func() {
|
It("Does nothing if nothing if no addresses are configured", func() {
|
||||||
headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
|
_, insertErr := headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
|
||||||
headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
var testConf config.ContractConfig
|
var testConf config.ContractConfig
|
||||||
testConf = test_helpers.TusdConfig
|
testConf = test_helpers.TusdConfig
|
||||||
testConf.Addresses = nil
|
testConf.Addresses = nil
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package integration_test
|
package integration_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
@ -85,6 +86,41 @@ var _ = Describe("Reading from the Geth blockchain", func() {
|
|||||||
close(done)
|
close(done)
|
||||||
}, 15)
|
}, 15)
|
||||||
|
|
||||||
|
It("retrieves transaction", func() {
|
||||||
|
// actual transaction: https://etherscan.io/tx/0x44d462f2a19ad267e276b234a62c542fc91c974d2e4754a325ca405f95440255
|
||||||
|
txHash := common.HexToHash("0x44d462f2a19ad267e276b234a62c542fc91c974d2e4754a325ca405f95440255")
|
||||||
|
transactions, err := blockChain.GetTransactions([]common.Hash{txHash})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(transactions)).To(Equal(1))
|
||||||
|
expectedData := []byte{149, 227, 197, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
1, 160, 85, 105, 13, 157, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,
|
||||||
|
241, 202, 218, 90, 30, 178, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 92, 155, 193, 43}
|
||||||
|
expectedRaw := []byte{248, 201, 9, 132, 59, 154, 202, 0, 131, 1, 102, 93, 148, 44, 75, 208, 100, 185, 152, 131,
|
||||||
|
128, 118, 250, 52, 26, 131, 208, 7, 252, 47, 165, 9, 87, 128, 184, 100, 149, 227, 197, 11, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 85, 105, 13, 157, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 241, 202, 218, 90, 30, 178, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 155, 193, 43, 37, 160, 237, 184, 236, 248, 23, 152,
|
||||||
|
53, 238, 44, 215, 181, 234, 229, 157, 246, 212, 178, 88, 25, 116, 134, 163, 124, 64, 2, 66, 25, 118, 1, 253, 27,
|
||||||
|
101, 160, 36, 226, 116, 43, 147, 236, 124, 76, 227, 250, 228, 168, 22, 19, 248, 155, 248, 151, 219, 14, 1, 186,
|
||||||
|
159, 35, 154, 22, 222, 123, 254, 147, 63, 221}
|
||||||
|
expectedModel := core.TransactionModel{
|
||||||
|
Data: expectedData,
|
||||||
|
From: "0x3b08b99441086edd66f36f9f9aee733280698378",
|
||||||
|
GasLimit: 91741,
|
||||||
|
GasPrice: 1000000000,
|
||||||
|
Hash: "0x44d462f2a19ad267e276b234a62c542fc91c974d2e4754a325ca405f95440255",
|
||||||
|
Nonce: 9,
|
||||||
|
Raw: expectedRaw,
|
||||||
|
Receipt: core.Receipt{},
|
||||||
|
To: "0x2c4bd064b998838076fa341a83d007fc2fa50957",
|
||||||
|
TxIndex: 30,
|
||||||
|
Value: "0",
|
||||||
|
}
|
||||||
|
Expect(transactions[0]).To(Equal(expectedModel))
|
||||||
|
})
|
||||||
|
|
||||||
//Benchmarking test: remove skip to test performance of block retrieval
|
//Benchmarking test: remove skip to test performance of block retrieval
|
||||||
XMeasure("retrieving n blocks", func(b Benchmarker) {
|
XMeasure("retrieving n blocks", func(b Benchmarker) {
|
||||||
b.Time("runtime", func() {
|
b.Time("runtime", func() {
|
||||||
|
53
libraries/shared/transactions/syncer.go
Normal file
53
libraries/shared/transactions/syncer.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package transactions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ITransactionsSyncer interface {
|
||||||
|
SyncTransactions(headerID int64, logs []types.Log) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionsSyncer struct {
|
||||||
|
BlockChain core.BlockChain
|
||||||
|
Repository datastore.HeaderRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTransactionsSyncer(db *postgres.DB, blockChain core.BlockChain) TransactionsSyncer {
|
||||||
|
repository := repositories.NewHeaderRepository(db)
|
||||||
|
return TransactionsSyncer{
|
||||||
|
BlockChain: blockChain,
|
||||||
|
Repository: repository,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer TransactionsSyncer) SyncTransactions(headerID int64, logs []types.Log) error {
|
||||||
|
transactionHashes := getUniqueTransactionHashes(logs)
|
||||||
|
transactions, transactionErr := syncer.BlockChain.GetTransactions(transactionHashes)
|
||||||
|
if transactionErr != nil {
|
||||||
|
return transactionErr
|
||||||
|
}
|
||||||
|
writeErr := syncer.Repository.CreateTransactions(headerID, transactions)
|
||||||
|
if writeErr != nil {
|
||||||
|
return writeErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUniqueTransactionHashes(logs []types.Log) []common.Hash {
|
||||||
|
seen := make(map[common.Hash]struct{}, len(logs))
|
||||||
|
var result []common.Hash
|
||||||
|
for _, log := range logs {
|
||||||
|
if _, ok := seen[log.TxHash]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[log.TxHash] = struct{}{}
|
||||||
|
result = append(result, log.TxHash)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
75
libraries/shared/transactions/syncer_test.go
Normal file
75
libraries/shared/transactions/syncer_test.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package transactions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/transactions"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
|
"github.com/vulcanize/vulcanizedb/test_config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Transaction syncer", func() {
|
||||||
|
var (
|
||||||
|
blockChain *fakes.MockBlockChain
|
||||||
|
syncer transactions.TransactionsSyncer
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
db := test_config.NewTestDB(test_config.NewTestNode())
|
||||||
|
test_config.CleanTestDB(db)
|
||||||
|
blockChain = fakes.NewMockBlockChain()
|
||||||
|
syncer = transactions.NewTransactionsSyncer(db, blockChain)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("fetches transactions for logs", func() {
|
||||||
|
err := syncer.SyncTransactions(0, []types.Log{})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(blockChain.GetTransactionsCalled).To(BeTrue())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("only fetches transactions with unique hashes", func() {
|
||||||
|
err := syncer.SyncTransactions(0, []types.Log{{
|
||||||
|
TxHash: fakes.FakeHash,
|
||||||
|
}, {
|
||||||
|
TxHash: fakes.FakeHash,
|
||||||
|
}})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(blockChain.GetTransactionsPassedHashes)).To(Equal(1))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns error if fetching transactions fails", func() {
|
||||||
|
blockChain.GetTransactionsError = fakes.FakeError
|
||||||
|
|
||||||
|
err := syncer.SyncTransactions(0, []types.Log{})
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("passes transactions to repository for persistence", func() {
|
||||||
|
blockChain.Transactions = []core.TransactionModel{{}}
|
||||||
|
mockHeaderRepository := fakes.NewMockHeaderRepository()
|
||||||
|
syncer.Repository = mockHeaderRepository
|
||||||
|
|
||||||
|
err := syncer.SyncTransactions(0, []types.Log{})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(mockHeaderRepository.CreateTransactionsCalled).To(BeTrue())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns error if persisting transactions fails", func() {
|
||||||
|
blockChain.Transactions = []core.TransactionModel{{}}
|
||||||
|
mockHeaderRepository := fakes.NewMockHeaderRepository()
|
||||||
|
mockHeaderRepository.CreateTransactionsError = fakes.FakeError
|
||||||
|
syncer.Repository = mockHeaderRepository
|
||||||
|
|
||||||
|
err := syncer.SyncTransactions(0, []types.Log{})
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
})
|
||||||
|
})
|
13
libraries/shared/transactions/transactions_suite_test.go
Normal file
13
libraries/shared/transactions/transactions_suite_test.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package transactions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTransactions(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Shared Transactions Suite")
|
||||||
|
}
|
@ -18,14 +18,16 @@ package watcher
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/transactions"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
log "github.com/sirupsen/logrus"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
chunk "github.com/vulcanize/vulcanizedb/libraries/shared/chunker"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/chunker"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
||||||
fetch "github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||||
repo "github.com/vulcanize/vulcanizedb/libraries/shared/repository"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/repository"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
@ -33,21 +35,26 @@ import (
|
|||||||
|
|
||||||
type EventWatcher struct {
|
type EventWatcher struct {
|
||||||
Transformers []transformer.EventTransformer
|
Transformers []transformer.EventTransformer
|
||||||
|
BlockChain core.BlockChain
|
||||||
DB *postgres.DB
|
DB *postgres.DB
|
||||||
Fetcher fetch.LogFetcher
|
Fetcher fetcher.LogFetcher
|
||||||
Chunker chunk.Chunker
|
Chunker chunker.Chunker
|
||||||
Addresses []common.Address
|
Addresses []common.Address
|
||||||
Topics []common.Hash
|
Topics []common.Hash
|
||||||
StartingBlock *int64
|
StartingBlock *int64
|
||||||
|
Syncer transactions.ITransactionsSyncer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEventWatcher(db *postgres.DB, bc core.BlockChain) EventWatcher {
|
func NewEventWatcher(db *postgres.DB, bc core.BlockChain) EventWatcher {
|
||||||
chunker := chunk.NewLogChunker()
|
logChunker := chunker.NewLogChunker()
|
||||||
fetcher := fetch.NewFetcher(bc)
|
logFetcher := fetcher.NewFetcher(bc)
|
||||||
|
transactionSyncer := transactions.NewTransactionsSyncer(db, bc)
|
||||||
return EventWatcher{
|
return EventWatcher{
|
||||||
DB: db,
|
BlockChain: bc,
|
||||||
Fetcher: fetcher,
|
DB: db,
|
||||||
Chunker: chunker,
|
Fetcher: logFetcher,
|
||||||
|
Chunker: logChunker,
|
||||||
|
Syncer: transactionSyncer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,15 +92,15 @@ func (watcher *EventWatcher) Execute(recheckHeaders constants.TransformerExecuti
|
|||||||
return fmt.Errorf("No transformers added to watcher")
|
return fmt.Errorf("No transformers added to watcher")
|
||||||
}
|
}
|
||||||
|
|
||||||
checkedColumnNames, err := repo.GetCheckedColumnNames(watcher.DB)
|
checkedColumnNames, err := repository.GetCheckedColumnNames(watcher.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
notCheckedSQL := repo.CreateNotCheckedSQL(checkedColumnNames, recheckHeaders)
|
notCheckedSQL := repository.CreateNotCheckedSQL(checkedColumnNames, recheckHeaders)
|
||||||
|
|
||||||
missingHeaders, err := repo.MissingHeaders(*watcher.StartingBlock, -1, watcher.DB, notCheckedSQL)
|
missingHeaders, err := repository.MissingHeaders(*watcher.StartingBlock, -1, watcher.DB, notCheckedSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Fetching of missing headers failed in watcher!")
|
logrus.Error("Fetching of missing headers failed in watcher!")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,28 +108,41 @@ func (watcher *EventWatcher) Execute(recheckHeaders constants.TransformerExecuti
|
|||||||
// TODO Extend FetchLogs for doing several blocks at a time
|
// TODO Extend FetchLogs for doing several blocks at a time
|
||||||
logs, err := watcher.Fetcher.FetchLogs(watcher.Addresses, watcher.Topics, header)
|
logs, err := watcher.Fetcher.FetchLogs(watcher.Addresses, watcher.Topics, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO Handle fetch error in watcher
|
logrus.Errorf("Error while fetching logs for header %v in watcher", header.Id)
|
||||||
log.Errorf("Error while fetching logs for header %v in watcher", header.Id)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkedLogs := watcher.Chunker.ChunkLogs(logs)
|
transactionsSyncErr := watcher.Syncer.SyncTransactions(header.Id, logs)
|
||||||
|
if transactionsSyncErr != nil {
|
||||||
|
logrus.Errorf("error syncing transactions: %s", transactionsSyncErr.Error())
|
||||||
|
return transactionsSyncErr
|
||||||
|
}
|
||||||
|
|
||||||
// Can't quit early and mark as checked if there are no logs. If we are running continuousLogSync,
|
transformErr := watcher.transformLogs(logs, header)
|
||||||
// not all logs we're interested in might have been fetched.
|
if transformErr != nil {
|
||||||
for _, t := range watcher.Transformers {
|
return transformErr
|
||||||
transformerName := t.GetConfig().TransformerName
|
|
||||||
logChunk := chunkedLogs[transformerName]
|
|
||||||
err = t.Execute(logChunk, header, constants.HeaderMissing)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%v transformer failed to execute in watcher: %v", transformerName, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (watcher *EventWatcher) transformLogs(logs []types.Log, header core.Header) error {
|
||||||
|
chunkedLogs := watcher.Chunker.ChunkLogs(logs)
|
||||||
|
|
||||||
|
// Can't quit early and mark as checked if there are no logs. If we are running continuousLogSync,
|
||||||
|
// not all logs we're interested in might have been fetched.
|
||||||
|
for _, t := range watcher.Transformers {
|
||||||
|
transformerName := t.GetConfig().TransformerName
|
||||||
|
logChunk := chunkedLogs[transformerName]
|
||||||
|
err := t.Execute(logChunk, header, constants.HeaderMissing)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("%v transformer failed to execute in watcher: %v", transformerName, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func earlierStartingBlockNumber(transformerBlock, watcherBlock int64) bool {
|
func earlierStartingBlockNumber(transformerBlock, watcherBlock int64) bool {
|
||||||
return transformerBlock < watcherBlock
|
return transformerBlock < watcherBlock
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,33 @@ var _ = Describe("Watcher", func() {
|
|||||||
w = watcher.NewEventWatcher(db, &mockBlockChain)
|
w = watcher.NewEventWatcher(db, &mockBlockChain)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("syncs transactions for fetched logs", func() {
|
||||||
|
fakeTransformer := &mocks.MockTransformer{}
|
||||||
|
w.AddTransformers([]transformer.EventTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
||||||
|
repository.SetMissingHeaders([]core.Header{fakes.FakeHeader})
|
||||||
|
mockTransactionSyncer := &fakes.MockTransactionSyncer{}
|
||||||
|
w.Syncer = mockTransactionSyncer
|
||||||
|
|
||||||
|
err := w.Execute(constants.HeaderMissing)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(mockTransactionSyncer.SyncTransactionsCalled).To(BeTrue())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns error if syncing transactions fails", func() {
|
||||||
|
fakeTransformer := &mocks.MockTransformer{}
|
||||||
|
w.AddTransformers([]transformer.EventTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
||||||
|
repository.SetMissingHeaders([]core.Header{fakes.FakeHeader})
|
||||||
|
mockTransactionSyncer := &fakes.MockTransactionSyncer{}
|
||||||
|
mockTransactionSyncer.SyncTransactionsError = fakes.FakeError
|
||||||
|
w.Syncer = mockTransactionSyncer
|
||||||
|
|
||||||
|
err := w.Execute(constants.HeaderMissing)
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
})
|
||||||
|
|
||||||
It("executes each transformer", func() {
|
It("executes each transformer", func() {
|
||||||
fakeTransformer := &mocks.MockTransformer{}
|
fakeTransformer := &mocks.MockTransformer{}
|
||||||
w.AddTransformers([]transformer.EventTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
w.AddTransformers([]transformer.EventTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package retriever_test
|
package retriever_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"strings"
|
"strings"
|
||||||
@ -32,19 +33,24 @@ import (
|
|||||||
var _ = Describe("Block Retriever", func() {
|
var _ = Describe("Block Retriever", func() {
|
||||||
var db *postgres.DB
|
var db *postgres.DB
|
||||||
var r retriever.BlockRetriever
|
var r retriever.BlockRetriever
|
||||||
|
var rawTransaction []byte
|
||||||
var blockRepository repositories.BlockRepository
|
var blockRepository repositories.BlockRepository
|
||||||
|
|
||||||
// Contains no contract address
|
// Contains no contract address
|
||||||
var block1 = core.Block{
|
var block1 = core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
||||||
Number: 1,
|
Number: 1,
|
||||||
Transactions: []core.Transaction{},
|
Transactions: []core.TransactionModel{},
|
||||||
}
|
}
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
db, _ = test_helpers.SetupDBandBC()
|
db, _ = test_helpers.SetupDBandBC()
|
||||||
blockRepository = *repositories.NewBlockRepository(db)
|
blockRepository = *repositories.NewBlockRepository(db)
|
||||||
r = retriever.NewBlockRetriever(db)
|
r = retriever.NewBlockRetriever(db)
|
||||||
|
gethTransaction := types.Transaction{}
|
||||||
|
var err error
|
||||||
|
rawTransaction, err = gethTransaction.MarshalJSON()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
@ -53,18 +59,23 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
|
|
||||||
Describe("RetrieveFirstBlock", func() {
|
Describe("RetrieveFirstBlock", func() {
|
||||||
It("Retrieves block number where contract first appears in receipt, if available", func() {
|
It("Retrieves block number where contract first appears in receipt, if available", func() {
|
||||||
|
|
||||||
// Contains the address in the receipt
|
// Contains the address in the receipt
|
||||||
block2 := core.Block{
|
block2 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
||||||
Number: 2,
|
Number: 2,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
||||||
|
GasPrice: 0,
|
||||||
|
GasLimit: 0,
|
||||||
|
Nonce: 0,
|
||||||
|
Raw: rawTransaction,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
||||||
ContractAddress: constants.TusdContractAddress,
|
ContractAddress: constants.TusdContractAddress,
|
||||||
Logs: []core.Log{},
|
Logs: []core.Log{},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,8 +83,12 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
block3 := core.Block{
|
block3 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
||||||
Number: 3,
|
Number: 3,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
||||||
|
GasPrice: 0,
|
||||||
|
GasLimit: 0,
|
||||||
|
Nonce: 0,
|
||||||
|
Raw: rawTransaction,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
||||||
ContractAddress: constants.TusdContractAddress,
|
ContractAddress: constants.TusdContractAddress,
|
||||||
@ -91,12 +106,17 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block1)
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(block1)
|
||||||
blockRepository.CreateOrUpdateBlock(block2)
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
blockRepository.CreateOrUpdateBlock(block3)
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(block2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
_, insertErrThree := blockRepository.CreateOrUpdateBlock(block3)
|
||||||
|
Expect(insertErrThree).NotTo(HaveOccurred())
|
||||||
|
|
||||||
i, err := r.RetrieveFirstBlock(strings.ToLower(constants.TusdContractAddress))
|
i, err := r.RetrieveFirstBlock(strings.ToLower(constants.TusdContractAddress))
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -104,12 +124,15 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Retrieves block number where contract first appears in event logs if it cannot find the address in a receipt", func() {
|
It("Retrieves block number where contract first appears in event logs if it cannot find the address in a receipt", func() {
|
||||||
|
|
||||||
block2 := core.Block{
|
block2 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
||||||
Number: 2,
|
Number: 2,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
||||||
|
GasPrice: 0,
|
||||||
|
GasLimit: 0,
|
||||||
|
Nonce: 0,
|
||||||
|
Raw: rawTransaction,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae",
|
||||||
ContractAddress: "",
|
ContractAddress: "",
|
||||||
@ -127,14 +150,20 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
block3 := core.Block{
|
block3 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
||||||
Number: 3,
|
Number: 3,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
||||||
|
GasPrice: 0,
|
||||||
|
GasLimit: 0,
|
||||||
|
Nonce: 0,
|
||||||
|
Raw: rawTransaction,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad234hfs",
|
||||||
ContractAddress: "",
|
ContractAddress: "",
|
||||||
@ -152,12 +181,17 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block1)
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(block1)
|
||||||
blockRepository.CreateOrUpdateBlock(block2)
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
blockRepository.CreateOrUpdateBlock(block3)
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(block2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
_, insertErrThree := blockRepository.CreateOrUpdateBlock(block3)
|
||||||
|
Expect(insertErrThree).NotTo(HaveOccurred())
|
||||||
|
|
||||||
i, err := r.RetrieveFirstBlock(constants.DaiContractAddress)
|
i, err := r.RetrieveFirstBlock(constants.DaiContractAddress)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -168,18 +202,21 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
block2 := core.Block{
|
block2 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
||||||
Number: 2,
|
Number: 2,
|
||||||
Transactions: []core.Transaction{},
|
Transactions: []core.TransactionModel{},
|
||||||
}
|
}
|
||||||
|
|
||||||
block3 := core.Block{
|
block3 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
||||||
Number: 3,
|
Number: 3,
|
||||||
Transactions: []core.Transaction{},
|
Transactions: []core.TransactionModel{},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block1)
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(block1)
|
||||||
blockRepository.CreateOrUpdateBlock(block2)
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
blockRepository.CreateOrUpdateBlock(block3)
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(block2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
_, insertErrThree := blockRepository.CreateOrUpdateBlock(block3)
|
||||||
|
Expect(insertErrThree).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err := r.RetrieveFirstBlock(constants.DaiContractAddress)
|
_, err := r.RetrieveFirstBlock(constants.DaiContractAddress)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
@ -191,18 +228,21 @@ var _ = Describe("Block Retriever", func() {
|
|||||||
block2 := core.Block{
|
block2 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
||||||
Number: 2,
|
Number: 2,
|
||||||
Transactions: []core.Transaction{},
|
Transactions: []core.TransactionModel{},
|
||||||
}
|
}
|
||||||
|
|
||||||
block3 := core.Block{
|
block3 := core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad456yui",
|
||||||
Number: 3,
|
Number: 3,
|
||||||
Transactions: []core.Transaction{},
|
Transactions: []core.TransactionModel{},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block1)
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(block1)
|
||||||
blockRepository.CreateOrUpdateBlock(block2)
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
blockRepository.CreateOrUpdateBlock(block3)
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(block2)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
_, insertErrThree := blockRepository.CreateOrUpdateBlock(block3)
|
||||||
|
Expect(insertErrThree).NotTo(HaveOccurred())
|
||||||
|
|
||||||
i, err := r.RetrieveMostRecentBlock()
|
i, err := r.RetrieveMostRecentBlock()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
@ -237,7 +237,10 @@ func TearDown(db *postgres.DB) {
|
|||||||
_, err = tx.Exec(`DELETE FROM log_filters`)
|
_, err = tx.Exec(`DELETE FROM log_filters`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DELETE FROM transactions`)
|
_, err = tx.Exec(`DELETE FROM full_sync_transactions`)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
_, err = tx.Exec("DELETE FROM light_sync_transactions")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DELETE FROM receipts`)
|
_, err = tx.Exec(`DELETE FROM receipts`)
|
||||||
|
@ -33,8 +33,11 @@ import (
|
|||||||
var TransferBlock1 = core.Block{
|
var TransferBlock1 = core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
||||||
Number: 6194633,
|
Number: 6194633,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
GasLimit: 0,
|
||||||
|
GasPrice: 0,
|
||||||
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
||||||
|
Nonce: 0,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
||||||
ContractAddress: "",
|
ContractAddress: "",
|
||||||
@ -52,14 +55,19 @@ var TransferBlock1 = core.Block{
|
|||||||
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var TransferBlock2 = core.Block{
|
var TransferBlock2 = core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ooo",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ooo",
|
||||||
Number: 6194634,
|
Number: 6194634,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee",
|
GasLimit: 0,
|
||||||
|
GasPrice: 0,
|
||||||
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee",
|
||||||
|
Nonce: 0,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee",
|
||||||
ContractAddress: "",
|
ContractAddress: "",
|
||||||
@ -77,14 +85,19 @@ var TransferBlock2 = core.Block{
|
|||||||
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var NewOwnerBlock1 = core.Block{
|
var NewOwnerBlock1 = core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ppp",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ppp",
|
||||||
Number: 6194635,
|
Number: 6194635,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654bbb",
|
GasLimit: 0,
|
||||||
|
GasPrice: 0,
|
||||||
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654bbb",
|
||||||
|
Nonce: 0,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654bbb",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654bbb",
|
||||||
ContractAddress: "",
|
ContractAddress: "",
|
||||||
@ -102,14 +115,19 @@ var NewOwnerBlock1 = core.Block{
|
|||||||
Data: "0x000000000000000000000000000000000000000000000000000000000000af21",
|
Data: "0x000000000000000000000000000000000000000000000000000000000000af21",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var NewOwnerBlock2 = core.Block{
|
var NewOwnerBlock2 = core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ggg",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ggg",
|
||||||
Number: 6194636,
|
Number: 6194636,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.TransactionModel{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654lll",
|
GasLimit: 0,
|
||||||
|
GasPrice: 0,
|
||||||
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654lll",
|
||||||
|
Nonce: 0,
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654lll",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654lll",
|
||||||
ContractAddress: "",
|
ContractAddress: "",
|
||||||
@ -127,6 +145,8 @@ var NewOwnerBlock2 = core.Block{
|
|||||||
Data: "0x000000000000000000000000000000000000000000000000000000000000af21",
|
Data: "0x000000000000000000000000000000000000000000000000000000000000af21",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ type Block struct {
|
|||||||
ParentHash string `db:"parenthash"`
|
ParentHash string `db:"parenthash"`
|
||||||
Size string `db:"size"`
|
Size string `db:"size"`
|
||||||
Time int64 `db:"time"`
|
Time int64 `db:"time"`
|
||||||
Transactions []Transaction
|
Transactions []TransactionModel
|
||||||
UncleHash string `db:"uncle_hash"`
|
UncleHash string `db:"uncle_hash"`
|
||||||
UnclesReward float64 `db:"uncles_reward"`
|
UnclesReward float64 `db:"uncles_reward"`
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
@ -26,10 +27,11 @@ import (
|
|||||||
type BlockChain interface {
|
type BlockChain interface {
|
||||||
ContractDataFetcher
|
ContractDataFetcher
|
||||||
GetBlockByNumber(blockNumber int64) (Block, error)
|
GetBlockByNumber(blockNumber int64) (Block, error)
|
||||||
|
GetEthLogsWithCustomQuery(query ethereum.FilterQuery) ([]types.Log, error)
|
||||||
GetHeaderByNumber(blockNumber int64) (Header, error)
|
GetHeaderByNumber(blockNumber int64) (Header, error)
|
||||||
GetHeaderByNumbers(blockNumbers []int64) ([]Header, error)
|
GetHeaderByNumbers(blockNumbers []int64) ([]Header, error)
|
||||||
GetLogs(contract Contract, startingBlockNumber *big.Int, endingBlockNumber *big.Int) ([]Log, error)
|
GetLogs(contract Contract, startingBlockNumber *big.Int, endingBlockNumber *big.Int) ([]Log, error)
|
||||||
GetEthLogsWithCustomQuery(query ethereum.FilterQuery) ([]types.Log, error)
|
GetTransactions(transactionHashes []common.Hash) ([]TransactionModel, error)
|
||||||
LastBlock() (*big.Int, error)
|
LastBlock() (*big.Int, error)
|
||||||
Node() Node
|
Node() Node
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,5 @@ package core
|
|||||||
type Contract struct {
|
type Contract struct {
|
||||||
Abi string
|
Abi string
|
||||||
Hash string
|
Hash string
|
||||||
Transactions []Transaction
|
Transactions []TransactionModel
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,31 @@
|
|||||||
|
|
||||||
package core
|
package core
|
||||||
|
|
||||||
type Transaction struct {
|
type TransactionModel struct {
|
||||||
Hash string `db:"hash"`
|
Data []byte `db:"input_data"`
|
||||||
Data string `db:"input_data"`
|
|
||||||
Nonce uint64 `db:"nonce"`
|
|
||||||
To string `db:"tx_to"`
|
|
||||||
From string `db:"tx_from"`
|
From string `db:"tx_from"`
|
||||||
GasLimit uint64 `db:"gaslimit"`
|
GasLimit uint64
|
||||||
GasPrice int64 `db:"gasprice"`
|
GasPrice int64
|
||||||
|
Hash string
|
||||||
|
Nonce uint64
|
||||||
|
Raw []byte
|
||||||
Receipt
|
Receipt
|
||||||
Value string `db:"value"`
|
To string `db:"tx_to"`
|
||||||
|
TxIndex int64 `db:"tx_index"`
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcTransaction struct {
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
GasPrice string `json:"gasPrice"`
|
||||||
|
GasLimit string `json:"gas"`
|
||||||
|
Recipient string `json:"to"`
|
||||||
|
Amount string `json:"value"`
|
||||||
|
Payload string `json:"input"`
|
||||||
|
V string `json:"v"`
|
||||||
|
R string `json:"r"`
|
||||||
|
S string `json:"s"`
|
||||||
|
Hash string
|
||||||
|
From string
|
||||||
|
TransactionIndex string `json:"transactionIndex"`
|
||||||
}
|
}
|
||||||
|
19
pkg/datastore/errors.go
Normal file
19
pkg/datastore/errors.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package datastore
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func ErrBlockDoesNotExist(blockNumber int64) error {
|
||||||
|
return fmt.Errorf("Block number %d does not exist", blockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrContractDoesNotExist(contractHash string) error {
|
||||||
|
return fmt.Errorf("Contract %v does not exist", contractHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrFilterDoesNotExist(name string) error {
|
||||||
|
return fmt.Errorf("filter %s does not exist", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrReceiptDoesNotExist(txHash string) error {
|
||||||
|
return fmt.Errorf("Receipt for tx: %v does not exist", txHash)
|
||||||
|
}
|
38
pkg/datastore/postgres/errors.go
Normal file
38
pkg/datastore/postgres/errors.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BeginTransactionFailedMsg = "failed to begin transaction"
|
||||||
|
DbConnectionFailedMsg = "db connection failed"
|
||||||
|
DeleteQueryFailedMsg = "delete query failed"
|
||||||
|
InsertQueryFailedMsg = "insert query failed"
|
||||||
|
SettingNodeFailedMsg = "unable to set db node"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ErrBeginTransactionFailed(beginErr error) error {
|
||||||
|
return formatError(BeginTransactionFailedMsg, beginErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrDBConnectionFailed(connectErr error) error {
|
||||||
|
return formatError(DbConnectionFailedMsg, connectErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrDBDeleteFailed(deleteErr error) error {
|
||||||
|
return formatError(DeleteQueryFailedMsg, deleteErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrDBInsertFailed(insertErr error) error {
|
||||||
|
return formatError(InsertQueryFailedMsg, insertErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrUnableToSetNode(setErr error) error {
|
||||||
|
return formatError(SettingNodeFailedMsg, setErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatError(msg, err string) error {
|
||||||
|
return errors.New(fmt.Sprintf("%s: %s", msg, err))
|
||||||
|
}
|
@ -17,8 +17,6 @@
|
|||||||
package postgres
|
package postgres
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/lib/pq" //postgres driver
|
_ "github.com/lib/pq" //postgres driver
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||||
@ -31,23 +29,18 @@ type DB struct {
|
|||||||
NodeID int64
|
NodeID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var ()
|
||||||
ErrDBInsertFailed = errors.New("postgres: insert failed")
|
|
||||||
ErrDBDeleteFailed = errors.New("postgres: delete failed")
|
|
||||||
ErrDBConnectionFailed = errors.New("postgres: db connection failed")
|
|
||||||
ErrUnableToSetNode = errors.New("postgres: unable to set node")
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewDB(databaseConfig config.Database, node core.Node) (*DB, error) {
|
func NewDB(databaseConfig config.Database, node core.Node) (*DB, error) {
|
||||||
connectString := config.DbConnectionString(databaseConfig)
|
connectString := config.DbConnectionString(databaseConfig)
|
||||||
db, err := sqlx.Connect("postgres", connectString)
|
db, connectErr := sqlx.Connect("postgres", connectString)
|
||||||
if err != nil {
|
if connectErr != nil {
|
||||||
return &DB{}, ErrDBConnectionFailed
|
return &DB{}, ErrDBConnectionFailed(connectErr)
|
||||||
}
|
}
|
||||||
pg := DB{DB: db, Node: node}
|
pg := DB{DB: db, Node: node}
|
||||||
err = pg.CreateNode(&node)
|
nodeErr := pg.CreateNode(&node)
|
||||||
if err != nil {
|
if nodeErr != nil {
|
||||||
return &DB{}, ErrUnableToSetNode
|
return &DB{}, ErrUnableToSetNode(nodeErr)
|
||||||
}
|
}
|
||||||
return &pg, nil
|
return &pg, nil
|
||||||
}
|
}
|
||||||
@ -66,7 +59,7 @@ func (db *DB) CreateNode(node *core.Node) error {
|
|||||||
RETURNING id`,
|
RETURNING id`,
|
||||||
node.GenesisBlock, node.NetworkID, node.ID, node.ClientName).Scan(&nodeId)
|
node.GenesisBlock, node.NetworkID, node.ID, node.ClientName).Scan(&nodeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrUnableToSetNode
|
return ErrUnableToSetNode(err)
|
||||||
}
|
}
|
||||||
db.NodeID = nodeId
|
db.NodeID = nodeId
|
||||||
return nil
|
return nil
|
||||||
|
@ -88,7 +88,7 @@ var _ = Describe("Postgres DB", func() {
|
|||||||
badBlock := core.Block{
|
badBlock := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Nonce: badNonce,
|
Nonce: badNonce,
|
||||||
Transactions: []core.Transaction{},
|
Transactions: []core.TransactionModel{},
|
||||||
}
|
}
|
||||||
node := core.Node{GenesisBlock: "GENESIS", NetworkID: 1, ID: "x123", ClientName: "geth"}
|
node := core.Node{GenesisBlock: "GENESIS", NetworkID: 1, ID: "x123", ClientName: "geth"}
|
||||||
db := test_config.NewTestDB(node)
|
db := test_config.NewTestDB(node)
|
||||||
@ -109,14 +109,18 @@ var _ = Describe("Postgres DB", func() {
|
|||||||
|
|
||||||
_, err := postgres.NewDB(invalidDatabase, node)
|
_, err := postgres.NewDB(invalidDatabase, node)
|
||||||
|
|
||||||
Expect(err).To(Equal(postgres.ErrDBConnectionFailed))
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err.Error()).To(ContainSubstring(postgres.DbConnectionFailedMsg))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("throws error when can't create node", func() {
|
It("throws error when can't create node", func() {
|
||||||
badHash := fmt.Sprintf("x %s", strings.Repeat("1", 100))
|
badHash := fmt.Sprintf("x %s", strings.Repeat("1", 100))
|
||||||
node := core.Node{GenesisBlock: badHash, NetworkID: 1, ID: "x123", ClientName: "geth"}
|
node := core.Node{GenesisBlock: badHash, NetworkID: 1, ID: "x123", ClientName: "geth"}
|
||||||
|
|
||||||
_, err := postgres.NewDB(test_config.DBConfig, node)
|
_, err := postgres.NewDB(test_config.DBConfig, node)
|
||||||
Expect(err).To(Equal(postgres.ErrUnableToSetNode))
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(err.Error()).To(ContainSubstring(postgres.SettingNodeFailedMsg))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("does not commit log if log is invalid", func() {
|
It("does not commit log if log is invalid", func() {
|
||||||
@ -142,10 +146,10 @@ var _ = Describe("Postgres DB", func() {
|
|||||||
It("does not commit block or transactions if transaction is invalid", func() {
|
It("does not commit block or transactions if transaction is invalid", func() {
|
||||||
//badHash violates db To field length
|
//badHash violates db To field length
|
||||||
badHash := fmt.Sprintf("x %s", strings.Repeat("1", 100))
|
badHash := fmt.Sprintf("x %s", strings.Repeat("1", 100))
|
||||||
badTransaction := core.Transaction{To: badHash}
|
badTransaction := core.TransactionModel{To: badHash}
|
||||||
block := core.Block{
|
block := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Transactions: []core.Transaction{badTransaction},
|
Transactions: []core.TransactionModel{badTransaction},
|
||||||
}
|
}
|
||||||
node := core.Node{GenesisBlock: "GENESIS", NetworkID: 1, ID: "x123", ClientName: "geth"}
|
node := core.Node{GenesisBlock: "GENESIS", NetworkID: 1, ID: "x123", ClientName: "geth"}
|
||||||
db, _ := postgres.NewDB(test_config.DBConfig, node)
|
db, _ := postgres.NewDB(test_config.DBConfig, node)
|
||||||
|
@ -121,30 +121,46 @@ func (blockRepository BlockRepository) GetBlock(blockNumber int64) (core.Block,
|
|||||||
|
|
||||||
func (blockRepository BlockRepository) insertBlock(block core.Block) (int64, error) {
|
func (blockRepository BlockRepository) insertBlock(block core.Block) (int64, error) {
|
||||||
var blockId int64
|
var blockId int64
|
||||||
tx, _ := blockRepository.database.Beginx()
|
tx, beginErr := blockRepository.database.Beginx()
|
||||||
err := tx.QueryRow(
|
if beginErr != nil {
|
||||||
|
return 0, postgres.ErrBeginTransactionFailed(beginErr)
|
||||||
|
}
|
||||||
|
insertBlockErr := tx.QueryRow(
|
||||||
`INSERT INTO blocks
|
`INSERT INTO blocks
|
||||||
(eth_node_id, number, gaslimit, gasused, time, difficulty, hash, nonce, parenthash, size, uncle_hash, is_final, miner, extra_data, reward, uncles_reward, eth_node_fingerprint)
|
(eth_node_id, number, gaslimit, gasused, time, difficulty, hash, nonce, parenthash, size, uncle_hash, is_final, miner, extra_data, reward, uncles_reward, eth_node_fingerprint)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||||
RETURNING id `,
|
RETURNING id `,
|
||||||
blockRepository.database.NodeID, block.Number, block.GasLimit, block.GasUsed, block.Time, block.Difficulty, block.Hash, block.Nonce, block.ParentHash, block.Size, block.UncleHash, block.IsFinal, block.Miner, block.ExtraData, block.Reward, block.UnclesReward, blockRepository.database.Node.ID).
|
blockRepository.database.NodeID, block.Number, block.GasLimit, block.GasUsed, block.Time, block.Difficulty, block.Hash, block.Nonce, block.ParentHash, block.Size, block.UncleHash, block.IsFinal, block.Miner, block.ExtraData, block.Reward, block.UnclesReward, blockRepository.database.Node.ID).
|
||||||
Scan(&blockId)
|
Scan(&blockId)
|
||||||
if err != nil {
|
if insertBlockErr != nil {
|
||||||
tx.Rollback()
|
rollbackErr := tx.Rollback()
|
||||||
return 0, err
|
if rollbackErr != nil {
|
||||||
|
log.Error("failed to rollback transaction: ", rollbackErr)
|
||||||
|
}
|
||||||
|
return 0, postgres.ErrDBInsertFailed(insertBlockErr)
|
||||||
}
|
}
|
||||||
if len(block.Transactions) > 0 {
|
if len(block.Transactions) > 0 {
|
||||||
err = blockRepository.createTransactions(tx, blockId, block.Transactions)
|
insertTxErr := blockRepository.createTransactions(tx, blockId, block.Transactions)
|
||||||
if err != nil {
|
if insertTxErr != nil {
|
||||||
tx.Rollback()
|
rollbackErr := tx.Rollback()
|
||||||
return 0, postgres.ErrDBInsertFailed
|
if rollbackErr != nil {
|
||||||
|
log.Warn("failed to rollback transaction: ", rollbackErr)
|
||||||
|
}
|
||||||
|
return 0, postgres.ErrDBInsertFailed(insertTxErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.Commit()
|
commitErr := tx.Commit()
|
||||||
|
if commitErr != nil {
|
||||||
|
rollbackErr := tx.Rollback()
|
||||||
|
if rollbackErr != nil {
|
||||||
|
log.Warn("failed to rollback transaction: ", rollbackErr)
|
||||||
|
}
|
||||||
|
return 0, commitErr
|
||||||
|
}
|
||||||
return blockId, nil
|
return blockId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockRepository BlockRepository) createTransactions(tx *sqlx.Tx, blockId int64, transactions []core.Transaction) error {
|
func (blockRepository BlockRepository) createTransactions(tx *sqlx.Tx, blockId int64, transactions []core.TransactionModel) error {
|
||||||
for _, transaction := range transactions {
|
for _, transaction := range transactions {
|
||||||
err := blockRepository.createTransaction(tx, blockId, transaction)
|
err := blockRepository.createTransaction(tx, blockId, transaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -164,13 +180,13 @@ func nullStringToZero(s string) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockRepository BlockRepository) createTransaction(tx *sqlx.Tx, blockId int64, transaction core.Transaction) error {
|
func (blockRepository BlockRepository) createTransaction(tx *sqlx.Tx, blockId int64, transaction core.TransactionModel) error {
|
||||||
_, err := tx.Exec(
|
_, err := tx.Exec(
|
||||||
`INSERT INTO transactions
|
`INSERT INTO full_sync_transactions
|
||||||
(block_id, hash, nonce, tx_to, tx_from, gaslimit, gasprice, value, input_data)
|
(block_id, gaslimit, gasprice, hash, input_data, nonce, raw, tx_from, tx_index, tx_to, "value")
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::NUMERIC, $9)
|
VALUES ($1, $2::NUMERIC, $3::NUMERIC, $4, $5, $6::NUMERIC, $7, $8, $9::NUMERIC, $10, $11::NUMERIC)
|
||||||
RETURNING id`,
|
RETURNING id`, blockId, transaction.GasLimit, transaction.GasPrice, transaction.Hash, transaction.Data,
|
||||||
blockId, transaction.Hash, transaction.Nonce, transaction.To, transaction.From, transaction.GasLimit, transaction.GasPrice, nullStringToZero(transaction.Value), transaction.Data)
|
transaction.Nonce, transaction.Raw, transaction.From, transaction.TxIndex, transaction.To, transaction.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -189,11 +205,11 @@ func (blockRepository BlockRepository) createTransaction(tx *sqlx.Tx, blockId in
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasLogs(transaction core.Transaction) bool {
|
func hasLogs(transaction core.TransactionModel) bool {
|
||||||
return len(transaction.Receipt.Logs) > 0
|
return len(transaction.Receipt.Logs) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasReceipt(transaction core.Transaction) bool {
|
func hasReceipt(transaction core.TransactionModel) bool {
|
||||||
return transaction.Receipt.TxHash != ""
|
return transaction.Receipt.TxHash != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +231,7 @@ func (blockRepository BlockRepository) createReceipt(tx *sqlx.Tx, blockId int64,
|
|||||||
|
|
||||||
func (blockRepository BlockRepository) getBlockHash(block core.Block) (string, bool) {
|
func (blockRepository BlockRepository) getBlockHash(block core.Block) (string, bool) {
|
||||||
var retrievedBlockHash string
|
var retrievedBlockHash string
|
||||||
|
// TODO: handle possible error
|
||||||
blockRepository.database.Get(&retrievedBlockHash,
|
blockRepository.database.Get(&retrievedBlockHash,
|
||||||
`SELECT hash
|
`SELECT hash
|
||||||
FROM blocks
|
FROM blocks
|
||||||
@ -232,7 +249,7 @@ func (blockRepository BlockRepository) createLogs(tx *sqlx.Tx, logs []core.Log,
|
|||||||
tlog.BlockNumber, tlog.Address, tlog.TxHash, tlog.Index, tlog.Topics[0], tlog.Topics[1], tlog.Topics[2], tlog.Topics[3], tlog.Data, receiptId,
|
tlog.BlockNumber, tlog.Address, tlog.TxHash, tlog.Index, tlog.Topics[0], tlog.Topics[1], tlog.Topics[2], tlog.Topics[3], tlog.Data, receiptId,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return postgres.ErrDBInsertFailed
|
return postgres.ErrDBInsertFailed(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -244,12 +261,10 @@ func blockExists(retrievedBlockHash string) bool {
|
|||||||
|
|
||||||
func (blockRepository BlockRepository) removeBlock(blockNumber int64) error {
|
func (blockRepository BlockRepository) removeBlock(blockNumber int64) error {
|
||||||
_, err := blockRepository.database.Exec(
|
_, err := blockRepository.database.Exec(
|
||||||
`DELETE FROM
|
`DELETE FROM blocks WHERE number=$1 AND eth_node_id=$2`,
|
||||||
blocks
|
|
||||||
WHERE number=$1 AND eth_node_id=$2`,
|
|
||||||
blockNumber, blockRepository.database.NodeID)
|
blockNumber, blockRepository.database.NodeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return postgres.ErrDBDeleteFailed
|
return postgres.ErrDBDeleteFailed(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -266,17 +281,19 @@ func (blockRepository BlockRepository) loadBlock(blockRows *sqlx.Row) (core.Bloc
|
|||||||
return core.Block{}, err
|
return core.Block{}, err
|
||||||
}
|
}
|
||||||
transactionRows, err := blockRepository.database.Queryx(`
|
transactionRows, err := blockRepository.database.Queryx(`
|
||||||
SELECT hash,
|
SELECT hash,
|
||||||
nonce,
|
gaslimit,
|
||||||
tx_to,
|
gasprice,
|
||||||
tx_from,
|
input_data,
|
||||||
gaslimit,
|
nonce,
|
||||||
gasprice,
|
raw,
|
||||||
value,
|
tx_from,
|
||||||
input_data
|
tx_index,
|
||||||
FROM transactions
|
tx_to,
|
||||||
WHERE block_id = $1
|
value
|
||||||
ORDER BY hash`, block.ID)
|
FROM full_sync_transactions
|
||||||
|
WHERE block_id = $1
|
||||||
|
ORDER BY hash`, block.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("loadBlock: error fetting transactions: ", err)
|
log.Error("loadBlock: error fetting transactions: ", err)
|
||||||
return core.Block{}, err
|
return core.Block{}, err
|
||||||
@ -285,10 +302,10 @@ func (blockRepository BlockRepository) loadBlock(blockRows *sqlx.Row) (core.Bloc
|
|||||||
return block.Block, nil
|
return block.Block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockRepository BlockRepository) LoadTransactions(transactionRows *sqlx.Rows) []core.Transaction {
|
func (blockRepository BlockRepository) LoadTransactions(transactionRows *sqlx.Rows) []core.TransactionModel {
|
||||||
var transactions []core.Transaction
|
var transactions []core.TransactionModel
|
||||||
for transactionRows.Next() {
|
for transactionRows.Next() {
|
||||||
var transaction core.Transaction
|
var transaction core.TransactionModel
|
||||||
err := transactionRows.StructScan(&transaction)
|
err := transactionRows.StructScan(&transaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
package repositories_test
|
package repositories_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -51,7 +55,8 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
block := core.Block{
|
block := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
}
|
}
|
||||||
blockRepository.CreateOrUpdateBlock(block)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(block)
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
nodeTwo := core.Node{
|
nodeTwo := core.Node{
|
||||||
GenesisBlock: "0x456",
|
GenesisBlock: "0x456",
|
||||||
NetworkID: 1,
|
NetworkID: 1,
|
||||||
@ -98,8 +103,9 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
UnclesReward: unclesReward,
|
UnclesReward: unclesReward,
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(block)
|
||||||
|
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
savedBlock, err := blockRepository.GetBlock(blockNumber)
|
savedBlock, err := blockRepository.GetBlock(blockNumber)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(savedBlock.Reward).To(Equal(blockReward))
|
Expect(savedBlock.Reward).To(Equal(blockReward))
|
||||||
@ -127,42 +133,54 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
It("saves one transaction associated to the block", func() {
|
It("saves one transaction associated to the block", func() {
|
||||||
block := core.Block{
|
block := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Transactions: []core.Transaction{{}},
|
Transactions: []core.TransactionModel{fakes.FakeTransaction},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(block)
|
||||||
|
|
||||||
savedBlock, _ := blockRepository.GetBlock(123)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
savedBlock, getErr := blockRepository.GetBlock(123)
|
||||||
|
Expect(getErr).NotTo(HaveOccurred())
|
||||||
Expect(len(savedBlock.Transactions)).To(Equal(1))
|
Expect(len(savedBlock.Transactions)).To(Equal(1))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("saves two transactions associated to the block", func() {
|
It("saves two transactions associated to the block", func() {
|
||||||
block := core.Block{
|
block := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Transactions: []core.Transaction{{}, {}},
|
Transactions: []core.TransactionModel{fakes.FakeTransaction, fakes.FakeTransaction},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(block)
|
||||||
|
|
||||||
savedBlock, _ := blockRepository.GetBlock(123)
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
savedBlock, getErr := blockRepository.GetBlock(123)
|
||||||
|
Expect(getErr).NotTo(HaveOccurred())
|
||||||
Expect(len(savedBlock.Transactions)).To(Equal(2))
|
Expect(len(savedBlock.Transactions)).To(Equal(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It(`replaces blocks and transactions associated to the block
|
It(`replaces blocks and transactions associated to the block
|
||||||
when a more new block is in conflict (same block number + nodeid)`, func() {
|
when a more new block is in conflict (same block number + nodeid)`, func() {
|
||||||
blockOne := core.Block{
|
blockOne := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Hash: "xabc",
|
Hash: "xabc",
|
||||||
Transactions: []core.Transaction{{Hash: "x123"}, {Hash: "x345"}},
|
Transactions: []core.TransactionModel{
|
||||||
|
fakes.GetFakeTransaction("x123", core.Receipt{}),
|
||||||
|
fakes.GetFakeTransaction("x345", core.Receipt{}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
blockTwo := core.Block{
|
blockTwo := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Hash: "xdef",
|
Hash: "xdef",
|
||||||
Transactions: []core.Transaction{{Hash: "x678"}, {Hash: "x9ab"}},
|
Transactions: []core.TransactionModel{
|
||||||
|
fakes.GetFakeTransaction("x678", core.Receipt{}),
|
||||||
|
fakes.GetFakeTransaction("x9ab", core.Receipt{}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(blockOne)
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(blockOne)
|
||||||
blockRepository.CreateOrUpdateBlock(blockTwo)
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(blockTwo)
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
|
||||||
savedBlock, _ := blockRepository.GetBlock(123)
|
savedBlock, _ := blockRepository.GetBlock(123)
|
||||||
Expect(len(savedBlock.Transactions)).To(Equal(2))
|
Expect(len(savedBlock.Transactions)).To(Equal(2))
|
||||||
@ -173,14 +191,21 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
It(`does not replace blocks when block number is not unique
|
It(`does not replace blocks when block number is not unique
|
||||||
but block number + node id is`, func() {
|
but block number + node id is`, func() {
|
||||||
blockOne := core.Block{
|
blockOne := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Transactions: []core.Transaction{{Hash: "x123"}, {Hash: "x345"}},
|
Transactions: []core.TransactionModel{
|
||||||
|
fakes.GetFakeTransaction("x123", core.Receipt{}),
|
||||||
|
fakes.GetFakeTransaction("x345", core.Receipt{}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
blockTwo := core.Block{
|
blockTwo := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Transactions: []core.Transaction{{Hash: "x678"}, {Hash: "x9ab"}},
|
Transactions: []core.TransactionModel{
|
||||||
|
fakes.GetFakeTransaction("x678", core.Receipt{}),
|
||||||
|
fakes.GetFakeTransaction("x9ab", core.Receipt{}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
blockRepository.CreateOrUpdateBlock(blockOne)
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(blockOne)
|
||||||
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
nodeTwo := core.Node{
|
nodeTwo := core.Node{
|
||||||
GenesisBlock: "0x456",
|
GenesisBlock: "0x456",
|
||||||
NetworkID: 1,
|
NetworkID: 1,
|
||||||
@ -189,10 +214,14 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
test_config.CleanTestDB(dbTwo)
|
test_config.CleanTestDB(dbTwo)
|
||||||
repositoryTwo := repositories.NewBlockRepository(dbTwo)
|
repositoryTwo := repositories.NewBlockRepository(dbTwo)
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(blockOne)
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(blockOne)
|
||||||
repositoryTwo.CreateOrUpdateBlock(blockTwo)
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
retrievedBlockOne, _ := blockRepository.GetBlock(123)
|
_, insertErrThree := repositoryTwo.CreateOrUpdateBlock(blockTwo)
|
||||||
retrievedBlockTwo, _ := repositoryTwo.GetBlock(123)
|
Expect(insertErrThree).NotTo(HaveOccurred())
|
||||||
|
retrievedBlockOne, getErrOne := blockRepository.GetBlock(123)
|
||||||
|
Expect(getErrOne).NotTo(HaveOccurred())
|
||||||
|
retrievedBlockTwo, getErrTwo := repositoryTwo.GetBlock(123)
|
||||||
|
Expect(getErrTwo).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(retrievedBlockOne.Transactions[0].Hash).To(Equal("x123"))
|
Expect(retrievedBlockOne.Transactions[0].Hash).To(Equal("x123"))
|
||||||
Expect(retrievedBlockTwo.Transactions[0].Hash).To(Equal("x678"))
|
Expect(retrievedBlockTwo.Transactions[0].Hash).To(Equal("x678"))
|
||||||
@ -223,46 +252,51 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
var value = new(big.Int)
|
var value = new(big.Int)
|
||||||
value.SetString("34940183920000000000", 10)
|
value.SetString("34940183920000000000", 10)
|
||||||
inputData := "0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"
|
inputData := "0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"
|
||||||
transaction := core.Transaction{
|
gethTransaction := types.NewTransaction(nonce, common.HexToAddress(to), value, gasLimit, big.NewInt(gasPrice), common.FromHex(inputData))
|
||||||
Hash: "x1234",
|
var raw bytes.Buffer
|
||||||
GasPrice: gasPrice,
|
rlpErr := gethTransaction.EncodeRLP(&raw)
|
||||||
GasLimit: gasLimit,
|
Expect(rlpErr).NotTo(HaveOccurred())
|
||||||
Nonce: nonce,
|
transaction := core.TransactionModel{
|
||||||
To: to,
|
Data: common.Hex2Bytes(inputData),
|
||||||
From: from,
|
From: from,
|
||||||
|
GasLimit: gasLimit,
|
||||||
|
GasPrice: gasPrice,
|
||||||
|
Hash: "x1234",
|
||||||
|
Nonce: nonce,
|
||||||
|
Raw: raw.Bytes(),
|
||||||
|
Receipt: core.Receipt{},
|
||||||
|
To: to,
|
||||||
|
TxIndex: 2,
|
||||||
Value: value.String(),
|
Value: value.String(),
|
||||||
Data: inputData,
|
|
||||||
}
|
}
|
||||||
block := core.Block{
|
block := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Transactions: []core.Transaction{transaction},
|
Transactions: []core.TransactionModel{transaction},
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.CreateOrUpdateBlock(block)
|
_, insertErr := blockRepository.CreateOrUpdateBlock(block)
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
savedBlock, _ := blockRepository.GetBlock(123)
|
savedBlock, err := blockRepository.GetBlock(123)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(savedBlock.Transactions)).To(Equal(1))
|
Expect(len(savedBlock.Transactions)).To(Equal(1))
|
||||||
savedTransaction := savedBlock.Transactions[0]
|
savedTransaction := savedBlock.Transactions[0]
|
||||||
Expect(savedTransaction.Data).To(Equal(transaction.Data))
|
Expect(savedTransaction).To(Equal(transaction))
|
||||||
Expect(savedTransaction.Hash).To(Equal(transaction.Hash))
|
|
||||||
Expect(savedTransaction.To).To(Equal(to))
|
|
||||||
Expect(savedTransaction.From).To(Equal(from))
|
|
||||||
Expect(savedTransaction.Nonce).To(Equal(nonce))
|
|
||||||
Expect(savedTransaction.GasLimit).To(Equal(gasLimit))
|
|
||||||
Expect(savedTransaction.GasPrice).To(Equal(gasPrice))
|
|
||||||
Expect(savedTransaction.Value).To(Equal(value.String()))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("The missing block numbers", func() {
|
Describe("The missing block numbers", func() {
|
||||||
It("is empty the starting block number is the highest known block number", func() {
|
It("is empty the starting block number is the highest known block number", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 1})
|
_, insertErr := blockRepository.CreateOrUpdateBlock(core.Block{Number: 1})
|
||||||
|
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
Expect(len(blockRepository.MissingBlockNumbers(1, 1, node.ID))).To(Equal(0))
|
Expect(len(blockRepository.MissingBlockNumbers(1, 1, node.ID))).To(Equal(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("is empty if copies of block exist from both current node and another", func() {
|
It("is empty if copies of block exist from both current node and another", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 0})
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(core.Block{Number: 0})
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 1})
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(core.Block{Number: 1})
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
nodeTwo := core.Node{
|
nodeTwo := core.Node{
|
||||||
GenesisBlock: "0x456",
|
GenesisBlock: "0x456",
|
||||||
NetworkID: 1,
|
NetworkID: 1,
|
||||||
@ -270,7 +304,8 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
dbTwo, err := postgres.NewDB(test_config.DBConfig, nodeTwo)
|
dbTwo, err := postgres.NewDB(test_config.DBConfig, nodeTwo)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
repositoryTwo := repositories.NewBlockRepository(dbTwo)
|
repositoryTwo := repositories.NewBlockRepository(dbTwo)
|
||||||
repositoryTwo.CreateOrUpdateBlock(core.Block{Number: 0})
|
_, insertErrThree := repositoryTwo.CreateOrUpdateBlock(core.Block{Number: 0})
|
||||||
|
Expect(insertErrThree).NotTo(HaveOccurred())
|
||||||
|
|
||||||
missing := blockRepository.MissingBlockNumbers(0, 1, node.ID)
|
missing := blockRepository.MissingBlockNumbers(0, 1, node.ID)
|
||||||
|
|
||||||
@ -278,42 +313,53 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("is the only missing block number", func() {
|
It("is the only missing block number", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 2})
|
_, insertErr := blockRepository.CreateOrUpdateBlock(core.Block{Number: 2})
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(blockRepository.MissingBlockNumbers(1, 2, node.ID)).To(Equal([]int64{1}))
|
Expect(blockRepository.MissingBlockNumbers(1, 2, node.ID)).To(Equal([]int64{1}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("is both missing block numbers", func() {
|
It("is both missing block numbers", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 3})
|
_, insertErr := blockRepository.CreateOrUpdateBlock(core.Block{Number: 3})
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(blockRepository.MissingBlockNumbers(1, 3, node.ID)).To(Equal([]int64{1, 2}))
|
Expect(blockRepository.MissingBlockNumbers(1, 3, node.ID)).To(Equal([]int64{1, 2}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("goes back to the starting block number", func() {
|
It("goes back to the starting block number", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 6})
|
_, insertErr := blockRepository.CreateOrUpdateBlock(core.Block{Number: 6})
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(blockRepository.MissingBlockNumbers(4, 6, node.ID)).To(Equal([]int64{4, 5}))
|
Expect(blockRepository.MissingBlockNumbers(4, 6, node.ID)).To(Equal([]int64{4, 5}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("only includes missing block numbers", func() {
|
It("only includes missing block numbers", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 4})
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(core.Block{Number: 4})
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 6})
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(core.Block{Number: 6})
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(blockRepository.MissingBlockNumbers(4, 6, node.ID)).To(Equal([]int64{5}))
|
Expect(blockRepository.MissingBlockNumbers(4, 6, node.ID)).To(Equal([]int64{5}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("includes blocks created by a different node", func() {
|
It("includes blocks created by a different node", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 4})
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(core.Block{Number: 4})
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 6})
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(core.Block{Number: 6})
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(blockRepository.MissingBlockNumbers(4, 6, "Different node id")).To(Equal([]int64{4, 5, 6}))
|
Expect(blockRepository.MissingBlockNumbers(4, 6, "Different node id")).To(Equal([]int64{4, 5, 6}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("is a list with multiple gaps", func() {
|
It("is a list with multiple gaps", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 4})
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(core.Block{Number: 4})
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 5})
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 8})
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(core.Block{Number: 5})
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 10})
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
_, insertErrThree := blockRepository.CreateOrUpdateBlock(core.Block{Number: 8})
|
||||||
|
Expect(insertErrThree).NotTo(HaveOccurred())
|
||||||
|
_, insertErrFour := blockRepository.CreateOrUpdateBlock(core.Block{Number: 10})
|
||||||
|
Expect(insertErrFour).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(blockRepository.MissingBlockNumbers(3, 10, node.ID)).To(Equal([]int64{3, 6, 7, 9}))
|
Expect(blockRepository.MissingBlockNumbers(3, 10, node.ID)).To(Equal([]int64{3, 6, 7, 9}))
|
||||||
})
|
})
|
||||||
@ -323,8 +369,10 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("only returns requested range even when other gaps exist", func() {
|
It("only returns requested range even when other gaps exist", func() {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 3})
|
_, insertErrOne := blockRepository.CreateOrUpdateBlock(core.Block{Number: 3})
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: 8})
|
Expect(insertErrOne).NotTo(HaveOccurred())
|
||||||
|
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(core.Block{Number: 8})
|
||||||
|
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(blockRepository.MissingBlockNumbers(1, 5, node.ID)).To(Equal([]int64{1, 2, 4, 5}))
|
Expect(blockRepository.MissingBlockNumbers(1, 5, node.ID)).To(Equal([]int64{1, 2, 4, 5}))
|
||||||
})
|
})
|
||||||
@ -334,11 +382,13 @@ var _ = Describe("Saving blocks", func() {
|
|||||||
It("sets the status of blocks within n-20 of chain HEAD as final", func() {
|
It("sets the status of blocks within n-20 of chain HEAD as final", func() {
|
||||||
blockNumberOfChainHead := 25
|
blockNumberOfChainHead := 25
|
||||||
for i := 0; i < blockNumberOfChainHead; i++ {
|
for i := 0; i < blockNumberOfChainHead; i++ {
|
||||||
blockRepository.CreateOrUpdateBlock(core.Block{Number: int64(i), Hash: strconv.Itoa(i)})
|
_, err := blockRepository.CreateOrUpdateBlock(core.Block{Number: int64(i), Hash: strconv.Itoa(i)})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRepository.SetBlocksStatus(int64(blockNumberOfChainHead))
|
setErr := blockRepository.SetBlocksStatus(int64(blockNumberOfChainHead))
|
||||||
|
|
||||||
|
Expect(setErr).NotTo(HaveOccurred())
|
||||||
blockOne, err := blockRepository.GetBlock(1)
|
blockOne, err := blockRepository.GetBlock(1)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(blockOne.IsFinal).To(Equal(true))
|
Expect(blockOne.IsFinal).To(Equal(true))
|
||||||
|
@ -42,7 +42,7 @@ func (contractRepository ContractRepository) CreateContract(contract core.Contra
|
|||||||
SET contract_hash = $1, contract_abi = $2
|
SET contract_hash = $1, contract_abi = $2
|
||||||
`, contract.Hash, abiToInsert)
|
`, contract.Hash, abiToInsert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return postgres.ErrDBInsertFailed
|
return postgres.ErrDBInsertFailed(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ func (contractRepository ContractRepository) addTransactions(contract core.Contr
|
|||||||
gasprice,
|
gasprice,
|
||||||
value,
|
value,
|
||||||
input_data
|
input_data
|
||||||
FROM transactions
|
FROM full_sync_transactions
|
||||||
WHERE tx_to = $1
|
WHERE tx_to = $1
|
||||||
ORDER BY block_id DESC`, contract.Hash)
|
ORDER BY block_id DESC`, contract.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -73,24 +73,26 @@ var _ = Describe("Creating contracts", func() {
|
|||||||
blockRepository = repositories.NewBlockRepository(db)
|
blockRepository = repositories.NewBlockRepository(db)
|
||||||
block := core.Block{
|
block := core.Block{
|
||||||
Number: 123,
|
Number: 123,
|
||||||
Transactions: []core.Transaction{
|
Transactions: []core.TransactionModel{
|
||||||
{Hash: "TRANSACTION1", To: "x123", Value: "0"},
|
{Hash: "TRANSACTION1", To: "x123", Value: "0"},
|
||||||
{Hash: "TRANSACTION2", To: "x345", Value: "0"},
|
{Hash: "TRANSACTION2", To: "x345", Value: "0"},
|
||||||
{Hash: "TRANSACTION3", To: "x123", Value: "0"},
|
{Hash: "TRANSACTION3", To: "x123", Value: "0"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
blockRepository.CreateOrUpdateBlock(block)
|
_, insertBlockErr := blockRepository.CreateOrUpdateBlock(block)
|
||||||
|
Expect(insertBlockErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
contractRepository.CreateContract(core.Contract{Hash: "x123"})
|
insertContractErr := contractRepository.CreateContract(core.Contract{Hash: "x123"})
|
||||||
|
Expect(insertContractErr).NotTo(HaveOccurred())
|
||||||
contract, err := contractRepository.GetContract("x123")
|
contract, err := contractRepository.GetContract("x123")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
sort.Slice(contract.Transactions, func(i, j int) bool {
|
sort.Slice(contract.Transactions, func(i, j int) bool {
|
||||||
return contract.Transactions[i].Hash < contract.Transactions[j].Hash
|
return contract.Transactions[i].Hash < contract.Transactions[j].Hash
|
||||||
})
|
})
|
||||||
Expect(contract.Transactions).To(
|
Expect(contract.Transactions).To(
|
||||||
Equal([]core.Transaction{
|
Equal([]core.TransactionModel{
|
||||||
{Hash: "TRANSACTION1", To: "x123", Value: "0"},
|
{Data: []byte{}, Hash: "TRANSACTION1", To: "x123", Value: "0"},
|
||||||
{Hash: "TRANSACTION3", To: "x123", Value: "0"},
|
{Data: []byte{}, Hash: "TRANSACTION3", To: "x123", Value: "0"},
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -49,11 +49,28 @@ func (repository HeaderRepository) CreateOrUpdateHeader(header core.Header) (int
|
|||||||
return 0, ErrValidHeaderExists
|
return 0, ErrValidHeaderExists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repository HeaderRepository) CreateTransactions(headerID int64, transactions []core.TransactionModel) error {
|
||||||
|
for _, transaction := range transactions {
|
||||||
|
_, err := repository.database.Exec(`INSERT INTO public.light_sync_transactions
|
||||||
|
(header_id, hash, gaslimit, gasprice, input_data, nonce, raw, tx_from, tx_index, tx_to, "value")
|
||||||
|
VALUES ($1, $2, $3::NUMERIC, $4::NUMERIC, $5, $6::NUMERIC, $7, $8, $9::NUMERIC, $10, $11::NUMERIC)
|
||||||
|
ON CONFLICT DO NOTHING`, headerID, transaction.Hash, transaction.GasLimit, transaction.GasPrice,
|
||||||
|
transaction.Data, transaction.Nonce, transaction.Raw, transaction.From, transaction.TxIndex, transaction.To,
|
||||||
|
transaction.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (repository HeaderRepository) GetHeader(blockNumber int64) (core.Header, error) {
|
func (repository HeaderRepository) GetHeader(blockNumber int64) (core.Header, error) {
|
||||||
var header core.Header
|
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`,
|
err := repository.database.Get(&header, `SELECT id, block_number, hash, raw, block_timestamp FROM headers WHERE block_number = $1 AND eth_node_fingerprint = $2`,
|
||||||
blockNumber, repository.database.Node.ID)
|
blockNumber, repository.database.Node.ID)
|
||||||
log.Error("GetHeader: error getting headers: ", err)
|
if err != nil {
|
||||||
|
log.Error("GetHeader: error getting headers: ", err)
|
||||||
|
}
|
||||||
return header, err
|
return header, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +180,71 @@ var _ = Describe("Block header repository", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("creating a transaction", func() {
|
||||||
|
var (
|
||||||
|
headerID int64
|
||||||
|
transactions []core.TransactionModel
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
var err error
|
||||||
|
headerID, err = repo.CreateOrUpdateHeader(header)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
fromAddress := common.HexToAddress("0x1234")
|
||||||
|
toAddress := common.HexToAddress("0x5678")
|
||||||
|
txHash := common.HexToHash("0x9876")
|
||||||
|
txHashTwo := common.HexToHash("0x5432")
|
||||||
|
txIndex := big.NewInt(123)
|
||||||
|
transactions = []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",
|
||||||
|
}, {
|
||||||
|
Data: []byte{},
|
||||||
|
From: fromAddress.Hex(),
|
||||||
|
GasLimit: 1,
|
||||||
|
GasPrice: 1,
|
||||||
|
Hash: txHashTwo.Hex(),
|
||||||
|
Nonce: 1,
|
||||||
|
Raw: []byte{},
|
||||||
|
To: toAddress.Hex(),
|
||||||
|
TxIndex: 1,
|
||||||
|
Value: "1",
|
||||||
|
}}
|
||||||
|
|
||||||
|
insertErr := repo.CreateTransactions(headerID, transactions)
|
||||||
|
Expect(insertErr).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("adds transactions", func() {
|
||||||
|
var dbTransactions []core.TransactionModel
|
||||||
|
err = db.Select(&dbTransactions,
|
||||||
|
`SELECT hash, gaslimit, gasprice, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
||||||
|
FROM public.light_sync_transactions WHERE header_id = $1`, headerID)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(dbTransactions).To(ConsistOf(transactions))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("silently ignores duplicate inserts", func() {
|
||||||
|
insertTwoErr := repo.CreateTransactions(headerID, transactions)
|
||||||
|
Expect(insertTwoErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
var dbTransactions []core.TransactionModel
|
||||||
|
err = db.Select(&dbTransactions,
|
||||||
|
`SELECT hash, gaslimit, gasprice, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
||||||
|
FROM public.light_sync_transactions WHERE header_id = $1`, headerID)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(dbTransactions)).To(Equal(2))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Describe("Getting a header", func() {
|
Describe("Getting a header", func() {
|
||||||
It("returns header if it exists", func() {
|
It("returns header if it exists", func() {
|
||||||
_, err = repo.CreateOrUpdateHeader(header)
|
_, err = repo.CreateOrUpdateHeader(header)
|
||||||
|
@ -32,18 +32,18 @@ type LogRepository struct {
|
|||||||
func (logRepository LogRepository) CreateLogs(lgs []core.Log, receiptId int64) error {
|
func (logRepository LogRepository) CreateLogs(lgs []core.Log, receiptId int64) error {
|
||||||
tx, _ := logRepository.DB.Beginx()
|
tx, _ := logRepository.DB.Beginx()
|
||||||
for _, tlog := range lgs {
|
for _, tlog := range lgs {
|
||||||
_, err := tx.Exec(
|
_, insertLogErr := tx.Exec(
|
||||||
`INSERT INTO logs (block_number, address, tx_hash, index, topic0, topic1, topic2, topic3, data, receipt_id)
|
`INSERT INTO logs (block_number, address, tx_hash, index, topic0, topic1, topic2, topic3, data, receipt_id)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
`,
|
`,
|
||||||
tlog.BlockNumber, tlog.Address, tlog.TxHash, tlog.Index, tlog.Topics[0], tlog.Topics[1], tlog.Topics[2], tlog.Topics[3], tlog.Data, receiptId,
|
tlog.BlockNumber, tlog.Address, tlog.TxHash, tlog.Index, tlog.Topics[0], tlog.Topics[1], tlog.Topics[2], tlog.Topics[3], tlog.Data, receiptId,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if insertLogErr != nil {
|
||||||
err = tx.Rollback()
|
rollbackErr := tx.Rollback()
|
||||||
if err != nil {
|
if rollbackErr != nil {
|
||||||
logrus.Error("CreateLogs: could not perform rollback: ", err)
|
logrus.Error("CreateLogs: could not perform rollback: ", rollbackErr)
|
||||||
}
|
}
|
||||||
return postgres.ErrDBInsertFailed
|
return postgres.ErrDBInsertFailed(insertLogErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := tx.Commit()
|
err := tx.Commit()
|
||||||
@ -52,7 +52,7 @@ func (logRepository LogRepository) CreateLogs(lgs []core.Log, receiptId int64) e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error("CreateLogs: could not perform rollback: ", err)
|
logrus.Error("CreateLogs: could not perform rollback: ", err)
|
||||||
}
|
}
|
||||||
return postgres.ErrDBInsertFailed
|
return postgres.ErrDBInsertFailed(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package repositories_test
|
package repositories_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
@ -201,13 +202,9 @@ var _ = Describe("Logs Repository", func() {
|
|||||||
Status: 1,
|
Status: 1,
|
||||||
TxHash: "0x002c4799161d809b23f67884eb6598c9df5894929fe1a9ead97ca175d360f547",
|
TxHash: "0x002c4799161d809b23f67884eb6598c9df5894929fe1a9ead97ca175d360f547",
|
||||||
}
|
}
|
||||||
transaction :=
|
transaction := fakes.GetFakeTransaction(receipt.TxHash, receipt)
|
||||||
core.Transaction{
|
|
||||||
Hash: receipt.TxHash,
|
|
||||||
Receipt: receipt,
|
|
||||||
}
|
|
||||||
|
|
||||||
block := core.Block{Transactions: []core.Transaction{transaction}}
|
block := core.Block{Transactions: []core.TransactionModel{transaction}}
|
||||||
_, err := blockRepository.CreateOrUpdateBlock(block)
|
_, err := blockRepository.CreateOrUpdateBlock(block)
|
||||||
Expect(err).To(Not(HaveOccurred()))
|
Expect(err).To(Not(HaveOccurred()))
|
||||||
retrievedLogs, err := logsRepository.GetLogs("0x99041f808d598b782d5a3e498681c2452a31da08", 4745407)
|
retrievedLogs, err := logsRepository.GetLogs("0x99041f808d598b782d5a3e498681c2452a31da08", 4745407)
|
||||||
|
@ -23,10 +23,11 @@ import (
|
|||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
"github.com/vulcanize/vulcanizedb/test_config"
|
"github.com/vulcanize/vulcanizedb/test_config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Receipts Repository", func() {
|
var _ = Describe("Receipt Repository", func() {
|
||||||
var blockRepository datastore.BlockRepository
|
var blockRepository datastore.BlockRepository
|
||||||
var logRepository datastore.LogRepository
|
var logRepository datastore.LogRepository
|
||||||
var receiptRepository datastore.ReceiptRepository
|
var receiptRepository datastore.ReceiptRepository
|
||||||
@ -117,11 +118,8 @@ var _ = Describe("Receipts Repository", func() {
|
|||||||
TxHash: "0xe340558980f89d5f86045ac11e5cc34e4bcec20f9f1e2a427aa39d87114e8223",
|
TxHash: "0xe340558980f89d5f86045ac11e5cc34e4bcec20f9f1e2a427aa39d87114e8223",
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction := core.Transaction{
|
transaction := fakes.GetFakeTransaction(expected.TxHash, expected)
|
||||||
Hash: expected.TxHash,
|
block := core.Block{Transactions: []core.TransactionModel{transaction}}
|
||||||
Receipt: expected,
|
|
||||||
}
|
|
||||||
block := core.Block{Transactions: []core.Transaction{transaction}}
|
|
||||||
|
|
||||||
_, err := blockRepository.CreateOrUpdateBlock(block)
|
_, err := blockRepository.CreateOrUpdateBlock(block)
|
||||||
|
|
||||||
@ -147,13 +145,10 @@ var _ = Describe("Receipts Repository", func() {
|
|||||||
receipt := core.Receipt{
|
receipt := core.Receipt{
|
||||||
TxHash: "0x002c4799161d809b23f67884eb6598c9df5894929fe1a9ead97ca175d360f547",
|
TxHash: "0x002c4799161d809b23f67884eb6598c9df5894929fe1a9ead97ca175d360f547",
|
||||||
}
|
}
|
||||||
transaction := core.Transaction{
|
transaction := fakes.GetFakeTransaction(receipt.TxHash, receipt)
|
||||||
Hash: receipt.TxHash,
|
|
||||||
Receipt: receipt,
|
|
||||||
}
|
|
||||||
|
|
||||||
block := core.Block{
|
block := core.Block{
|
||||||
Transactions: []core.Transaction{transaction},
|
Transactions: []core.TransactionModel{transaction},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := blockRepository.CreateOrUpdateBlock(block)
|
_, err := blockRepository.CreateOrUpdateBlock(block)
|
@ -17,16 +17,10 @@
|
|||||||
package datastore
|
package datastore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/filters"
|
"github.com/vulcanize/vulcanizedb/pkg/filters"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrBlockDoesNotExist = func(blockNumber int64) error {
|
|
||||||
return fmt.Errorf("Block number %d does not exist", blockNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlockRepository interface {
|
type BlockRepository interface {
|
||||||
CreateOrUpdateBlock(block core.Block) (int64, error)
|
CreateOrUpdateBlock(block core.Block) (int64, error)
|
||||||
GetBlock(blockNumber int64) (core.Block, error)
|
GetBlock(blockNumber int64) (core.Block, error)
|
||||||
@ -34,20 +28,12 @@ type BlockRepository interface {
|
|||||||
SetBlocksStatus(chainHead int64) error
|
SetBlocksStatus(chainHead int64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrContractDoesNotExist = func(contractHash string) error {
|
|
||||||
return fmt.Errorf("Contract %v does not exist", contractHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContractRepository interface {
|
type ContractRepository interface {
|
||||||
CreateContract(contract core.Contract) error
|
CreateContract(contract core.Contract) error
|
||||||
GetContract(contractHash string) (core.Contract, error)
|
GetContract(contractHash string) (core.Contract, error)
|
||||||
ContractExists(contractHash string) (bool, error)
|
ContractExists(contractHash string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrFilterDoesNotExist = func(name string) error {
|
|
||||||
return fmt.Errorf("filter %s does not exist", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
type FilterRepository interface {
|
type FilterRepository interface {
|
||||||
CreateFilter(filter filters.LogFilter) error
|
CreateFilter(filter filters.LogFilter) error
|
||||||
GetFilter(name string) (filters.LogFilter, error)
|
GetFilter(name string) (filters.LogFilter, error)
|
||||||
@ -55,6 +41,7 @@ type FilterRepository interface {
|
|||||||
|
|
||||||
type HeaderRepository interface {
|
type HeaderRepository interface {
|
||||||
CreateOrUpdateHeader(header core.Header) (int64, error)
|
CreateOrUpdateHeader(header core.Header) (int64, error)
|
||||||
|
CreateTransactions(headerID int64, transactions []core.TransactionModel) error
|
||||||
GetHeader(blockNumber int64) (core.Header, error)
|
GetHeader(blockNumber int64) (core.Header, error)
|
||||||
MissingBlockNumbers(startingBlockNumber, endingBlockNumber int64, nodeID string) ([]int64, error)
|
MissingBlockNumbers(startingBlockNumber, endingBlockNumber int64, nodeID string) ([]int64, error)
|
||||||
}
|
}
|
||||||
@ -64,10 +51,6 @@ type LogRepository interface {
|
|||||||
GetLogs(address string, blockNumber int64) ([]core.Log, error)
|
GetLogs(address string, blockNumber int64) ([]core.Log, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrReceiptDoesNotExist = func(txHash string) error {
|
|
||||||
return fmt.Errorf("Receipt for tx: %v does not exist", txHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReceiptRepository interface {
|
type ReceiptRepository interface {
|
||||||
CreateReceiptsAndLogs(blockId int64, receipts []core.Receipt) error
|
CreateReceiptsAndLogs(blockId int64, receipts []core.Receipt) error
|
||||||
CreateReceipt(blockId int64, receipt core.Receipt) (int64, error)
|
CreateReceipt(blockId int64, receipt core.Receipt) (int64, error)
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package fakes
|
package fakes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -28,6 +29,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
FakeAddress = common.HexToAddress("0x1234567890abcdef")
|
||||||
FakeError = errors.New("failed")
|
FakeError = errors.New("failed")
|
||||||
FakeHash = common.BytesToHash([]byte{1, 2, 3, 4, 5})
|
FakeHash = common.BytesToHash([]byte{1, 2, 3, 4, 5})
|
||||||
fakeTimestamp = int64(111111111)
|
fakeTimestamp = int64(111111111)
|
||||||
@ -48,3 +50,42 @@ func GetFakeHeader(blockNumber int64) core.Header {
|
|||||||
Timestamp: strconv.FormatInt(fakeTimestamp, 10),
|
Timestamp: strconv.FormatInt(fakeTimestamp, 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fakeTransaction types.Transaction
|
||||||
|
var rawTransaction bytes.Buffer
|
||||||
|
var _ = fakeTransaction.EncodeRLP(&rawTransaction)
|
||||||
|
var FakeTransaction = core.TransactionModel{
|
||||||
|
Data: []byte{},
|
||||||
|
From: "",
|
||||||
|
GasLimit: 0,
|
||||||
|
GasPrice: 0,
|
||||||
|
Hash: "",
|
||||||
|
Nonce: 0,
|
||||||
|
Raw: rawTransaction.Bytes(),
|
||||||
|
Receipt: core.Receipt{},
|
||||||
|
To: "",
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFakeTransaction(hash string, receipt core.Receipt) core.TransactionModel {
|
||||||
|
gethTransaction := types.Transaction{}
|
||||||
|
var raw bytes.Buffer
|
||||||
|
err := gethTransaction.EncodeRLP(&raw)
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to marshal transaction creating test fake")
|
||||||
|
}
|
||||||
|
return core.TransactionModel{
|
||||||
|
Data: []byte{},
|
||||||
|
From: "",
|
||||||
|
GasLimit: 0,
|
||||||
|
GasPrice: 0,
|
||||||
|
Hash: hash,
|
||||||
|
Nonce: 0,
|
||||||
|
Raw: raw.Bytes(),
|
||||||
|
Receipt: receipt,
|
||||||
|
To: "",
|
||||||
|
TxIndex: 0,
|
||||||
|
Value: "0",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package fakes
|
package fakes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
@ -35,11 +36,15 @@ type MockBlockChain struct {
|
|||||||
fetchContractDataPassedResult interface{}
|
fetchContractDataPassedResult interface{}
|
||||||
fetchContractDataPassedBlockNumber int64
|
fetchContractDataPassedBlockNumber int64
|
||||||
getBlockByNumberErr error
|
getBlockByNumberErr error
|
||||||
|
GetTransactionsCalled bool
|
||||||
|
GetTransactionsError error
|
||||||
|
GetTransactionsPassedHashes []common.Hash
|
||||||
logQuery ethereum.FilterQuery
|
logQuery ethereum.FilterQuery
|
||||||
logQueryErr error
|
logQueryErr error
|
||||||
logQueryReturnLogs []types.Log
|
logQueryReturnLogs []types.Log
|
||||||
lastBlock *big.Int
|
lastBlock *big.Int
|
||||||
node core.Node
|
node core.Node
|
||||||
|
Transactions []core.TransactionModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockBlockChain() *MockBlockChain {
|
func NewMockBlockChain() *MockBlockChain {
|
||||||
@ -104,6 +109,12 @@ func (chain *MockBlockChain) GetLogs(contract core.Contract, startingBlockNumber
|
|||||||
return []core.Log{}, nil
|
return []core.Log{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (chain *MockBlockChain) GetTransactions(transactionHashes []common.Hash) ([]core.TransactionModel, error) {
|
||||||
|
chain.GetTransactionsCalled = true
|
||||||
|
chain.GetTransactionsPassedHashes = transactionHashes
|
||||||
|
return chain.Transactions, chain.GetTransactionsError
|
||||||
|
}
|
||||||
|
|
||||||
func (chain *MockBlockChain) CallContract(contractHash string, input []byte, blockNumber *big.Int) ([]byte, error) {
|
func (chain *MockBlockChain) CallContract(contractHash string, input []byte, blockNumber *big.Int) ([]byte, error) {
|
||||||
return []byte{}, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ type MockHeaderRepository struct {
|
|||||||
createOrUpdateHeaderErr error
|
createOrUpdateHeaderErr error
|
||||||
createOrUpdateHeaderPassedBlockNumbers []int64
|
createOrUpdateHeaderPassedBlockNumbers []int64
|
||||||
createOrUpdateHeaderReturnID int64
|
createOrUpdateHeaderReturnID int64
|
||||||
|
CreateTransactionsCalled bool
|
||||||
|
CreateTransactionsError error
|
||||||
getHeaderError error
|
getHeaderError error
|
||||||
getHeaderReturnBlockHash string
|
getHeaderReturnBlockHash string
|
||||||
missingBlockNumbers []int64
|
missingBlockNumbers []int64
|
||||||
@ -56,6 +58,11 @@ func (repository *MockHeaderRepository) CreateOrUpdateHeader(header core.Header)
|
|||||||
return repository.createOrUpdateHeaderReturnID, repository.createOrUpdateHeaderErr
|
return repository.createOrUpdateHeaderReturnID, repository.createOrUpdateHeaderErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repository *MockHeaderRepository) CreateTransactions(headerID int64, transactions []core.TransactionModel) error {
|
||||||
|
repository.CreateTransactionsCalled = true
|
||||||
|
return repository.CreateTransactionsError
|
||||||
|
}
|
||||||
|
|
||||||
func (repository *MockHeaderRepository) GetHeader(blockNumber int64) (core.Header, error) {
|
func (repository *MockHeaderRepository) GetHeader(blockNumber int64) (core.Header, error) {
|
||||||
repository.GetHeaderPassedBlockNumber = blockNumber
|
repository.GetHeaderPassedBlockNumber = blockNumber
|
||||||
return core.Header{BlockNumber: blockNumber, Hash: repository.getHeaderReturnBlockHash}, repository.getHeaderError
|
return core.Header{BlockNumber: blockNumber, Hash: repository.getHeaderReturnBlockHash}, repository.getHeaderError
|
||||||
|
@ -18,39 +18,30 @@ package fakes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockTransactionConverter struct {
|
type MockTransactionConverter struct {
|
||||||
convertTransactionsToCoreCalled bool
|
ConvertHeaderTransactionIndexToIntCalled bool
|
||||||
convertTransactionsToCorePassedBlock *types.Block
|
ConvertBlockTransactionsToCoreCalled bool
|
||||||
convertTransactionsToCoreReturnTransactions []core.Transaction
|
ConvertBlockTransactionsToCorePassedBlock *types.Block
|
||||||
convertTransactionsToCoreReturnError error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockTransactionConverter() *MockTransactionConverter {
|
func NewMockTransactionConverter() *MockTransactionConverter {
|
||||||
return &MockTransactionConverter{
|
return &MockTransactionConverter{
|
||||||
convertTransactionsToCoreCalled: false,
|
ConvertHeaderTransactionIndexToIntCalled: false,
|
||||||
convertTransactionsToCorePassedBlock: nil,
|
ConvertBlockTransactionsToCoreCalled: false,
|
||||||
convertTransactionsToCoreReturnTransactions: nil,
|
ConvertBlockTransactionsToCorePassedBlock: nil,
|
||||||
convertTransactionsToCoreReturnError: nil,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mtc *MockTransactionConverter) SetConvertTransactionsToCoreReturnVals(transactions []core.Transaction, err error) {
|
func (converter *MockTransactionConverter) ConvertBlockTransactionsToCore(gethBlock *types.Block) ([]core.TransactionModel, error) {
|
||||||
mtc.convertTransactionsToCoreReturnTransactions = transactions
|
converter.ConvertBlockTransactionsToCoreCalled = true
|
||||||
mtc.convertTransactionsToCoreReturnError = err
|
converter.ConvertBlockTransactionsToCorePassedBlock = gethBlock
|
||||||
|
return []core.TransactionModel{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mtc *MockTransactionConverter) ConvertTransactionsToCore(gethBlock *types.Block) ([]core.Transaction, error) {
|
func (converter *MockTransactionConverter) ConvertRpcTransactionsToModels(transactions []core.RpcTransaction) ([]core.TransactionModel, error) {
|
||||||
mtc.convertTransactionsToCoreCalled = true
|
converter.ConvertHeaderTransactionIndexToIntCalled = true
|
||||||
mtc.convertTransactionsToCorePassedBlock = gethBlock
|
return nil, nil
|
||||||
return mtc.convertTransactionsToCoreReturnTransactions, mtc.convertTransactionsToCoreReturnError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mtc *MockTransactionConverter) AssertConvertTransactionsToCoreCalledWith(gethBlock *types.Block) {
|
|
||||||
Expect(mtc.convertTransactionsToCoreCalled).To(BeTrue())
|
|
||||||
Expect(mtc.convertTransactionsToCorePassedBlock).To(Equal(gethBlock))
|
|
||||||
}
|
}
|
||||||
|
13
pkg/fakes/mock_transaction_syncer.go
Normal file
13
pkg/fakes/mock_transaction_syncer.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package fakes
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
||||||
|
type MockTransactionSyncer struct {
|
||||||
|
SyncTransactionsCalled bool
|
||||||
|
SyncTransactionsError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *MockTransactionSyncer) SyncTransactions(headerID int64, logs []types.Log) error {
|
||||||
|
syncer.SyncTransactionsCalled = true
|
||||||
|
return syncer.SyncTransactionsError
|
||||||
|
}
|
@ -18,10 +18,11 @@ package geth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -37,20 +38,22 @@ var ErrEmptyHeader = errors.New("empty header returned over RPC")
|
|||||||
const MAX_BATCH_SIZE = 100
|
const MAX_BATCH_SIZE = 100
|
||||||
|
|
||||||
type BlockChain struct {
|
type BlockChain struct {
|
||||||
blockConverter vulcCommon.BlockConverter
|
blockConverter vulcCommon.BlockConverter
|
||||||
ethClient core.EthClient
|
ethClient core.EthClient
|
||||||
headerConverter vulcCommon.HeaderConverter
|
headerConverter vulcCommon.HeaderConverter
|
||||||
node core.Node
|
node core.Node
|
||||||
rpcClient core.RpcClient
|
rpcClient core.RpcClient
|
||||||
|
transactionConverter vulcCommon.TransactionConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockChain(ethClient core.EthClient, rpcClient core.RpcClient, node core.Node, converter vulcCommon.TransactionConverter) *BlockChain {
|
func NewBlockChain(ethClient core.EthClient, rpcClient core.RpcClient, node core.Node, converter vulcCommon.TransactionConverter) *BlockChain {
|
||||||
return &BlockChain{
|
return &BlockChain{
|
||||||
blockConverter: vulcCommon.NewBlockConverter(converter),
|
blockConverter: vulcCommon.NewBlockConverter(converter),
|
||||||
ethClient: ethClient,
|
ethClient: ethClient,
|
||||||
headerConverter: vulcCommon.HeaderConverter{},
|
headerConverter: vulcCommon.HeaderConverter{},
|
||||||
node: node,
|
node: node,
|
||||||
rpcClient: rpcClient,
|
rpcClient: rpcClient,
|
||||||
|
transactionConverter: converter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +65,14 @@ func (blockChain *BlockChain) GetBlockByNumber(blockNumber int64) (block core.Bl
|
|||||||
return blockChain.blockConverter.ToCoreBlock(gethBlock)
|
return blockChain.blockConverter.ToCoreBlock(gethBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (blockChain *BlockChain) GetEthLogsWithCustomQuery(query ethereum.FilterQuery) ([]types.Log, error) {
|
||||||
|
gethLogs, err := blockChain.ethClient.FilterLogs(context.Background(), query)
|
||||||
|
if err != nil {
|
||||||
|
return []types.Log{}, err
|
||||||
|
}
|
||||||
|
return gethLogs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) GetHeaderByNumber(blockNumber int64) (header core.Header, err error) {
|
func (blockChain *BlockChain) GetHeaderByNumber(blockNumber int64) (header core.Header, err error) {
|
||||||
if blockChain.node.NetworkID == core.KOVAN_NETWORK_ID {
|
if blockChain.node.NetworkID == core.KOVAN_NETWORK_ID {
|
||||||
return blockChain.getPOAHeader(blockNumber)
|
return blockChain.getPOAHeader(blockNumber)
|
||||||
@ -76,49 +87,55 @@ func (blockChain *BlockChain) GetHeaderByNumbers(blockNumbers []int64) (header [
|
|||||||
return blockChain.getPOWHeaders(blockNumbers)
|
return blockChain.getPOWHeaders(blockNumbers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) getPOWHeader(blockNumber int64) (header core.Header, err error) {
|
func (blockChain *BlockChain) GetLogs(contract core.Contract, startingBlockNumber, endingBlockNumber *big.Int) ([]core.Log, error) {
|
||||||
gethHeader, err := blockChain.ethClient.HeaderByNumber(context.Background(), big.NewInt(blockNumber))
|
if endingBlockNumber == nil {
|
||||||
if err != nil {
|
endingBlockNumber = startingBlockNumber
|
||||||
return header, err
|
|
||||||
}
|
}
|
||||||
return blockChain.headerConverter.Convert(gethHeader, gethHeader.Hash().String()), nil
|
contractAddress := common.HexToAddress(contract.Hash)
|
||||||
|
fc := ethereum.FilterQuery{
|
||||||
|
FromBlock: startingBlockNumber,
|
||||||
|
ToBlock: endingBlockNumber,
|
||||||
|
Addresses: []common.Address{contractAddress},
|
||||||
|
Topics: nil,
|
||||||
|
}
|
||||||
|
gethLogs, err := blockChain.GetEthLogsWithCustomQuery(fc)
|
||||||
|
if err != nil {
|
||||||
|
return []core.Log{}, err
|
||||||
|
}
|
||||||
|
logs := vulcCommon.ToCoreLogs(gethLogs)
|
||||||
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) getPOWHeaders(blockNumbers []int64) (headers []core.Header, err error) {
|
func (blockChain *BlockChain) GetTransactions(transactionHashes []common.Hash) ([]core.TransactionModel, error) {
|
||||||
|
numTransactions := len(transactionHashes)
|
||||||
var batch []client.BatchElem
|
var batch []client.BatchElem
|
||||||
var POWHeaders [MAX_BATCH_SIZE]types.Header
|
transactions := make([]core.RpcTransaction, numTransactions)
|
||||||
includeTransactions := false
|
|
||||||
|
|
||||||
for index, blockNumber := range blockNumbers {
|
|
||||||
|
|
||||||
if index >= MAX_BATCH_SIZE {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
blockNumberArg := hexutil.EncodeBig(big.NewInt(blockNumber))
|
|
||||||
|
|
||||||
|
for index, transactionHash := range transactionHashes {
|
||||||
batchElem := client.BatchElem{
|
batchElem := client.BatchElem{
|
||||||
Method: "eth_getBlockByNumber",
|
Method: "eth_getTransactionByHash",
|
||||||
Result: &POWHeaders[index],
|
Result: &transactions[index],
|
||||||
Args: []interface{}{blockNumberArg, includeTransactions},
|
Args: []interface{}{transactionHash},
|
||||||
}
|
}
|
||||||
|
|
||||||
batch = append(batch, batchElem)
|
batch = append(batch, batchElem)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = blockChain.rpcClient.BatchCall(batch)
|
rpcErr := blockChain.rpcClient.BatchCall(batch)
|
||||||
if err != nil {
|
if rpcErr != nil {
|
||||||
return headers, err
|
fmt.Println("rpc err")
|
||||||
|
return []core.TransactionModel{}, rpcErr
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, POWHeader := range POWHeaders {
|
return blockChain.transactionConverter.ConvertRpcTransactionsToModels(transactions)
|
||||||
if POWHeader.Number != nil {
|
}
|
||||||
header := blockChain.headerConverter.Convert(&POWHeader, POWHeader.Hash().String())
|
|
||||||
headers = append(headers, header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return headers, err
|
func (blockChain *BlockChain) LastBlock() (*big.Int, error) {
|
||||||
|
block, err := blockChain.ethClient.HeaderByNumber(context.Background(), nil)
|
||||||
|
return block.Number, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (blockChain *BlockChain) Node() core.Node {
|
||||||
|
return blockChain.node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) getPOAHeader(blockNumber int64) (header core.Header, err error) {
|
func (blockChain *BlockChain) getPOAHeader(blockNumber int64) (header core.Header, err error) {
|
||||||
@ -204,38 +221,47 @@ func (blockChain *BlockChain) getPOAHeaders(blockNumbers []int64) (headers []cor
|
|||||||
return headers, err
|
return headers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) GetLogs(contract core.Contract, startingBlockNumber, endingBlockNumber *big.Int) ([]core.Log, error) {
|
func (blockChain *BlockChain) getPOWHeader(blockNumber int64) (header core.Header, err error) {
|
||||||
if endingBlockNumber == nil {
|
gethHeader, err := blockChain.ethClient.HeaderByNumber(context.Background(), big.NewInt(blockNumber))
|
||||||
endingBlockNumber = startingBlockNumber
|
|
||||||
}
|
|
||||||
contractAddress := common.HexToAddress(contract.Hash)
|
|
||||||
fc := ethereum.FilterQuery{
|
|
||||||
FromBlock: startingBlockNumber,
|
|
||||||
ToBlock: endingBlockNumber,
|
|
||||||
Addresses: []common.Address{contractAddress},
|
|
||||||
Topics: nil,
|
|
||||||
}
|
|
||||||
gethLogs, err := blockChain.GetEthLogsWithCustomQuery(fc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []core.Log{}, err
|
return header, err
|
||||||
}
|
}
|
||||||
logs := vulcCommon.ToCoreLogs(gethLogs)
|
return blockChain.headerConverter.Convert(gethHeader, gethHeader.Hash().String()), nil
|
||||||
return logs, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) GetEthLogsWithCustomQuery(query ethereum.FilterQuery) ([]types.Log, error) {
|
func (blockChain *BlockChain) getPOWHeaders(blockNumbers []int64) (headers []core.Header, err error) {
|
||||||
gethLogs, err := blockChain.ethClient.FilterLogs(context.Background(), query)
|
var batch []client.BatchElem
|
||||||
|
var POWHeaders [MAX_BATCH_SIZE]types.Header
|
||||||
|
includeTransactions := false
|
||||||
|
|
||||||
|
for index, blockNumber := range blockNumbers {
|
||||||
|
|
||||||
|
if index >= MAX_BATCH_SIZE {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
blockNumberArg := hexutil.EncodeBig(big.NewInt(blockNumber))
|
||||||
|
|
||||||
|
batchElem := client.BatchElem{
|
||||||
|
Method: "eth_getBlockByNumber",
|
||||||
|
Result: &POWHeaders[index],
|
||||||
|
Args: []interface{}{blockNumberArg, includeTransactions},
|
||||||
|
}
|
||||||
|
|
||||||
|
batch = append(batch, batchElem)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = blockChain.rpcClient.BatchCall(batch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []types.Log{}, err
|
return headers, err
|
||||||
}
|
}
|
||||||
return gethLogs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (blockChain *BlockChain) LastBlock() (*big.Int, error) {
|
for _, POWHeader := range POWHeaders {
|
||||||
block, err := blockChain.ethClient.HeaderByNumber(context.Background(), nil)
|
if POWHeader.Number != nil {
|
||||||
return block.Number, err
|
header := blockChain.headerConverter.Convert(&POWHeader, POWHeader.Hash().String())
|
||||||
}
|
headers = append(headers, header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) Node() core.Node {
|
return headers, err
|
||||||
return blockChain.node
|
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,23 @@ import (
|
|||||||
vulcCore "github.com/vulcanize/vulcanizedb/pkg/core"
|
vulcCore "github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/geth/converters/cold_db"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Geth blockchain", func() {
|
var _ = Describe("Geth blockchain", func() {
|
||||||
var mockClient *fakes.MockEthClient
|
var (
|
||||||
var mockRpcClient *fakes.MockRpcClient
|
mockClient *fakes.MockEthClient
|
||||||
var node vulcCore.Node
|
blockChain *geth.BlockChain
|
||||||
var blockChain *geth.BlockChain
|
mockRpcClient *fakes.MockRpcClient
|
||||||
|
mockTransactionConverter *fakes.MockTransactionConverter
|
||||||
|
node vulcCore.Node
|
||||||
|
)
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
mockClient = fakes.NewMockEthClient()
|
mockClient = fakes.NewMockEthClient()
|
||||||
mockRpcClient = fakes.NewMockRpcClient()
|
mockRpcClient = fakes.NewMockRpcClient()
|
||||||
|
mockTransactionConverter = fakes.NewMockTransactionConverter()
|
||||||
node = vulcCore.Node{}
|
node = vulcCore.Node{}
|
||||||
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, cold_db.NewColdDbTransactionConverter())
|
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, mockTransactionConverter)
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("getting a block", func() {
|
Describe("getting a block", func() {
|
||||||
@ -89,8 +92,6 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("fetches headers with multiple blocks", func() {
|
It("fetches headers with multiple blocks", func() {
|
||||||
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, cold_db.NewColdDbTransactionConverter())
|
|
||||||
|
|
||||||
_, err := blockChain.GetHeaderByNumbers([]int64{100, 99})
|
_, err := blockChain.GetHeaderByNumbers([]int64{100, 99})
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -103,7 +104,7 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
||||||
blockNumber := hexutil.Big(*big.NewInt(100))
|
blockNumber := hexutil.Big(*big.NewInt(100))
|
||||||
mockRpcClient.SetReturnPOAHeader(vulcCore.POAHeader{Number: &blockNumber})
|
mockRpcClient.SetReturnPOAHeader(vulcCore.POAHeader{Number: &blockNumber})
|
||||||
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, cold_db.NewColdDbTransactionConverter())
|
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, fakes.NewMockTransactionConverter())
|
||||||
|
|
||||||
_, err := blockChain.GetHeaderByNumber(100)
|
_, err := blockChain.GetHeaderByNumber(100)
|
||||||
|
|
||||||
@ -114,7 +115,7 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
It("returns err if rpcClient returns err", func() {
|
It("returns err if rpcClient returns err", func() {
|
||||||
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
||||||
mockRpcClient.SetCallContextErr(fakes.FakeError)
|
mockRpcClient.SetCallContextErr(fakes.FakeError)
|
||||||
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, cold_db.NewColdDbTransactionConverter())
|
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, fakes.NewMockTransactionConverter())
|
||||||
|
|
||||||
_, err := blockChain.GetHeaderByNumber(100)
|
_, err := blockChain.GetHeaderByNumber(100)
|
||||||
|
|
||||||
@ -124,7 +125,7 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
|
|
||||||
It("returns error if returned header is empty", func() {
|
It("returns error if returned header is empty", func() {
|
||||||
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
||||||
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, cold_db.NewColdDbTransactionConverter())
|
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, fakes.NewMockTransactionConverter())
|
||||||
|
|
||||||
_, err := blockChain.GetHeaderByNumber(100)
|
_, err := blockChain.GetHeaderByNumber(100)
|
||||||
|
|
||||||
@ -136,7 +137,6 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
node.NetworkID = vulcCore.KOVAN_NETWORK_ID
|
||||||
blockNumber := hexutil.Big(*big.NewInt(100))
|
blockNumber := hexutil.Big(*big.NewInt(100))
|
||||||
mockRpcClient.SetReturnPOAHeaders([]vulcCore.POAHeader{{Number: &blockNumber}})
|
mockRpcClient.SetReturnPOAHeaders([]vulcCore.POAHeader{{Number: &blockNumber}})
|
||||||
blockChain = geth.NewBlockChain(mockClient, mockRpcClient, node, cold_db.NewColdDbTransactionConverter())
|
|
||||||
|
|
||||||
_, err := blockChain.GetHeaderByNumbers([]int64{100, 99})
|
_, err := blockChain.GetHeaderByNumbers([]int64{100, 99})
|
||||||
|
|
||||||
@ -216,6 +216,22 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("getting transactions", func() {
|
||||||
|
It("fetches transaction for each hash", func() {
|
||||||
|
_, err := blockChain.GetTransactions([]common.Hash{{}, {}})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
mockRpcClient.AssertBatchCalledWith("eth_getTransactionByHash", 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("converts transaction indexes from hex to int", func() {
|
||||||
|
_, err := blockChain.GetTransactions([]common.Hash{{}, {}})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(mockTransactionConverter.ConvertHeaderTransactionIndexToIntCalled).To(BeTrue())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Describe("getting the most recent block number", func() {
|
Describe("getting the most recent block number", func() {
|
||||||
It("fetches latest header from ethClient", func() {
|
It("fetches latest header from ethClient", func() {
|
||||||
blockNumber := int64(100)
|
blockNumber := int64(100)
|
||||||
|
@ -81,7 +81,8 @@ var _ = Describe("Geth cold importer", func() {
|
|||||||
|
|
||||||
mockEthereumDatabase.AssertGetBlockHashCalledWith(blockNumber)
|
mockEthereumDatabase.AssertGetBlockHashCalledWith(blockNumber)
|
||||||
mockEthereumDatabase.AssertGetBlockCalledWith(fakeHash, blockNumber)
|
mockEthereumDatabase.AssertGetBlockCalledWith(fakeHash, blockNumber)
|
||||||
mockTransactionConverter.AssertConvertTransactionsToCoreCalledWith(fakeGethBlock)
|
Expect(mockTransactionConverter.ConvertBlockTransactionsToCoreCalled).To(BeTrue())
|
||||||
|
Expect(mockTransactionConverter.ConvertBlockTransactionsToCorePassedBlock).To(Equal(fakeGethBlock))
|
||||||
convertedBlock, err := blockConverter.ToCoreBlock(fakeGethBlock)
|
convertedBlock, err := blockConverter.ToCoreBlock(fakeGethBlock)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
mockBlockRepository.AssertCreateOrUpdateBlockCalledWith(convertedBlock)
|
mockBlockRepository.AssertCreateOrUpdateBlockCalledWith(convertedBlock)
|
||||||
|
@ -18,7 +18,6 @@ package cold_db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@ -31,9 +30,9 @@ func NewColdDbTransactionConverter() *ColdDbTransactionConverter {
|
|||||||
return &ColdDbTransactionConverter{}
|
return &ColdDbTransactionConverter{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cdtc *ColdDbTransactionConverter) ConvertTransactionsToCore(gethBlock *types.Block) ([]core.Transaction, error) {
|
func (cdtc *ColdDbTransactionConverter) ConvertBlockTransactionsToCore(gethBlock *types.Block) ([]core.TransactionModel, error) {
|
||||||
var g errgroup.Group
|
var g errgroup.Group
|
||||||
coreTransactions := make([]core.Transaction, len(gethBlock.Transactions()))
|
coreTransactions := make([]core.TransactionModel, len(gethBlock.Transactions()))
|
||||||
|
|
||||||
for gethTransactionIndex, gethTransaction := range gethBlock.Transactions() {
|
for gethTransactionIndex, gethTransaction := range gethBlock.Transactions() {
|
||||||
transaction := gethTransaction
|
transaction := gethTransaction
|
||||||
@ -55,6 +54,10 @@ func (cdtc *ColdDbTransactionConverter) ConvertTransactionsToCore(gethBlock *typ
|
|||||||
return coreTransactions, nil
|
return coreTransactions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cdtc *ColdDbTransactionConverter) ConvertRpcTransactionsToModels(transactions []core.RpcTransaction) ([]core.TransactionModel, error) {
|
||||||
|
panic("converting transaction indexes to integer not supported for cold import")
|
||||||
|
}
|
||||||
|
|
||||||
func getSigner(tx *types.Transaction) types.Signer {
|
func getSigner(tx *types.Transaction) types.Signer {
|
||||||
v, _, _ := tx.RawSignatureValues()
|
v, _, _ := tx.RawSignatureValues()
|
||||||
if v.Sign() != 0 && tx.Protected() {
|
if v.Sign() != 0 && tx.Protected() {
|
||||||
@ -63,9 +66,8 @@ func getSigner(tx *types.Transaction) types.Signer {
|
|||||||
return types.HomesteadSigner{}
|
return types.HomesteadSigner{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transToCoreTrans(transaction *types.Transaction, from *common.Address) core.Transaction {
|
func transToCoreTrans(transaction *types.Transaction, from *common.Address) core.TransactionModel {
|
||||||
data := hexutil.Encode(transaction.Data())
|
return core.TransactionModel{
|
||||||
return core.Transaction{
|
|
||||||
Hash: transaction.Hash().Hex(),
|
Hash: transaction.Hash().Hex(),
|
||||||
Nonce: transaction.Nonce(),
|
Nonce: transaction.Nonce(),
|
||||||
To: strings.ToLower(addressToHex(transaction.To())),
|
To: strings.ToLower(addressToHex(transaction.To())),
|
||||||
@ -73,7 +75,7 @@ func transToCoreTrans(transaction *types.Transaction, from *common.Address) core
|
|||||||
GasLimit: transaction.Gas(),
|
GasLimit: transaction.Gas(),
|
||||||
GasPrice: transaction.GasPrice().Int64(),
|
GasPrice: transaction.GasPrice().Int64(),
|
||||||
Value: transaction.Value().String(),
|
Value: transaction.Value().String(),
|
||||||
Data: data,
|
Data: transaction.Data(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ func NewBlockConverter(transactionConverter TransactionConverter) BlockConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bc BlockConverter) ToCoreBlock(gethBlock *types.Block) (core.Block, error) {
|
func (bc BlockConverter) ToCoreBlock(gethBlock *types.Block) (core.Block, error) {
|
||||||
transactions, err := bc.transactionConverter.ConvertTransactionsToCore(gethBlock)
|
transactions, err := bc.transactionConverter.ConvertBlockTransactionsToCore(gethBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.Block{}, err
|
return core.Block{}, err
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package common_test
|
package common_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -227,6 +228,9 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
|
|||||||
big.NewInt(3),
|
big.NewInt(3),
|
||||||
hexutil.MustDecode("0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"),
|
hexutil.MustDecode("0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"),
|
||||||
)
|
)
|
||||||
|
var rawTransaction bytes.Buffer
|
||||||
|
encodeErr := gethTransaction.EncodeRLP(&rawTransaction)
|
||||||
|
Expect(encodeErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
gethReceipt := &types.Receipt{
|
gethReceipt := &types.Receipt{
|
||||||
Bloom: types.BytesToBloom(hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
|
Bloom: types.BytesToBloom(hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
|
||||||
@ -256,11 +260,14 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(len(coreBlock.Transactions)).To(Equal(1))
|
Expect(len(coreBlock.Transactions)).To(Equal(1))
|
||||||
coreTransaction := coreBlock.Transactions[0]
|
coreTransaction := coreBlock.Transactions[0]
|
||||||
Expect(coreTransaction.Data).To(Equal("0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"))
|
expectedData := common.FromHex("0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14")
|
||||||
|
Expect(coreTransaction.Data).To(Equal(expectedData))
|
||||||
Expect(coreTransaction.To).To(Equal(gethTransaction.To().Hex()))
|
Expect(coreTransaction.To).To(Equal(gethTransaction.To().Hex()))
|
||||||
Expect(coreTransaction.From).To(Equal("0x0000000000000000000000000000000000000123"))
|
Expect(coreTransaction.From).To(Equal("0x0000000000000000000000000000000000000123"))
|
||||||
Expect(coreTransaction.GasLimit).To(Equal(gethTransaction.Gas()))
|
Expect(coreTransaction.GasLimit).To(Equal(gethTransaction.Gas()))
|
||||||
Expect(coreTransaction.GasPrice).To(Equal(gethTransaction.GasPrice().Int64()))
|
Expect(coreTransaction.GasPrice).To(Equal(gethTransaction.GasPrice().Int64()))
|
||||||
|
Expect(coreTransaction.Raw).To(Equal(rawTransaction.Bytes()))
|
||||||
|
Expect(coreTransaction.TxIndex).To(Equal(int64(0)))
|
||||||
Expect(coreTransaction.Value).To(Equal(gethTransaction.Value().String()))
|
Expect(coreTransaction.Value).To(Equal(gethTransaction.Value().String()))
|
||||||
Expect(coreTransaction.Nonce).To(Equal(gethTransaction.Nonce()))
|
Expect(coreTransaction.Nonce).To(Equal(gethTransaction.Nonce()))
|
||||||
|
|
||||||
|
@ -22,5 +22,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TransactionConverter interface {
|
type TransactionConverter interface {
|
||||||
ConvertTransactionsToCore(gethBlock *types.Block) ([]core.Transaction, error)
|
ConvertBlockTransactionsToCore(gethBlock *types.Block) ([]core.TransactionModel, error)
|
||||||
|
ConvertRpcTransactionsToModels(transactions []core.RpcTransaction) ([]core.TransactionModel, error)
|
||||||
}
|
}
|
||||||
|
13
pkg/geth/converters/rpc/rpc_suite_test.go
Normal file
13
pkg/geth/converters/rpc/rpc_suite_test.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package rpc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRpc(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Rpc Suite")
|
||||||
|
}
|
@ -17,13 +17,17 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"log"
|
"log"
|
||||||
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
@ -34,26 +38,75 @@ type RpcTransactionConverter struct {
|
|||||||
client core.EthClient
|
client core.EthClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// raw transaction data, required for generating RLP
|
||||||
|
type transactionData struct {
|
||||||
|
AccountNonce uint64
|
||||||
|
Price *big.Int
|
||||||
|
GasLimit uint64
|
||||||
|
Recipient *common.Address `rlp:"nil"` // nil means contract creation
|
||||||
|
Amount *big.Int
|
||||||
|
Payload []byte
|
||||||
|
V *big.Int
|
||||||
|
R *big.Int
|
||||||
|
S *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
func NewRpcTransactionConverter(client core.EthClient) *RpcTransactionConverter {
|
func NewRpcTransactionConverter(client core.EthClient) *RpcTransactionConverter {
|
||||||
return &RpcTransactionConverter{client: client}
|
return &RpcTransactionConverter{client: client}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rtc *RpcTransactionConverter) ConvertTransactionsToCore(gethBlock *types.Block) ([]core.Transaction, error) {
|
func (converter *RpcTransactionConverter) ConvertRpcTransactionsToModels(transactions []core.RpcTransaction) ([]core.TransactionModel, error) {
|
||||||
|
var results []core.TransactionModel
|
||||||
|
for _, transaction := range transactions {
|
||||||
|
txData, convertErr := getTransactionData(transaction)
|
||||||
|
if convertErr != nil {
|
||||||
|
return nil, convertErr
|
||||||
|
}
|
||||||
|
txRLP, rlpErr := getTransactionRLP(txData)
|
||||||
|
if rlpErr != nil {
|
||||||
|
return nil, rlpErr
|
||||||
|
}
|
||||||
|
txIndex, txIndexErr := hexToBigInt(transaction.TransactionIndex)
|
||||||
|
if txIndexErr != nil {
|
||||||
|
return nil, txIndexErr
|
||||||
|
}
|
||||||
|
transactionModel := core.TransactionModel{
|
||||||
|
Data: txData.Payload,
|
||||||
|
From: transaction.From,
|
||||||
|
GasLimit: txData.GasLimit,
|
||||||
|
GasPrice: txData.Price.Int64(),
|
||||||
|
Hash: transaction.Hash,
|
||||||
|
Nonce: txData.AccountNonce,
|
||||||
|
Raw: txRLP,
|
||||||
|
// NOTE: Light Sync transactions don't include receipt; would require separate RPC call
|
||||||
|
To: transaction.Recipient,
|
||||||
|
TxIndex: txIndex.Int64(),
|
||||||
|
Value: txData.Amount.String(),
|
||||||
|
}
|
||||||
|
results = append(results, transactionModel)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (converter *RpcTransactionConverter) ConvertBlockTransactionsToCore(gethBlock *types.Block) ([]core.TransactionModel, error) {
|
||||||
var g errgroup.Group
|
var g errgroup.Group
|
||||||
coreTransactions := make([]core.Transaction, len(gethBlock.Transactions()))
|
coreTransactions := make([]core.TransactionModel, len(gethBlock.Transactions()))
|
||||||
|
|
||||||
for gethTransactionIndex, gethTransaction := range gethBlock.Transactions() {
|
for gethTransactionIndex, gethTransaction := range gethBlock.Transactions() {
|
||||||
//https://golang.org/doc/faq#closures_and_goroutines
|
//https://golang.org/doc/faq#closures_and_goroutines
|
||||||
transaction := gethTransaction
|
transaction := gethTransaction
|
||||||
transactionIndex := uint(gethTransactionIndex)
|
transactionIndex := uint(gethTransactionIndex)
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
from, err := rtc.client.TransactionSender(context.Background(), transaction, gethBlock.Hash(), transactionIndex)
|
from, err := converter.client.TransactionSender(context.Background(), transaction, gethBlock.Hash(), transactionIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("transaction sender: ", err)
|
log.Println("transaction sender: ", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
coreTransaction := transToCoreTrans(transaction, &from)
|
coreTransaction, convertErr := convertGethTransactionToModel(transaction, &from, int64(gethTransactionIndex))
|
||||||
coreTransaction, err = rtc.appendReceiptToTransaction(coreTransaction)
|
if convertErr != nil {
|
||||||
|
return convertErr
|
||||||
|
}
|
||||||
|
coreTransaction, err = converter.appendReceiptToTransaction(coreTransaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("receipt: ", err)
|
log.Println("receipt: ", err)
|
||||||
return err
|
return err
|
||||||
@ -69,7 +122,7 @@ func (rtc *RpcTransactionConverter) ConvertTransactionsToCore(gethBlock *types.B
|
|||||||
return coreTransactions, nil
|
return coreTransactions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rtc *RpcTransactionConverter) appendReceiptToTransaction(transaction core.Transaction) (core.Transaction, error) {
|
func (rtc *RpcTransactionConverter) appendReceiptToTransaction(transaction core.TransactionModel) (core.TransactionModel, error) {
|
||||||
gethReceipt, err := rtc.client.TransactionReceipt(context.Background(), common.HexToHash(transaction.Hash))
|
gethReceipt, err := rtc.client.TransactionReceipt(context.Background(), common.HexToHash(transaction.Hash))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return transaction, err
|
return transaction, err
|
||||||
@ -79,18 +132,76 @@ func (rtc *RpcTransactionConverter) appendReceiptToTransaction(transaction core.
|
|||||||
return transaction, nil
|
return transaction, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func transToCoreTrans(transaction *types.Transaction, from *common.Address) core.Transaction {
|
func convertGethTransactionToModel(transaction *types.Transaction, from *common.Address, transactionIndex int64) (core.TransactionModel, error) {
|
||||||
data := hexutil.Encode(transaction.Data())
|
raw := bytes.Buffer{}
|
||||||
return core.Transaction{
|
encodeErr := transaction.EncodeRLP(&raw)
|
||||||
Hash: transaction.Hash().Hex(),
|
if encodeErr != nil {
|
||||||
Nonce: transaction.Nonce(),
|
return core.TransactionModel{}, encodeErr
|
||||||
To: strings.ToLower(addressToHex(transaction.To())),
|
}
|
||||||
|
return core.TransactionModel{
|
||||||
|
Data: transaction.Data(),
|
||||||
From: strings.ToLower(addressToHex(from)),
|
From: strings.ToLower(addressToHex(from)),
|
||||||
GasLimit: transaction.Gas(),
|
GasLimit: transaction.Gas(),
|
||||||
GasPrice: transaction.GasPrice().Int64(),
|
GasPrice: transaction.GasPrice().Int64(),
|
||||||
|
Hash: transaction.Hash().Hex(),
|
||||||
|
Nonce: transaction.Nonce(),
|
||||||
|
Raw: raw.Bytes(),
|
||||||
|
To: strings.ToLower(addressToHex(transaction.To())),
|
||||||
|
TxIndex: transactionIndex,
|
||||||
Value: transaction.Value().String(),
|
Value: transaction.Value().String(),
|
||||||
Data: data,
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTransactionData(transaction core.RpcTransaction) (transactionData, error) {
|
||||||
|
nonce, nonceErr := hexToBigInt(transaction.Nonce)
|
||||||
|
if nonceErr != nil {
|
||||||
|
return transactionData{}, nonceErr
|
||||||
}
|
}
|
||||||
|
gasPrice, gasPriceErr := hexToBigInt(transaction.GasPrice)
|
||||||
|
if gasPriceErr != nil {
|
||||||
|
return transactionData{}, gasPriceErr
|
||||||
|
}
|
||||||
|
gasLimit, gasLimitErr := hexToBigInt(transaction.GasLimit)
|
||||||
|
if gasLimitErr != nil {
|
||||||
|
return transactionData{}, gasLimitErr
|
||||||
|
}
|
||||||
|
recipient := common.HexToAddress(transaction.Recipient)
|
||||||
|
amount, amountErr := hexToBigInt(transaction.Amount)
|
||||||
|
if amountErr != nil {
|
||||||
|
return transactionData{}, amountErr
|
||||||
|
}
|
||||||
|
v, vErr := hexToBigInt(transaction.V)
|
||||||
|
if vErr != nil {
|
||||||
|
return transactionData{}, vErr
|
||||||
|
}
|
||||||
|
r, rErr := hexToBigInt(transaction.R)
|
||||||
|
if rErr != nil {
|
||||||
|
return transactionData{}, rErr
|
||||||
|
}
|
||||||
|
s, sErr := hexToBigInt(transaction.S)
|
||||||
|
if sErr != nil {
|
||||||
|
return transactionData{}, sErr
|
||||||
|
}
|
||||||
|
return transactionData{
|
||||||
|
AccountNonce: nonce.Uint64(),
|
||||||
|
Price: gasPrice,
|
||||||
|
GasLimit: gasLimit.Uint64(),
|
||||||
|
Recipient: &recipient,
|
||||||
|
Amount: amount,
|
||||||
|
Payload: hexutil.MustDecode(transaction.Payload),
|
||||||
|
V: v,
|
||||||
|
R: r,
|
||||||
|
S: s,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTransactionRLP(txData transactionData) ([]byte, error) {
|
||||||
|
transactionRlp := bytes.Buffer{}
|
||||||
|
encodeErr := rlp.Encode(&transactionRlp, txData)
|
||||||
|
if encodeErr != nil {
|
||||||
|
return nil, encodeErr
|
||||||
|
}
|
||||||
|
return transactionRlp.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addressToHex(to *common.Address) string {
|
func addressToHex(to *common.Address) string {
|
||||||
@ -99,3 +210,12 @@ func addressToHex(to *common.Address) string {
|
|||||||
}
|
}
|
||||||
return to.Hex()
|
return to.Hex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hexToBigInt(hex string) (*big.Int, error) {
|
||||||
|
result := big.NewInt(0)
|
||||||
|
_, scanErr := fmt.Sscan(hex, result)
|
||||||
|
if scanErr != nil {
|
||||||
|
return nil, scanErr
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
108
pkg/geth/converters/rpc/transaction_converter_test.go
Normal file
108
pkg/geth/converters/rpc/transaction_converter_test.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package rpc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("RPC transaction converter", func() {
|
||||||
|
var converter rpc.RpcTransactionConverter
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
converter = rpc.RpcTransactionConverter{}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("converts hex fields to integers", func() {
|
||||||
|
rpcTransaction := getFakeRpcTransaction("0x1")
|
||||||
|
|
||||||
|
transactionModels, err := converter.ConvertRpcTransactionsToModels([]core.RpcTransaction{rpcTransaction})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(transactionModels)).To(Equal(1))
|
||||||
|
Expect(transactionModels[0].GasLimit).To(Equal(uint64(1)))
|
||||||
|
Expect(transactionModels[0].GasPrice).To(Equal(int64(1)))
|
||||||
|
Expect(transactionModels[0].Nonce).To(Equal(uint64(1)))
|
||||||
|
Expect(transactionModels[0].TxIndex).To(Equal(int64(1)))
|
||||||
|
Expect(transactionModels[0].Value).To(Equal("1"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns error if invalid hex cannot be converted", func() {
|
||||||
|
invalidTransaction := getFakeRpcTransaction("invalid")
|
||||||
|
|
||||||
|
_, err := converter.ConvertRpcTransactionsToModels([]core.RpcTransaction{invalidTransaction})
|
||||||
|
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("copies RPC transaction hash, from, and to values to model", func() {
|
||||||
|
rpcTransaction := getFakeRpcTransaction("0x1")
|
||||||
|
|
||||||
|
transactionModels, err := converter.ConvertRpcTransactionsToModels([]core.RpcTransaction{rpcTransaction})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(transactionModels)).To(Equal(1))
|
||||||
|
Expect(transactionModels[0].Hash).To(Equal(rpcTransaction.Hash))
|
||||||
|
Expect(transactionModels[0].From).To(Equal(rpcTransaction.From))
|
||||||
|
Expect(transactionModels[0].To).To(Equal(rpcTransaction.Recipient))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("derives transaction RLP", func() {
|
||||||
|
// actual transaction: https://kovan.etherscan.io/tx/0x3b29ef265425d304069c57e5145cd1c7558568b06d231775f50a693bee1aad4f
|
||||||
|
rpcTransaction := core.RpcTransaction{
|
||||||
|
Nonce: "0x7aa9",
|
||||||
|
GasPrice: "0x3b9aca00",
|
||||||
|
GasLimit: "0x7a120",
|
||||||
|
Recipient: "0xf88bbdc1e2718f8857f30a180076ec38d53cf296",
|
||||||
|
Amount: "0x0",
|
||||||
|
Payload: "0x18178358",
|
||||||
|
V: "0x78",
|
||||||
|
R: "0x79f6a78ababfdb37b87a4d52795a49b08b5b5171443d1f2fb8f373431e77439c",
|
||||||
|
S: "0x3f1a210dd3b59d161735a314b88568fa91552dfe207c00a2fdbcd52ccb081409",
|
||||||
|
Hash: "0x3b29ef265425d304069c57e5145cd1c7558568b06d231775f50a693bee1aad4f",
|
||||||
|
From: "0x694032e172d9b0ee6aff5d36749bad4947a36e4e",
|
||||||
|
TransactionIndex: "0xa",
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionModels, err := converter.ConvertRpcTransactionsToModels([]core.RpcTransaction{rpcTransaction})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(transactionModels)).To(Equal(1))
|
||||||
|
model := transactionModels[0]
|
||||||
|
expectedRLP := []byte{248, 106, 130, 122, 169, 132, 59, 154, 202, 0, 131, 7, 161, 32, 148, 248, 139, 189, 193,
|
||||||
|
226, 113, 143, 136, 87, 243, 10, 24, 0, 118, 236, 56, 213, 60, 242, 150, 128, 132, 24, 23, 131, 88, 120, 160,
|
||||||
|
121, 246, 167, 138, 186, 191, 219, 55, 184, 122, 77, 82, 121, 90, 73, 176, 139, 91, 81, 113, 68, 61, 31, 47,
|
||||||
|
184, 243, 115, 67, 30, 119, 67, 156, 160, 63, 26, 33, 13, 211, 181, 157, 22, 23, 53, 163, 20, 184, 133, 104,
|
||||||
|
250, 145, 85, 45, 254, 32, 124, 0, 162, 253, 188, 213, 44, 203, 8, 20, 9}
|
||||||
|
Expect(model.Raw).To(Equal(expectedRLP))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("does not include transaction receipt", func() {
|
||||||
|
rpcTransaction := getFakeRpcTransaction("0x1")
|
||||||
|
|
||||||
|
transactionModels, err := converter.ConvertRpcTransactionsToModels([]core.RpcTransaction{rpcTransaction})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(transactionModels)).To(Equal(1))
|
||||||
|
Expect(transactionModels[0].Receipt).To(Equal(core.Receipt{}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
func getFakeRpcTransaction(hex string) core.RpcTransaction {
|
||||||
|
return core.RpcTransaction{
|
||||||
|
Hash: "0x2",
|
||||||
|
Amount: hex,
|
||||||
|
GasLimit: hex,
|
||||||
|
GasPrice: hex,
|
||||||
|
Nonce: hex,
|
||||||
|
From: fakes.FakeAddress.Hex(),
|
||||||
|
Recipient: fakes.FakeAddress.Hex(),
|
||||||
|
V: "0x2",
|
||||||
|
R: "0x2",
|
||||||
|
S: "0x2",
|
||||||
|
Payload: "0x12",
|
||||||
|
TransactionIndex: hex,
|
||||||
|
}
|
||||||
|
}
|
@ -66,7 +66,10 @@ func TearDown(db *postgres.DB) {
|
|||||||
_, err = tx.Exec(`DELETE FROM log_filters`)
|
_, err = tx.Exec(`DELETE FROM log_filters`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DELETE FROM transactions`)
|
_, err = tx.Exec(`DELETE FROM full_sync_transactions`)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
_, err = tx.Exec("DELETE FROM light_sync_transactions")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DELETE FROM receipts`)
|
_, err = tx.Exec(`DELETE FROM receipts`)
|
||||||
|
@ -108,13 +108,14 @@ func CleanTestDB(db *postgres.DB) {
|
|||||||
db.MustExec("DELETE FROM blocks")
|
db.MustExec("DELETE FROM blocks")
|
||||||
db.MustExec("DELETE FROM checked_headers")
|
db.MustExec("DELETE FROM checked_headers")
|
||||||
// can't delete from eth_nodes since this function is called after the required eth_node is persisted
|
// can't delete from eth_nodes since this function is called after the required eth_node is persisted
|
||||||
|
db.MustExec("DELETE FROM full_sync_transactions")
|
||||||
db.MustExec("DELETE FROM goose_db_version")
|
db.MustExec("DELETE FROM goose_db_version")
|
||||||
db.MustExec("DELETE FROM headers")
|
db.MustExec("DELETE FROM headers")
|
||||||
|
db.MustExec("DELETE FROM light_sync_transactions")
|
||||||
db.MustExec("DELETE FROM log_filters")
|
db.MustExec("DELETE FROM log_filters")
|
||||||
db.MustExec("DELETE FROM logs")
|
db.MustExec("DELETE FROM logs")
|
||||||
db.MustExec("DELETE FROM queued_storage")
|
db.MustExec("DELETE FROM queued_storage")
|
||||||
db.MustExec("DELETE FROM receipts")
|
db.MustExec("DELETE FROM receipts")
|
||||||
db.MustExec("DELETE FROM transactions")
|
|
||||||
db.MustExec("DELETE FROM watched_contracts")
|
db.MustExec("DELETE FROM watched_contracts")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user