diff --git a/db/schema.sql b/db/schema.sql index b9e03a52..55439ca8 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -2,8 +2,8 @@ -- PostgreSQL database dump -- --- Dumped from database version 10.3 --- Dumped by pg_dump version 10.3 +-- Dumped from database version 10.5 +-- Dumped by pg_dump version 10.4 SET statement_timeout = 0; SET lock_timeout = 0; @@ -16,14 +16,14 @@ SET client_min_messages = warning; SET row_security = off; -- --- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: - +-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -- CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; -- --- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: - +-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -- COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; @@ -34,7 +34,7 @@ SET default_tablespace = ''; SET default_with_oids = false; -- --- Name: logs; Type: TABLE; Schema: public; Owner: - +-- Name: logs; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.logs ( @@ -52,8 +52,10 @@ CREATE TABLE public.logs ( ); +ALTER TABLE public.logs OWNER TO iannorden; + -- --- Name: block_stats; Type: VIEW; Schema: public; Owner: - +-- Name: block_stats; Type: VIEW; Schema: public; Owner: iannorden -- CREATE VIEW public.block_stats AS @@ -62,8 +64,10 @@ CREATE VIEW public.block_stats AS FROM public.logs; +ALTER TABLE public.block_stats OWNER TO iannorden; + -- --- Name: blocks; Type: TABLE; Schema: public; Owner: - +-- Name: blocks; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.blocks ( @@ -88,8 +92,10 @@ CREATE TABLE public.blocks ( ); +ALTER TABLE public.blocks OWNER TO iannorden; + -- --- Name: blocks_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: blocks_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.blocks_id_seq @@ -101,15 +107,17 @@ CREATE SEQUENCE public.blocks_id_seq CACHE 1; +ALTER TABLE public.blocks_id_seq OWNER TO iannorden; + -- --- Name: blocks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: blocks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.blocks_id_seq OWNED BY public.blocks.id; -- --- Name: eth_nodes; Type: TABLE; Schema: public; Owner: - +-- Name: eth_nodes; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.eth_nodes ( @@ -121,8 +129,10 @@ CREATE TABLE public.eth_nodes ( ); +ALTER TABLE public.eth_nodes OWNER TO iannorden; + -- --- Name: headers; Type: TABLE; Schema: public; Owner: - +-- Name: headers; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.headers ( @@ -135,8 +145,10 @@ CREATE TABLE public.headers ( ); +ALTER TABLE public.headers OWNER TO iannorden; + -- --- Name: headers_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: headers_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.headers_id_seq @@ -148,15 +160,17 @@ CREATE SEQUENCE public.headers_id_seq CACHE 1; +ALTER TABLE public.headers_id_seq OWNER TO iannorden; + -- --- Name: headers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: headers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.headers_id_seq OWNED BY public.headers.id; -- --- Name: log_filters; Type: TABLE; Schema: public; Owner: - +-- Name: log_filters; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.log_filters ( @@ -175,8 +189,10 @@ CREATE TABLE public.log_filters ( ); +ALTER TABLE public.log_filters OWNER TO iannorden; + -- --- Name: log_filters_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: log_filters_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.log_filters_id_seq @@ -188,15 +204,17 @@ CREATE SEQUENCE public.log_filters_id_seq CACHE 1; +ALTER TABLE public.log_filters_id_seq OWNER TO iannorden; + -- --- Name: log_filters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: log_filters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.log_filters_id_seq OWNED BY public.log_filters.id; -- --- Name: logs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: logs_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.logs_id_seq @@ -208,15 +226,17 @@ CREATE SEQUENCE public.logs_id_seq CACHE 1; +ALTER TABLE public.logs_id_seq OWNER TO iannorden; + -- --- Name: logs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: logs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.logs_id_seq OWNED BY public.logs.id; -- --- Name: nodes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: nodes_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.nodes_id_seq @@ -228,15 +248,17 @@ CREATE SEQUENCE public.nodes_id_seq CACHE 1; +ALTER TABLE public.nodes_id_seq OWNER TO iannorden; + -- --- Name: nodes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: nodes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.nodes_id_seq OWNED BY public.eth_nodes.id; -- --- Name: receipts; Type: TABLE; Schema: public; Owner: - +-- Name: receipts; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.receipts ( @@ -251,8 +273,10 @@ CREATE TABLE public.receipts ( ); +ALTER TABLE public.receipts OWNER TO iannorden; + -- --- Name: receipts_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: receipts_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.receipts_id_seq @@ -264,15 +288,17 @@ CREATE SEQUENCE public.receipts_id_seq CACHE 1; +ALTER TABLE public.receipts_id_seq OWNER TO iannorden; + -- --- Name: receipts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: receipts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.receipts_id_seq OWNED BY public.receipts.id; -- --- Name: schema_migrations; Type: TABLE; Schema: public; Owner: - +-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.schema_migrations ( @@ -281,8 +307,10 @@ CREATE TABLE public.schema_migrations ( ); +ALTER TABLE public.schema_migrations OWNER TO iannorden; + -- --- Name: token_supply; Type: TABLE; Schema: public; Owner: - +-- Name: token_supply; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.token_supply ( @@ -293,8 +321,10 @@ CREATE TABLE public.token_supply ( ); +ALTER TABLE public.token_supply OWNER TO iannorden; + -- --- Name: token_supply_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: token_supply_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.token_supply_id_seq @@ -306,15 +336,92 @@ CREATE SEQUENCE public.token_supply_id_seq CACHE 1; +ALTER TABLE public.token_supply_id_seq OWNER TO iannorden; + -- --- Name: token_supply_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: token_supply_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.token_supply_id_seq OWNED BY public.token_supply.id; -- --- Name: transactions; Type: TABLE; Schema: public; Owner: - +-- Name: token_allowance; Type: TABLE; Schema: public; Owner: iannorden +-- + +CREATE TABLE public.token_allowance ( + id integer DEFAULT nextval('public.token_supply_id_seq'::regclass) NOT NULL, + block_id integer NOT NULL, + allowance numeric NOT NULL, + token_address character varying(66) NOT NULL, + token_holder_address character varying(66) NOT NULL, + token_spender_address character varying(66) NOT NULL +); + + +ALTER TABLE public.token_allowance OWNER TO iannorden; + +-- +-- Name: token_allowance_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden +-- + +CREATE SEQUENCE public.token_allowance_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.token_allowance_id_seq OWNER TO iannorden; + +-- +-- Name: token_allowance_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden +-- + +ALTER SEQUENCE public.token_allowance_id_seq OWNED BY public.token_allowance.id; + + +-- +-- Name: token_balance; Type: TABLE; Schema: public; Owner: iannorden +-- + +CREATE TABLE public.token_balance ( + id integer DEFAULT nextval('public.token_supply_id_seq'::regclass) NOT NULL, + block_id integer NOT NULL, + balance numeric NOT NULL, + token_address character varying(66) NOT NULL, + token_holder_address character varying(66) NOT NULL +); + + +ALTER TABLE public.token_balance OWNER TO iannorden; + +-- +-- Name: token_balance_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden +-- + +CREATE SEQUENCE public.token_balance_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.token_balance_id_seq OWNER TO iannorden; + +-- +-- Name: token_balance_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden +-- + +ALTER SEQUENCE public.token_balance_id_seq OWNED BY public.token_balance.id; + + +-- +-- Name: transactions; Type: TABLE; Schema: public; Owner: iannorden -- CREATE TABLE public.transactions ( @@ -331,8 +438,10 @@ CREATE TABLE public.transactions ( ); +ALTER TABLE public.transactions OWNER TO iannorden; + -- --- Name: transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.transactions_id_seq @@ -344,15 +453,17 @@ CREATE SEQUENCE public.transactions_id_seq CACHE 1; +ALTER TABLE public.transactions_id_seq OWNER TO iannorden; + -- --- Name: transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- 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: iannorden -- CREATE TABLE public.watched_contracts ( @@ -362,8 +473,10 @@ CREATE TABLE public.watched_contracts ( ); +ALTER TABLE public.watched_contracts OWNER TO iannorden; + -- --- Name: watched_contracts_contract_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: watched_contracts_contract_id_seq; Type: SEQUENCE; Schema: public; Owner: iannorden -- CREATE SEQUENCE public.watched_contracts_contract_id_seq @@ -375,15 +488,17 @@ CREATE SEQUENCE public.watched_contracts_contract_id_seq CACHE 1; +ALTER TABLE public.watched_contracts_contract_id_seq OWNER TO iannorden; + -- --- Name: watched_contracts_contract_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- Name: watched_contracts_contract_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: iannorden -- ALTER SEQUENCE public.watched_contracts_contract_id_seq OWNED BY public.watched_contracts.contract_id; -- --- Name: watched_event_logs; Type: VIEW; Schema: public; Owner: - +-- Name: watched_event_logs; Type: VIEW; Schema: public; Owner: iannorden -- CREATE VIEW public.watched_event_logs AS @@ -405,71 +520,261 @@ CREATE VIEW public.watched_event_logs AS WHERE ((((log_filters.topic0)::text = (logs.topic0)::text) OR (log_filters.topic0 IS NULL)) AND (((log_filters.topic1)::text = (logs.topic1)::text) OR (log_filters.topic1 IS NULL)) AND (((log_filters.topic2)::text = (logs.topic2)::text) OR (log_filters.topic2 IS NULL)) AND (((log_filters.topic3)::text = (logs.topic3)::text) OR (log_filters.topic3 IS NULL))); +ALTER TABLE public.watched_event_logs OWNER TO iannorden; + -- --- Name: blocks id; Type: DEFAULT; Schema: public; Owner: - +-- Name: blocks id; Type: DEFAULT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.blocks ALTER COLUMN id SET DEFAULT nextval('public.blocks_id_seq'::regclass); -- --- Name: eth_nodes id; Type: DEFAULT; Schema: public; Owner: - +-- Name: eth_nodes id; Type: DEFAULT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.eth_nodes ALTER COLUMN id SET DEFAULT nextval('public.nodes_id_seq'::regclass); -- --- Name: headers id; Type: DEFAULT; Schema: public; Owner: - +-- Name: headers id; Type: DEFAULT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.headers ALTER COLUMN id SET DEFAULT nextval('public.headers_id_seq'::regclass); -- --- Name: log_filters id; Type: DEFAULT; Schema: public; Owner: - +-- Name: log_filters id; Type: DEFAULT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.log_filters ALTER COLUMN id SET DEFAULT nextval('public.log_filters_id_seq'::regclass); -- --- Name: logs id; Type: DEFAULT; Schema: public; Owner: - +-- Name: logs id; Type: DEFAULT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.logs ALTER COLUMN id SET DEFAULT nextval('public.logs_id_seq'::regclass); -- --- Name: receipts id; Type: DEFAULT; Schema: public; Owner: - +-- Name: receipts id; Type: DEFAULT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.receipts ALTER COLUMN id SET DEFAULT nextval('public.receipts_id_seq'::regclass); -- --- Name: token_supply id; Type: DEFAULT; Schema: public; Owner: - +-- Name: token_supply id; Type: DEFAULT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.token_supply ALTER COLUMN id SET DEFAULT nextval('public.token_supply_id_seq'::regclass); -- --- Name: transactions id; Type: DEFAULT; Schema: public; Owner: - +-- Name: transactions id; Type: DEFAULT; Schema: public; Owner: iannorden -- 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: iannorden -- ALTER TABLE ONLY public.watched_contracts ALTER COLUMN contract_id SET DEFAULT nextval('public.watched_contracts_contract_id_seq'::regclass); -- --- Name: blocks blocks_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Data for Name: blocks; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.blocks (number, gaslimit, gasused, "time", id, difficulty, hash, nonce, parenthash, size, uncle_hash, eth_node_id, is_final, miner, extra_data, reward, uncles_reward, eth_node_fingerprint) FROM stdin; +\. + + +-- +-- Data for Name: eth_nodes; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.eth_nodes (id, genesis_block, network_id, eth_node_id, client_name) FROM stdin; +1 GENESIS 1 2ea672a45c4c7b96e3c4b130b21a22af390a552fd0b3cff96420b4bda26568d470dc56e05e453823f64f2556a6e4460ad1d4d00eb2d8b8fc16fcb1be73e86522 Geth/v1.7.2-stable-1db4ecdc/darwin-amd64/go1.9 +111 0x456 1 +66 GENESIS 1 x123 geth +42 GENESIS 1 b6f90c0fdd8ec9607aed8ee45c69322e47b7063f0bfb7a29c8ecafab24d0a22d24dd2329b5ee6ed4125a03cb14e57fd584e67f9e53e6c631055cbbd82f080845 Geth/v1.7.2-stable-1db4ecdc/darwin-amd64/go1.9 +70 0 EthNodeFingerprint +104 0x456 1 x123456 Geth +73 0 Fingerprint +74 0 FingerprintTwo +5 GENESIS 1 testNodeId Geth/v1.7.2-stable-1db4ecdc/darwin-amd64/go1.9 +69 0 +81 0 NodeFingerprint +79 0 NodeFingerprintTwo +\. + + +-- +-- Data for Name: headers; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.headers (id, hash, block_number, raw, eth_node_id, eth_node_fingerprint) FROM stdin; +304 1 \\x 81 NodeFingerprint +305 3 \\x 81 NodeFingerprint +306 5 \\x 81 NodeFingerprint +\. + + +-- +-- Data for Name: log_filters; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.log_filters (id, name, from_block, to_block, address, topic0, topic1, topic2, topic3) FROM stdin; +\. + + +-- +-- Data for Name: logs; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.logs (id, block_number, address, tx_hash, index, topic0, topic1, topic2, topic3, data, receipt_id) FROM stdin; +\. + + +-- +-- Data for Name: receipts; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.receipts (id, contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, block_id) FROM stdin; +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.schema_migrations (version, dirty) FROM stdin; +\. + + +-- +-- Data for Name: token_allowance; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.token_allowance (id, block_id, allowance, token_address, token_holder_address, token_spender_address) FROM stdin; +\. + + +-- +-- Data for Name: token_balance; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.token_balance (id, block_id, balance, token_address, token_holder_address) FROM stdin; +\. + + +-- +-- Data for Name: token_supply; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.token_supply (id, block_id, supply, token_address) FROM stdin; +\. + + +-- +-- Data for Name: transactions; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.transactions (id, hash, nonce, tx_to, gaslimit, gasprice, value, block_id, tx_from, input_data) FROM stdin; +\. + + +-- +-- Data for Name: watched_contracts; Type: TABLE DATA; Schema: public; Owner: iannorden +-- + +COPY public.watched_contracts (contract_id, contract_hash, contract_abi) FROM stdin; +\. + + +-- +-- Name: blocks_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.blocks_id_seq', 1902, true); + + +-- +-- Name: headers_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.headers_id_seq', 306, true); + + +-- +-- Name: log_filters_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.log_filters_id_seq', 102, true); + + +-- +-- Name: logs_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.logs_id_seq', 290, true); + + +-- +-- Name: nodes_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.nodes_id_seq', 1770, true); + + +-- +-- Name: receipts_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.receipts_id_seq', 153, true); + + +-- +-- Name: token_allowance_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.token_allowance_id_seq', 1, false); + + +-- +-- Name: token_balance_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.token_balance_id_seq', 1, false); + + +-- +-- Name: token_supply_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.token_supply_id_seq', 400, true); + + +-- +-- Name: transactions_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.transactions_id_seq', 340, true); + + +-- +-- Name: watched_contracts_contract_id_seq; Type: SEQUENCE SET; Schema: public; Owner: iannorden +-- + +SELECT pg_catalog.setval('public.watched_contracts_contract_id_seq', 102, true); + + +-- +-- Name: blocks blocks_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.blocks @@ -477,7 +782,7 @@ ALTER TABLE ONLY public.blocks -- --- Name: watched_contracts contract_hash_uc; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: watched_contracts contract_hash_uc; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.watched_contracts @@ -485,7 +790,7 @@ ALTER TABLE ONLY public.watched_contracts -- --- Name: blocks eth_node_id_block_number_uc; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: blocks eth_node_id_block_number_uc; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.blocks @@ -493,7 +798,7 @@ ALTER TABLE ONLY public.blocks -- --- Name: eth_nodes eth_node_uc; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: eth_nodes eth_node_uc; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.eth_nodes @@ -501,7 +806,7 @@ ALTER TABLE ONLY public.eth_nodes -- --- Name: headers headers_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: headers headers_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.headers @@ -509,7 +814,7 @@ ALTER TABLE ONLY public.headers -- --- Name: logs logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: logs logs_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.logs @@ -517,7 +822,7 @@ ALTER TABLE ONLY public.logs -- --- Name: log_filters name_uc; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: log_filters name_uc; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.log_filters @@ -525,7 +830,7 @@ ALTER TABLE ONLY public.log_filters -- --- Name: eth_nodes nodes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: eth_nodes nodes_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.eth_nodes @@ -533,7 +838,7 @@ ALTER TABLE ONLY public.eth_nodes -- --- Name: receipts receipts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: receipts receipts_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.receipts @@ -541,7 +846,7 @@ ALTER TABLE ONLY public.receipts -- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.schema_migrations @@ -549,7 +854,7 @@ ALTER TABLE ONLY public.schema_migrations -- --- Name: transactions transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: transactions transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.transactions @@ -557,7 +862,7 @@ ALTER TABLE ONLY public.transactions -- --- Name: watched_contracts watched_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: watched_contracts watched_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.watched_contracts @@ -565,42 +870,42 @@ ALTER TABLE ONLY public.watched_contracts -- --- Name: block_id_index; Type: INDEX; Schema: public; Owner: - +-- Name: block_id_index; Type: INDEX; Schema: public; Owner: iannorden -- CREATE INDEX block_id_index ON public.transactions USING btree (block_id); -- --- Name: block_number_index; Type: INDEX; Schema: public; Owner: - +-- Name: block_number_index; Type: INDEX; Schema: public; Owner: iannorden -- CREATE INDEX block_number_index ON public.blocks USING btree (number); -- --- Name: node_id_index; Type: INDEX; Schema: public; Owner: - +-- Name: node_id_index; Type: INDEX; Schema: public; Owner: iannorden -- CREATE INDEX node_id_index ON public.blocks USING btree (eth_node_id); -- --- Name: tx_from_index; Type: INDEX; Schema: public; Owner: - +-- Name: tx_from_index; Type: INDEX; Schema: public; Owner: iannorden -- CREATE INDEX tx_from_index ON public.transactions USING btree (tx_from); -- --- Name: tx_to_index; Type: INDEX; Schema: public; Owner: - +-- Name: tx_to_index; Type: INDEX; Schema: public; Owner: iannorden -- CREATE INDEX tx_to_index ON public.transactions USING btree (tx_to); -- --- Name: transactions blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: transactions blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.transactions @@ -608,7 +913,7 @@ ALTER TABLE ONLY public.transactions -- --- Name: receipts blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: receipts blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.receipts @@ -616,7 +921,7 @@ ALTER TABLE ONLY public.receipts -- --- Name: token_supply blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: token_supply blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.token_supply @@ -624,7 +929,23 @@ ALTER TABLE ONLY public.token_supply -- --- Name: headers eth_nodes_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: token_balance blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden +-- + +ALTER TABLE ONLY public.token_balance + ADD CONSTRAINT blocks_fk FOREIGN KEY (block_id) REFERENCES public.blocks(id) ON DELETE CASCADE; + + +-- +-- Name: token_allowance blocks_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden +-- + +ALTER TABLE ONLY public.token_allowance + ADD CONSTRAINT blocks_fk FOREIGN KEY (block_id) REFERENCES public.blocks(id) ON DELETE CASCADE; + + +-- +-- Name: headers eth_nodes_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.headers @@ -632,7 +953,7 @@ ALTER TABLE ONLY public.headers -- --- Name: blocks node_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: blocks node_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.blocks @@ -640,7 +961,7 @@ ALTER TABLE ONLY public.blocks -- --- Name: logs receipts_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: logs receipts_fk; Type: FK CONSTRAINT; Schema: public; Owner: iannorden -- ALTER TABLE ONLY public.logs diff --git a/examples/erc20_watcher/every_block/getter_test.go b/examples/erc20_watcher/every_block/getter_test.go index a6447811..c41726b0 100644 --- a/examples/erc20_watcher/every_block/getter_test.go +++ b/examples/erc20_watcher/every_block/getter_test.go @@ -22,6 +22,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/ethereum/go-ethereum/common" "github.com/vulcanize/vulcanizedb/examples/constants" "github.com/vulcanize/vulcanizedb/examples/erc20_watcher/every_block" "github.com/vulcanize/vulcanizedb/pkg/fakes" @@ -32,7 +33,7 @@ import ( ) var _ = Describe("ERC20 Getter", func() { - blockNumber := int64(5502914) + blockNumber := int64(6194634) Describe("totalSupply", func() { It("gets total supply data from the blockchain with the correct arguments", func() { @@ -64,8 +65,8 @@ var _ = Describe("ERC20 Getter", func() { Expect(err).NotTo(HaveOccurred()) expectedResult := big.Int{} - expectedResult.SetString("27647235749155415536952630", 10) - Expect(result).To(Equal(expectedResult)) + expectedResult.SetString("47327413946297204537985606", 10) + Expect(result.String()).To(Equal(expectedResult.String())) }) It("returns an error if the call to the blockchain fails", func() { @@ -80,4 +81,132 @@ var _ = Describe("ERC20 Getter", func() { Expect(err.Error()).To(ContainSubstring(fakes.FakeError.Error())) }) }) + + Describe("balanceOf", func() { + It("gets balance of a token holder address at a token contract address from the blockchain with the correct arguments", func() { + fakeBlockChain := fakes.NewMockBlockChain() + testGetter := every_block.NewGetter(fakeBlockChain) + testAbi := "testAbi" + testContractAddress := "testContractAddress" + + testTokenHolderAddress := common.HexToAddress("0x2cccc4b4708b318a6290511aac75d6c3dbe0cf9f") + hashArgs := []common.Address{testTokenHolderAddress} + balanceOfArgs := make([]interface{}, len(hashArgs)) + for i, s := range hashArgs { + balanceOfArgs[i] = s + } + + _, err := testGetter.GetBalance(testAbi, testContractAddress, blockNumber, balanceOfArgs) + + Expect(err).NotTo(HaveOccurred()) + expectedResult := big.Int{} + expected := &expectedResult + fakeBlockChain.AssertFetchContractDataCalledWith(testAbi, testContractAddress, "balanceOf", balanceOfArgs, &expected, blockNumber) + }) + + It("gets a token holder address's balance on the dai contract at the given block height", func() { + infuraIPC := "https://mainnet.infura.io/J5Vd2fRtGsw0zZ0Ov3BL" + rawRpcClient, err := rpc.Dial(infuraIPC) + Expect(err).NotTo(HaveOccurred()) + rpcClient := client.NewRpcClient(rawRpcClient, infuraIPC) + ethClient := ethclient.NewClient(rawRpcClient) + blockChainClient := client.NewEthClient(ethClient) + node := node.MakeNode(rpcClient) + transactionConverter := rpc2.NewRpcTransactionConverter(ethClient) + blockChain := geth.NewBlockChain(blockChainClient, node, transactionConverter) + realGetter := every_block.NewGetter(blockChain) + + testTokenHolderAddress := common.HexToAddress("0x2cccc4b4708b318a6290511aac75d6c3dbe0cf9f") + hashArgs := []common.Address{testTokenHolderAddress} + balanceOfArgs := make([]interface{}, len(hashArgs)) + for i, s := range hashArgs { + balanceOfArgs[i] = s + } + + result, err := realGetter.GetBalance(constants.DaiAbiString, constants.DaiContractAddress, blockNumber, balanceOfArgs) + + Expect(err).NotTo(HaveOccurred()) + expectedResult := big.Int{} + expectedResult.SetString("1000000000000000000000000", 10) + Expect(result.String()).To(Equal(expectedResult.String())) + }) + + It("returns an error if the call to the blockchain fails", func() { + blockChain := fakes.NewMockBlockChain() + blockChain.SetFetchContractDataErr(fakes.FakeError) + errorGetter := every_block.NewGetter(blockChain) + result, err := errorGetter.GetBalance("", "", 0, nil) + + Expect(result.String()).To(Equal("0")) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("balanceOf")) + Expect(err.Error()).To(ContainSubstring(fakes.FakeError.Error())) + }) + }) + + Describe("allowance", func() { + It("gets allowance data from the blockchain with the correct arguments", func() { + fakeBlockChain := fakes.NewMockBlockChain() + testGetter := every_block.NewGetter(fakeBlockChain) + testAbi := "testAbi" + testContractAddress := "testContractAddress" + + testTokenHolderAddress := common.HexToAddress("0x2cccc4b4708b318a6290511aac75d6c3dbe0cf9f") + testTokenSpenderAddress := common.HexToAddress("0x2cccc4b4708b318a6290511aac75d6c3dbe0cf9f") + + hashArgs := []common.Address{testTokenHolderAddress, testTokenSpenderAddress} + allowanceArgs := make([]interface{}, len(hashArgs)) + for i, s := range hashArgs { + allowanceArgs[i] = s + } + + _, err := testGetter.GetAllowance(testAbi, testContractAddress, blockNumber, allowanceArgs) + + Expect(err).NotTo(HaveOccurred()) + expectedResult := big.Int{} + expected := &expectedResult + fakeBlockChain.AssertFetchContractDataCalledWith(testAbi, testContractAddress, "allowance", allowanceArgs, &expected, blockNumber) + }) + + It("gets the allowance for a spending address and holder address on the dai contract at the given block height", func() { + infuraIPC := "https://mainnet.infura.io/J5Vd2fRtGsw0zZ0Ov3BL" + rawRpcClient, err := rpc.Dial(infuraIPC) + Expect(err).NotTo(HaveOccurred()) + rpcClient := client.NewRpcClient(rawRpcClient, infuraIPC) + ethClient := ethclient.NewClient(rawRpcClient) + blockChainClient := client.NewEthClient(ethClient) + node := node.MakeNode(rpcClient) + transactionConverter := rpc2.NewRpcTransactionConverter(ethClient) + blockChain := geth.NewBlockChain(blockChainClient, node, transactionConverter) + realGetter := every_block.NewGetter(blockChain) + + testTokenHolderAddress := common.HexToAddress("0x2cccc4b4708b318a6290511aac75d6c3dbe0cf9f") + testTokenSpenderAddress := common.HexToAddress("0x2cccc4b4708b318a6290511aac75d6c3dbe0cf9f") + + hashArgs := []common.Address{testTokenHolderAddress, testTokenSpenderAddress} + allowanceArgs := make([]interface{}, len(hashArgs)) + for i, s := range hashArgs { + allowanceArgs[i] = s + } + + result, err := realGetter.GetAllowance(constants.DaiAbiString, constants.DaiContractAddress, blockNumber, allowanceArgs) + + Expect(err).NotTo(HaveOccurred()) + expectedResult := big.Int{} + expectedResult.SetString("0", 10) + Expect(result.String()).To(Equal(expectedResult.String())) + }) + + It("returns an error if the call to the blockchain fails", func() { + blockChain := fakes.NewMockBlockChain() + blockChain.SetFetchContractDataErr(fakes.FakeError) + errorGetter := every_block.NewGetter(blockChain) + result, err := errorGetter.GetAllowance("", "", 0, nil) + + Expect(result.String()).To(Equal("0")) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("allowance")) + Expect(err.Error()).To(ContainSubstring(fakes.FakeError.Error())) + }) + }) }) diff --git a/examples/erc20_watcher/every_block/repository.go b/examples/erc20_watcher/every_block/repository.go index 61b62f72..15fa7325 100644 --- a/examples/erc20_watcher/every_block/repository.go +++ b/examples/erc20_watcher/every_block/repository.go @@ -23,7 +23,11 @@ import ( // Interface definition for a generic ERC20 token repository type ERC20RepositoryInterface interface { CreateSupply(supply TokenSupply) error + CreateBalance(balance TokenBalance) error + CreateAllowance(allowance TokenAllowance) error MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) + MissingBalanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress string) ([]int64, error) + MissingAllowanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress, spenderAddress string) ([]int64, error) } // Generic ERC20 token Repo struct @@ -52,9 +56,11 @@ func newRepositoryError(err error, msg string, blockNumber int64) error { // Constant error definitions const ( - GetBlockError = "Error fetching block number %d: %s" - InsertTokenSupplyError = "Error inserting token_supply for block number %d: %s" - MissingBlockError = "Error finding missing token_supply records starting at block %d: %s" + GetBlockError = "Error fetching block number %d: %s" + InsertTokenSupplyError = "Error inserting token_supply for block number %d: %s" + InsertTokenBalanceError = "Error inserting token_balance for block number %d: %s" + InsertTokenAllowanceError = "Error inserting token_allowance for block number %d: %s" + MissingBlockError = "Error finding missing token_supply records starting at block %d: %s" ) // Supply methods @@ -100,3 +106,97 @@ func (tsp *ERC20TokenRepository) MissingSupplyBlocks(startingBlock, highestBlock } return blockNumbers, err } + +// Balance methods +// This method inserts the balance for a given token contract address and token owner address at a given block height into the token_balance table +func (tsp *ERC20TokenRepository) CreateBalance(balance TokenBalance) error { + var blockId int + err := tsp.DB.Get(&blockId, `SELECT id FROM blocks WHERE number = $1 AND eth_node_id = $2`, balance.BlockNumber, tsp.NodeID) + if err != nil { + return newRepositoryError(err, GetBlockError, balance.BlockNumber) + } + + _, err = tsp.DB.Exec( + `INSERT INTO token_balance (balance, token_address, block_id, token_holder_address) + VALUES($1, $2, $3, $4)`, + balance.Value, balance.TokenAddress, blockId, balance.TokenHolderAddress) + if err != nil { + return newRepositoryError(err, InsertTokenBalanceError, balance.BlockNumber) + } + return nil +} + +// This method returns an array of blocks that are missing a token_balance entry for a given token contract address and token owner address +func (tsp *ERC20TokenRepository) MissingBalanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress string) ([]int64, error) { + blockNumbers := make([]int64, 0) + + err := tsp.DB.Select( + &blockNumbers, + `SELECT number FROM BLOCKS + LEFT JOIN token_balance ON blocks.id = block_id + AND token_address = $1 + AND token_holder_address = $2 + WHERE block_id ISNULL + AND eth_node_id = $3 + AND number >= $4 + AND number <= $5 + LIMIT 20`, + tokenAddress, + holderAddress, + tsp.NodeID, + startingBlock, + highestBlock, + ) + if err != nil { + return []int64{}, newRepositoryError(err, MissingBlockError, startingBlock) + } + return blockNumbers, err +} + +// Allowance methods +// This method inserts the allowance for a given token contract address, token owner address, and token spender address at a given block height into the +func (tsp *ERC20TokenRepository) CreateAllowance(allowance TokenAllowance) error { + var blockId int + err := tsp.DB.Get(&blockId, `SELECT id FROM blocks WHERE number = $1 AND eth_node_id = $2`, allowance.BlockNumber, tsp.NodeID) + if err != nil { + return newRepositoryError(err, GetBlockError, allowance.BlockNumber) + } + + _, err = tsp.DB.Exec( + `INSERT INTO token_allowance (allowance, token_address, block_id, token_holder_address, token_spender_address) + VALUES($1, $2, $3, $4, $5)`, + allowance.Value, allowance.TokenAddress, blockId, allowance.TokenHolderAddress, allowance.TokenSpenderAddress) + if err != nil { + return newRepositoryError(err, InsertTokenAllowanceError, allowance.BlockNumber) + } + return nil +} + +// This method returns an array of blocks that are missing a token_allowance entry for a given token contract address, token owner address, and token spender address +func (tsp *ERC20TokenRepository) MissingAllowanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress, spenderAddress string) ([]int64, error) { + blockNumbers := make([]int64, 0) + + err := tsp.DB.Select( + &blockNumbers, + `SELECT number FROM BLOCKS + LEFT JOIN token_allowance ON blocks.id = block_id + AND token_address = $1 + AND token_holder_address = $2 + AND token_spender_address = $3 + WHERE block_id ISNULL + AND eth_node_id = $4 + AND number >= $5 + AND number <= $6 + LIMIT 20`, + tokenAddress, + holderAddress, + spenderAddress, + tsp.NodeID, + startingBlock, + highestBlock, + ) + if err != nil { + return []int64{}, newRepositoryError(err, MissingBlockError, startingBlock) + } + return blockNumbers, err +} diff --git a/examples/erc20_watcher/every_block/repository_test.go b/examples/erc20_watcher/every_block/repository_test.go index e6ed2136..5b6176ad 100644 --- a/examples/erc20_watcher/every_block/repository_test.go +++ b/examples/erc20_watcher/every_block/repository_test.go @@ -182,6 +182,335 @@ var _ = Describe("ERC20 Token Supply Repository", func() { }) }) +var _ = Describe("ERC20 Token Balance Repository", func() { + var db *postgres.DB + var blockId int64 + var blockNumber int64 + var repository every_block.ERC20TokenRepository + var blockRepository repositories.BlockRepository + testTokenAddress := "abc" + testHolderAddress := "def" + + BeforeEach(func() { + db = test_helpers.CreateNewDatabase() + repository = every_block.ERC20TokenRepository{DB: db} + _, err := db.Query(`DELETE FROM token_balance`) + Expect(err).NotTo(HaveOccurred()) + + blockRepository = *repositories.NewBlockRepository(db) + blockNumber = rand.Int63() + blockId = test_helpers.CreateBlock(blockNumber, blockRepository) + }) + + Describe("Create", func() { + It("creates a token balance record", func() { + balance := balanceOfModel(blockNumber, testTokenAddress, testHolderAddress, "100") + err := repository.CreateBalance(balance) + Expect(err).NotTo(HaveOccurred()) + + dbResult := test_helpers.TokenBalanceDBRow{} + expectedTokenBalance := test_helpers.TokenBalanceDBRow{ + Balance: int64(100), + BlockID: blockId, + TokenAddress: testTokenAddress, + TokenHolderAddress: testHolderAddress, + } + + var count int + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_balance`).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(1)) + + err = repository.DB.QueryRowx(`SELECT * FROM token_balance`).StructScan(&dbResult) + Expect(err).NotTo(HaveOccurred()) + Expect(dbResult.Balance).To(Equal(expectedTokenBalance.Balance)) + Expect(dbResult.BlockID).To(Equal(expectedTokenBalance.BlockID)) + Expect(dbResult.TokenAddress).To(Equal(expectedTokenBalance.TokenAddress)) + Expect(dbResult.TokenHolderAddress).To(Equal(expectedTokenBalance.TokenHolderAddress)) + }) + + It("returns an error if fetching the block's id from the database fails", func() { + errorBalance := balanceOfModel(-1, "", "", "") + err := repository.CreateBalance(errorBalance) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("sql")) + Expect(err.Error()).To(ContainSubstring("block number -1")) + }) + + It("returns an error if inserting the token_balance fails", func() { + errorBalance := balanceOfModel(blockNumber, "", "", "") + err := repository.CreateBalance(errorBalance) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq")) + Expect(err.Error()).To(ContainSubstring("token_balance for block number")) + }) + }) + + Describe("When there are multiple nodes", func() { + var node2DB *postgres.DB + var node2BlockRepo *repositories.BlockRepository + var node2BlockId int64 + var node2TokenSupplyRepo every_block.ERC20TokenRepository + var tokenBalance every_block.TokenBalance + + BeforeEach(func() { + node2DB = createDbForAnotherNode() + + //create another block with the same number on node2 + node2BlockRepo = repositories.NewBlockRepository(node2DB) + node2BlockId = test_helpers.CreateBlock(blockNumber, *node2BlockRepo) + + tokenBalance = balanceOfModel(blockNumber, "abc", "def", "100") + node2TokenSupplyRepo = every_block.ERC20TokenRepository{DB: node2DB} + }) + + It("only creates token_balance records for the current node (node2)", func() { + err := node2TokenSupplyRepo.CreateBalance(tokenBalance) + Expect(err).NotTo(HaveOccurred()) + + var tokenBalances []test_helpers.TokenBalanceDBRow + err = node2TokenSupplyRepo.DB.Select(&tokenBalances, `SELECT * FROM token_balance`) + Expect(err).NotTo(HaveOccurred()) + Expect(len(tokenBalances)).To(Equal(1)) + Expect(tokenBalances[0].BlockID).To(Equal(node2BlockId)) + }) + + It("only includes missing block numbers for the current node", func() { + //create token_balance on original node + err := repository.CreateBalance(tokenBalance) + Expect(err).NotTo(HaveOccurred()) + + originalNodeMissingBlocks, err := repository.MissingBalanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(len(originalNodeMissingBlocks)).To(Equal(0)) + + node2MissingBlocks, err := node2TokenSupplyRepo.MissingBalanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(len(node2MissingBlocks)).To(Equal(1)) + }) + }) + + Describe("MissingBlocks", func() { + It("returns the block numbers for which an associated TokenBalance record hasn't been created", func() { + createTokenBalanceFor(repository, blockNumber, testTokenAddress, testHolderAddress) + + newBlockNumber := blockNumber + 1 + test_helpers.CreateBlock(newBlockNumber, blockRepository) + blocks, err := repository.MissingBalanceBlocks(blockNumber, newBlockNumber, testTokenAddress, testHolderAddress) + + Expect(blocks).To(ConsistOf(newBlockNumber)) + Expect(err).NotTo(HaveOccurred()) + }) + + It("only returns blocks within the given range", func() { + newBlockNumber := blockNumber + 1 + test_helpers.CreateBlock(newBlockNumber, blockRepository) + blocks, err := repository.MissingBalanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress) + + Expect(blocks).NotTo(ConsistOf(newBlockNumber)) + Expect(err).NotTo(HaveOccurred()) + }) + + It("does not return numbers that already have an associated TokenBalance record", func() { + createTokenBalanceFor(repository, blockNumber, testTokenAddress, testHolderAddress) + blocks, err := repository.MissingBalanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress) + + Expect(blocks).To(BeEmpty()) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + It("deletes the token balance record when the associated block is deleted", func() { + err := repository.CreateBalance(every_block.TokenBalance{ + BlockNumber: blockNumber, + TokenAddress: testTokenAddress, + TokenHolderAddress: testHolderAddress, + Value: "0", + }) + Expect(err).NotTo(HaveOccurred()) + + var count int + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_balance`).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(1)) + + _, err = db.Query(`DELETE FROM blocks`) + Expect(err).NotTo(HaveOccurred()) + + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_balance`).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(0)) + }) +}) + +var _ = Describe("ERC20 Token Allowance Repository", func() { + var db *postgres.DB + var blockId int64 + var blockNumber int64 + var repository every_block.ERC20TokenRepository + var blockRepository repositories.BlockRepository + testTokenAddress := "abc" + testHolderAddress := "def" + testSpenderAddress := "ghi" + + BeforeEach(func() { + db = test_helpers.CreateNewDatabase() + repository = every_block.ERC20TokenRepository{DB: db} + _, err := db.Query(`DELETE FROM token_allowance`) + Expect(err).NotTo(HaveOccurred()) + + blockRepository = *repositories.NewBlockRepository(db) + blockNumber = rand.Int63() + blockId = test_helpers.CreateBlock(blockNumber, blockRepository) + }) + + Describe("Create", func() { + It("creates a token balance record", func() { + allowance := allowanceModel(blockNumber, testTokenAddress, testHolderAddress, testSpenderAddress, "100") + err := repository.CreateAllowance(allowance) + Expect(err).NotTo(HaveOccurred()) + + dbResult := test_helpers.TokenAllowanceDBRow{} + expectedTokenAllowance := test_helpers.TokenAllowanceDBRow{ + Allowance: int64(100), + BlockID: blockId, + TokenAddress: testTokenAddress, + TokenHolderAddress: testHolderAddress, + TokenSpenderAddress: testSpenderAddress, + } + + var count int + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_allowance`).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(1)) + + err = repository.DB.QueryRowx(`SELECT * FROM token_allowance`).StructScan(&dbResult) + Expect(err).NotTo(HaveOccurred()) + Expect(dbResult.Allowance).To(Equal(expectedTokenAllowance.Allowance)) + Expect(dbResult.BlockID).To(Equal(expectedTokenAllowance.BlockID)) + Expect(dbResult.TokenAddress).To(Equal(expectedTokenAllowance.TokenAddress)) + Expect(dbResult.TokenHolderAddress).To(Equal(expectedTokenAllowance.TokenHolderAddress)) + }) + + It("returns an error if fetching the block's id from the database fails", func() { + errorAllowance := allowanceModel(-1, "", "", "", "") + err := repository.CreateAllowance(errorAllowance) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("sql")) + Expect(err.Error()).To(ContainSubstring("block number -1")) + }) + + It("returns an error if inserting the token_allowance fails", func() { + errorAllowance := allowanceModel(blockNumber, "", "", "", "") + err := repository.CreateAllowance(errorAllowance) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq")) + Expect(err.Error()).To(ContainSubstring("token_allowance for block number")) + }) + }) + + Describe("When there are multiple nodes", func() { + var node2DB *postgres.DB + var node2BlockRepo *repositories.BlockRepository + var node2BlockId int64 + var node2TokenSupplyRepo every_block.ERC20TokenRepository + var tokenAllowance every_block.TokenAllowance + + BeforeEach(func() { + node2DB = createDbForAnotherNode() + + //create another block with the same number on node2 + node2BlockRepo = repositories.NewBlockRepository(node2DB) + node2BlockId = test_helpers.CreateBlock(blockNumber, *node2BlockRepo) + + tokenAllowance = allowanceModel(blockNumber, "abc", "def", "ghi", "100") + node2TokenSupplyRepo = every_block.ERC20TokenRepository{DB: node2DB} + }) + + It("only creates token_allowance records for the current node (node2)", func() { + err := node2TokenSupplyRepo.CreateAllowance(tokenAllowance) + Expect(err).NotTo(HaveOccurred()) + + var tokenAllowances []test_helpers.TokenAllowanceDBRow + err = node2TokenSupplyRepo.DB.Select(&tokenAllowances, `SELECT * FROM token_allowance`) + Expect(err).NotTo(HaveOccurred()) + Expect(len(tokenAllowances)).To(Equal(1)) + Expect(tokenAllowances[0].BlockID).To(Equal(node2BlockId)) + }) + + It("only includes missing block numbers for the current node", func() { + //create token_allowance on original node + err := repository.CreateAllowance(tokenAllowance) + Expect(err).NotTo(HaveOccurred()) + + originalNodeMissingBlocks, err := repository.MissingAllowanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress, testSpenderAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(len(originalNodeMissingBlocks)).To(Equal(0)) + + node2MissingBlocks, err := node2TokenSupplyRepo.MissingAllowanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress, testSpenderAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(len(node2MissingBlocks)).To(Equal(1)) + }) + }) + + Describe("MissingBlocks", func() { + It("returns the block numbers for which an associated TokenAllowance record hasn't been created", func() { + createTokenAllowanceFor(repository, blockNumber, testTokenAddress, testHolderAddress, testSpenderAddress) + + newBlockNumber := blockNumber + 1 + test_helpers.CreateBlock(newBlockNumber, blockRepository) + blocks, err := repository.MissingAllowanceBlocks(blockNumber, newBlockNumber, testTokenAddress, testHolderAddress, testSpenderAddress) + + Expect(blocks).To(ConsistOf(newBlockNumber)) + Expect(err).NotTo(HaveOccurred()) + }) + + It("only returns blocks within the given range", func() { + newBlockNumber := blockNumber + 1 + test_helpers.CreateBlock(newBlockNumber, blockRepository) + blocks, err := repository.MissingAllowanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress, testSpenderAddress) + + Expect(blocks).NotTo(ConsistOf(newBlockNumber)) + Expect(err).NotTo(HaveOccurred()) + }) + + It("does not return numbers that already have an associated TokenAllowance record", func() { + createTokenAllowanceFor(repository, blockNumber, testTokenAddress, testHolderAddress, testSpenderAddress) + blocks, err := repository.MissingAllowanceBlocks(blockNumber, blockNumber, testTokenAddress, testHolderAddress, testSpenderAddress) + + Expect(blocks).To(BeEmpty()) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + It("deletes the token balance record when the associated block is deleted", func() { + err := repository.CreateAllowance(every_block.TokenAllowance{ + BlockNumber: blockNumber, + TokenAddress: testTokenAddress, + TokenHolderAddress: testHolderAddress, + TokenSpenderAddress: testSpenderAddress, + Value: "0", + }) + Expect(err).NotTo(HaveOccurred()) + + var count int + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_allowance`).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(1)) + + _, err = db.Query(`DELETE FROM blocks`) + Expect(err).NotTo(HaveOccurred()) + + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_allowance`).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + Expect(count).To(Equal(0)) + }) +}) + func supplyModel(blockNumber int64, tokenAddress, supplyValue string) every_block.TokenSupply { return every_block.TokenSupply{ Value: supplyValue, @@ -190,6 +519,25 @@ func supplyModel(blockNumber int64, tokenAddress, supplyValue string) every_bloc } } +func balanceOfModel(blockNumber int64, tokenAddress, holderAddress, supplyValue string) every_block.TokenBalance { + return every_block.TokenBalance{ + Value: supplyValue, + TokenAddress: tokenAddress, + TokenHolderAddress: holderAddress, + BlockNumber: blockNumber, + } +} + +func allowanceModel(blockNumber int64, tokenAddress, holderAddress, spenderAddress, supplyValue string) every_block.TokenAllowance { + return every_block.TokenAllowance{ + Value: supplyValue, + TokenAddress: tokenAddress, + TokenHolderAddress: holderAddress, + TokenSpenderAddress: spenderAddress, + BlockNumber: blockNumber, + } +} + func createTokenSupplyFor(repository every_block.ERC20TokenRepository, blockNumber int64, tokenAddress string) { err := repository.CreateSupply(every_block.TokenSupply{ BlockNumber: blockNumber, @@ -199,6 +547,27 @@ func createTokenSupplyFor(repository every_block.ERC20TokenRepository, blockNumb Expect(err).NotTo(HaveOccurred()) } +func createTokenBalanceFor(repository every_block.ERC20TokenRepository, blockNumber int64, tokenAddress, holderAddress string) { + err := repository.CreateBalance(every_block.TokenBalance{ + BlockNumber: blockNumber, + TokenAddress: tokenAddress, + TokenHolderAddress: holderAddress, + Value: "0", + }) + Expect(err).NotTo(HaveOccurred()) +} + +func createTokenAllowanceFor(repository every_block.ERC20TokenRepository, blockNumber int64, tokenAddress, holderAddress, spenderAddress string) { + err := repository.CreateAllowance(every_block.TokenAllowance{ + BlockNumber: blockNumber, + TokenAddress: tokenAddress, + TokenHolderAddress: holderAddress, + TokenSpenderAddress: spenderAddress, + Value: "0", + }) + Expect(err).NotTo(HaveOccurred()) +} + func createDbForAnotherNode() *postgres.DB { anotherNode := core.Node{ GenesisBlock: "GENESIS", diff --git a/examples/test_helpers/database.go b/examples/test_helpers/database.go index b15521d7..3e7acf45 100644 --- a/examples/test_helpers/database.go +++ b/examples/test_helpers/database.go @@ -30,6 +30,23 @@ type TokenSupplyDBRow struct { TokenAddress string `db:"token_address"` } +type TokenBalanceDBRow struct { + ID int64 `db:"id"` + Balance int64 `db:"balance"` + BlockID int64 `db:"block_id"` + TokenAddress string `db:"token_address"` + TokenHolderAddress string `db:"token_holder_address"` +} + +type TokenAllowanceDBRow struct { + ID int64 `db:"id"` + Allowance int64 `db:"allowance"` + BlockID int64 `db:"block_id"` + TokenAddress string `db:"token_address"` + TokenHolderAddress string `db:"token_holder_address"` + TokenSpenderAddress string `db:"token_spender_address"` +} + type TransferDBRow struct { ID int64 `db:"id"` VulcanizeLogID int64 `db:"vulcanize_log_id"` @@ -40,7 +57,7 @@ func CreateNewDatabase() *postgres.DB { node = core.Node{ GenesisBlock: "GENESIS", NetworkID: 1, - ID: "b6f90c0fdd8ec9607aed8ee45c69322e47b7063f0bfb7a29c8ecafab24d0a22d24dd2329b5ee6ed4125a03cb14e57fd584e67f9e53e6c631055cbbd82f080845", + ID: "2ea672a45c4c7b96e3c4b130b21a22af390a552fd0b3cff96420b4bda26568d470dc56e05e453823f64f2556a6e4460ad1d4d00eb2d8b8fc16fcb1be73e86522", ClientName: "Geth/v1.7.2-stable-1db4ecdc/darwin-amd64/go1.9", } db := test_config.NewTestDB(node)