From 867f92c4317a4026abdc131b1f3bee288c5d29ec Mon Sep 17 00:00:00 2001 From: Rob Mulholand Date: Mon, 28 Jan 2019 16:52:47 -0600 Subject: [PATCH] (VDB-298) Consume Pit contract storage diffs - Continuously parse storage diffs CSV data to read Pit contract state - Convert ilks in database to raw bytes32 value for use in generating storage keys dynamically - Persist storage diffs with block number and hash for validation --- cmd/backfillMakerLogs.go | 2 +- cmd/continuousLogSync.go | 2 +- cmd/parseStorageDiffs.go | 66 ++++ cmd/root.go | 4 + ...071_create_pit_contract_storage_tables.sql | 52 +++ db/schema.sql | 303 +++++++++++++++++- environments/staging.toml | 3 + .../shared/{watcher.go => event_watcher.go} | 10 +- ...{watcher_test.go => event_watcher_test.go} | 18 +- libraries/shared/shared_suite_test.go | 2 +- libraries/shared/storage_watcher.go | 74 +++++ libraries/shared/storage_watcher_test.go | 175 ++++++++++ .../repositories/header_repository.go | 2 +- .../repositories/header_repository_test.go | 2 + pkg/fakes/mock_header_repository.go | 16 +- pkg/fakes/mock_tailer.go | 29 ++ pkg/fs/reader.go | 3 +- pkg/fs/tail.go | 15 + pkg/transformers/bite/converter.go | 3 +- pkg/transformers/bite/converter_test.go | 2 +- .../cat_file/chop_lump/converter.go | 2 +- pkg/transformers/cat_file/flip/converter.go | 3 +- pkg/transformers/drip_drip/converter.go | 4 +- pkg/transformers/drip_file/ilk/converter.go | 2 +- .../factories/storage/storage_suite_test.go | 29 ++ .../factories/storage/transformer.go | 53 +++ .../factories/storage/transformer_test.go | 102 ++++++ pkg/transformers/frob/converter.go | 3 +- pkg/transformers/integration_tests/bite.go | 2 +- .../integration_tests/cat_file.go | 6 +- .../integration_tests/drip_drip.go | 2 +- pkg/transformers/integration_tests/frob.go | 2 +- .../integration_tests/pit_file_ilk.go | 4 +- .../integration_tests/vat_flux.go | 2 +- .../integration_tests/vat_fold.go | 2 +- .../integration_tests/vat_grab.go | 2 +- .../integration_tests/vat_init.go | 2 +- .../integration_tests/vat_slip.go | 2 +- .../integration_tests/vat_tune.go | 2 +- pkg/transformers/pit_file/ilk/converter.go | 2 +- .../shared/storage/transformer.go | 14 + pkg/transformers/shared/utilities.go | 5 + pkg/transformers/shared/utilities_test.go | 8 + .../maker/maker_storage_repository.go | 38 +++ .../maker/maker_storage_repository_test.go | 57 ++++ .../storage_diffs/maker/maker_suite_test.go | 29 ++ .../storage_diffs/maker/pit/mappings.go | 146 +++++++++ .../storage_diffs/maker/pit/mappings_test.go | 90 ++++++ .../storage_diffs/maker/pit/pit_suite_test.go | 13 + .../storage_diffs/maker/pit/repository.go | 79 +++++ .../maker/pit/repository_test.go | 172 ++++++++++ .../test_helpers/maker_storage_repository.go | 19 ++ pkg/transformers/storage_diffs/mappings.go | 39 +++ pkg/transformers/storage_diffs/repository.go | 27 ++ .../storage_diffs/shared/decoder.go | 42 +++ .../storage_diffs/shared/decoder_test.go | 58 ++++ .../storage_diffs/shared/errors.go | 61 ++++ pkg/transformers/storage_diffs/shared/row.go | 49 +++ .../storage_diffs/shared/row_test.go | 57 ++++ .../storage_diffs/shared/shared_suite_test.go | 29 ++ .../storage_diffs/shared/value.go | 31 ++ pkg/transformers/storage_transformers.go | 17 + pkg/transformers/test_data/bite.go | 2 +- pkg/transformers/test_data/cat_file.go | 6 +- pkg/transformers/test_data/drip_drip.go | 2 +- pkg/transformers/test_data/drip_file.go | 2 +- pkg/transformers/test_data/frob.go | 2 +- pkg/transformers/test_data/mocks/mappings.go | 22 ++ .../test_data/mocks/storage_repository.go | 26 ++ .../test_data/mocks/storage_transformer.go | 27 ++ pkg/transformers/test_data/pit_file.go | 4 +- pkg/transformers/test_data/vat_flux.go | 2 +- pkg/transformers/test_data/vat_fold.go | 2 +- pkg/transformers/test_data/vat_grab.go | 2 +- pkg/transformers/test_data/vat_init.go | 2 +- pkg/transformers/test_data/vat_slip.go | 2 +- pkg/transformers/test_data/vat_toll.go | 2 +- pkg/transformers/test_data/vat_tune.go | 2 +- pkg/transformers/vat_flux/converter.go | 3 +- pkg/transformers/vat_fold/converter.go | 3 +- pkg/transformers/vat_grab/converter.go | 3 +- pkg/transformers/vat_init/converter.go | 4 +- pkg/transformers/vat_slip/converter.go | 4 +- pkg/transformers/vat_toll/converter.go | 4 +- pkg/transformers/vat_tune/converter.go | 3 +- 85 files changed, 2143 insertions(+), 79 deletions(-) create mode 100644 cmd/parseStorageDiffs.go create mode 100644 db/migrations/00071_create_pit_contract_storage_tables.sql rename libraries/shared/{watcher.go => event_watcher.go} (93%) rename libraries/shared/{watcher_test.go => event_watcher_test.go} (94%) create mode 100644 libraries/shared/storage_watcher.go create mode 100644 libraries/shared/storage_watcher_test.go create mode 100644 pkg/fakes/mock_tailer.go create mode 100644 pkg/fs/tail.go create mode 100644 pkg/transformers/factories/storage/storage_suite_test.go create mode 100644 pkg/transformers/factories/storage/transformer.go create mode 100644 pkg/transformers/factories/storage/transformer_test.go create mode 100644 pkg/transformers/shared/storage/transformer.go create mode 100644 pkg/transformers/storage_diffs/maker/maker_storage_repository.go create mode 100644 pkg/transformers/storage_diffs/maker/maker_storage_repository_test.go create mode 100644 pkg/transformers/storage_diffs/maker/maker_suite_test.go create mode 100644 pkg/transformers/storage_diffs/maker/pit/mappings.go create mode 100644 pkg/transformers/storage_diffs/maker/pit/mappings_test.go create mode 100644 pkg/transformers/storage_diffs/maker/pit/pit_suite_test.go create mode 100644 pkg/transformers/storage_diffs/maker/pit/repository.go create mode 100644 pkg/transformers/storage_diffs/maker/pit/repository_test.go create mode 100644 pkg/transformers/storage_diffs/maker/test_helpers/maker_storage_repository.go create mode 100644 pkg/transformers/storage_diffs/mappings.go create mode 100644 pkg/transformers/storage_diffs/repository.go create mode 100644 pkg/transformers/storage_diffs/shared/decoder.go create mode 100644 pkg/transformers/storage_diffs/shared/decoder_test.go create mode 100644 pkg/transformers/storage_diffs/shared/errors.go create mode 100644 pkg/transformers/storage_diffs/shared/row.go create mode 100644 pkg/transformers/storage_diffs/shared/row_test.go create mode 100644 pkg/transformers/storage_diffs/shared/shared_suite_test.go create mode 100644 pkg/transformers/storage_diffs/shared/value.go create mode 100644 pkg/transformers/storage_transformers.go create mode 100644 pkg/transformers/test_data/mocks/mappings.go create mode 100644 pkg/transformers/test_data/mocks/storage_repository.go create mode 100644 pkg/transformers/test_data/mocks/storage_transformer.go diff --git a/cmd/backfillMakerLogs.go b/cmd/backfillMakerLogs.go index 5bbd65d0..73ce9d12 100644 --- a/cmd/backfillMakerLogs.go +++ b/cmd/backfillMakerLogs.go @@ -47,7 +47,7 @@ func backfillMakerLogs() { log.Fatal("Failed to initialize database.") } - watcher := shared.NewWatcher(db, blockChain) + watcher := shared.NewEventWatcher(db, blockChain) watcher.AddTransformers(transformers.TransformerInitializers()) err = watcher.Execute() diff --git a/cmd/continuousLogSync.go b/cmd/continuousLogSync.go index 59b36bc1..240915fd 100644 --- a/cmd/continuousLogSync.go +++ b/cmd/continuousLogSync.go @@ -62,7 +62,7 @@ func syncMakerLogs() { initializers := getTransformerInitializers(transformerNames) - watcher := shared.NewWatcher(db, blockChain) + watcher := shared.NewEventWatcher(db, blockChain) watcher.AddTransformers(initializers) for range ticker.C { diff --git a/cmd/parseStorageDiffs.go b/cmd/parseStorageDiffs.go new file mode 100644 index 00000000..753603f1 --- /dev/null +++ b/cmd/parseStorageDiffs.go @@ -0,0 +1,66 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/vulcanize/vulcanizedb/libraries/shared" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/fs" + "github.com/vulcanize/vulcanizedb/pkg/transformers" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/storage" + "log" +) + +// parseStorageDiffsCmd represents the parseStorageDiffs command +var parseStorageDiffsCmd = &cobra.Command{ + Use: "parseStorageDiffs", + Short: "Continuously ingest storage diffs from a CSV file", + Long: `Read storage diffs out of a CSV file that is constantly receiving +new rows from an Ethereum node. For example: + +./vulcanizedb parseStorageDiffs --config=environments/staging.toml + +Note that the path to your storage diffs must be configured in your toml +file under storageDiffsPath.`, + Run: func(cmd *cobra.Command, args []string) { + parseStorageDiffs() + }, +} + +func init() { + rootCmd.AddCommand(parseStorageDiffsCmd) +} + +func parseStorageDiffs() { + blockChain := getBlockChain() + db, err := postgres.NewDB(databaseConfig, blockChain.Node()) + if err != nil { + log.Fatal("Failed to initialize database: ", err) + } + + tailer := fs.FileTailer{Path: storageDiffsPath} + + // TODO: configure transformers + watcher := shared.NewStorageWatcher(tailer, db) + watcher.AddTransformers([]storage.TransformerInitializer{transformers.GetPitStorageTransformer().NewTransformer}) + + err = watcher.Execute() + if err != nil { + log.Fatal(err) + } +} diff --git a/cmd/root.go b/cmd/root.go index 458a5158..c0d7e7ef 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -40,6 +40,7 @@ var ( ipc string levelDbPath string startingBlockNumber int64 + storageDiffsPath string syncAll bool endingBlockNumber int64 ) @@ -64,6 +65,7 @@ func Execute() { func database(cmd *cobra.Command, args []string) { ipc = viper.GetString("client.ipcpath") levelDbPath = viper.GetString("client.leveldbpath") + storageDiffsPath = viper.GetString("filesystem.storageDiffsPath") databaseConfig = config.Database{ Name: viper.GetString("database.name"), Hostname: viper.GetString("database.hostname"), @@ -86,6 +88,7 @@ func init() { rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file") rootCmd.PersistentFlags().String("client-levelDbPath", "", "location of levelDb chaindata") rootCmd.PersistentFlags().String("datadog-name", "vulcanize-test", "datadog service name") + rootCmd.PersistentFlags().String("filesystem-storageDiffsPath", "", "location of storage diffs csv file") viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name")) viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port")) @@ -95,6 +98,7 @@ func init() { viper.BindPFlag("client.ipcPath", rootCmd.PersistentFlags().Lookup("client-ipcPath")) viper.BindPFlag("client.levelDbPath", rootCmd.PersistentFlags().Lookup("client-levelDbPath")) viper.BindPFlag("datadog.name", rootCmd.PersistentFlags().Lookup("datadog-name")) + viper.BindPFlag("filesystem.storageDiffsPath", rootCmd.PersistentFlags().Lookup("filesystem-storageDiffsPath")) } func initConfig() { diff --git a/db/migrations/00071_create_pit_contract_storage_tables.sql b/db/migrations/00071_create_pit_contract_storage_tables.sql new file mode 100644 index 00000000..856b0da0 --- /dev/null +++ b/db/migrations/00071_create_pit_contract_storage_tables.sql @@ -0,0 +1,52 @@ +-- +goose Up +CREATE TABLE maker.pit_drip ( + id SERIAL PRIMARY KEY, + block_number BIGINT, + block_hash TEXT, + drip TEXT +); + +CREATE TABLE maker.pit_ilk_spot ( + id SERIAL PRIMARY KEY, + block_number BIGINT, + block_hash TEXT, + ilk TEXT, + spot NUMERIC NOT NULL +); + +CREATE TABLE maker.pit_ilk_line ( + id SERIAL PRIMARY KEY, + block_number BIGINT, + block_hash TEXT, + ilk TEXT, + line NUMERIC NOT NULL +); + +CREATE TABLE maker.pit_line ( + id SERIAL PRIMARY KEY, + block_number BIGINT, + block_hash TEXT, + line NUMERIC NOT NULL +); + +CREATE TABLE maker.pit_live ( + id SERIAL PRIMARY KEY, + block_number BIGINT, + block_hash TEXT, + live NUMERIC NOT NULL +); + +CREATE TABLE maker.pit_vat ( + id SERIAL PRIMARY KEY, + block_number BIGINT, + block_hash TEXT, + vat TEXT +); + +-- +goose Down +DROP TABLE maker.pit_drip; +DROP TABLE maker.pit_ilk_spot; +DROP TABLE maker.pit_ilk_line; +DROP TABLE maker.pit_line; +DROP TABLE maker.pit_live; +DROP TABLE maker.pit_vat; \ No newline at end of file diff --git a/db/schema.sql b/db/schema.sql index a7871549..2e0a6df6 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -3,7 +3,7 @@ -- -- Dumped from database version 10.5 --- Dumped by pg_dump version 11.1 +-- Dumped by pg_dump version 10.5 SET statement_timeout = 0; SET lock_timeout = 0; @@ -22,6 +22,20 @@ SET row_security = off; CREATE SCHEMA maker; +-- +-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; + + -- -- Name: notify_pricefeed(); Type: FUNCTION; Schema: public; Owner: - -- @@ -559,6 +573,38 @@ CREATE SEQUENCE maker.frob_id_seq ALTER SEQUENCE maker.frob_id_seq OWNED BY maker.frob.id; +-- +-- Name: pit_drip; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.pit_drip ( + id integer NOT NULL, + block_number bigint, + block_hash text, + drip text +); + + +-- +-- Name: pit_drip_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.pit_drip_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pit_drip_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.pit_drip_id_seq OWNED BY maker.pit_drip.id; + + -- -- Name: pit_file_debt_ceiling; Type: TABLE; Schema: maker; Owner: - -- @@ -630,6 +676,168 @@ CREATE SEQUENCE maker.pit_file_ilk_id_seq ALTER SEQUENCE maker.pit_file_ilk_id_seq OWNED BY maker.pit_file_ilk.id; +-- +-- Name: pit_ilk_line; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.pit_ilk_line ( + id integer NOT NULL, + block_number bigint, + block_hash text, + ilk text, + line numeric NOT NULL +); + + +-- +-- Name: pit_ilk_line_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.pit_ilk_line_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pit_ilk_line_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.pit_ilk_line_id_seq OWNED BY maker.pit_ilk_line.id; + + +-- +-- Name: pit_ilk_spot; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.pit_ilk_spot ( + id integer NOT NULL, + block_number bigint, + block_hash text, + ilk text, + spot numeric NOT NULL +); + + +-- +-- Name: pit_ilk_spot_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.pit_ilk_spot_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pit_ilk_spot_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.pit_ilk_spot_id_seq OWNED BY maker.pit_ilk_spot.id; + + +-- +-- Name: pit_line; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.pit_line ( + id integer NOT NULL, + block_number bigint, + block_hash text, + line numeric NOT NULL +); + + +-- +-- Name: pit_line_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.pit_line_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pit_line_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.pit_line_id_seq OWNED BY maker.pit_line.id; + + +-- +-- Name: pit_live; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.pit_live ( + id integer NOT NULL, + block_number bigint, + block_hash text, + live numeric NOT NULL +); + + +-- +-- Name: pit_live_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.pit_live_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pit_live_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.pit_live_id_seq OWNED BY maker.pit_live.id; + + +-- +-- Name: pit_vat; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.pit_vat ( + id integer NOT NULL, + block_number bigint, + block_hash text, + vat text +); + + +-- +-- Name: pit_vat_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.pit_vat_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: pit_vat_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.pit_vat_id_seq OWNED BY maker.pit_vat.id; + + -- -- Name: price_feeds; Type: TABLE; Schema: maker; Owner: - -- @@ -1618,6 +1826,13 @@ ALTER TABLE ONLY maker.flop_kick ALTER COLUMN id SET DEFAULT nextval('maker.flop ALTER TABLE ONLY maker.frob ALTER COLUMN id SET DEFAULT nextval('maker.frob_id_seq'::regclass); +-- +-- Name: pit_drip id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_drip ALTER COLUMN id SET DEFAULT nextval('maker.pit_drip_id_seq'::regclass); + + -- -- Name: pit_file_debt_ceiling id; Type: DEFAULT; Schema: maker; Owner: - -- @@ -1632,6 +1847,41 @@ ALTER TABLE ONLY maker.pit_file_debt_ceiling ALTER COLUMN id SET DEFAULT nextval ALTER TABLE ONLY maker.pit_file_ilk ALTER COLUMN id SET DEFAULT nextval('maker.pit_file_ilk_id_seq'::regclass); +-- +-- Name: pit_ilk_line id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_ilk_line ALTER COLUMN id SET DEFAULT nextval('maker.pit_ilk_line_id_seq'::regclass); + + +-- +-- Name: pit_ilk_spot id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_ilk_spot ALTER COLUMN id SET DEFAULT nextval('maker.pit_ilk_spot_id_seq'::regclass); + + +-- +-- Name: pit_line id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_line ALTER COLUMN id SET DEFAULT nextval('maker.pit_line_id_seq'::regclass); + + +-- +-- Name: pit_live id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_live ALTER COLUMN id SET DEFAULT nextval('maker.pit_live_id_seq'::regclass); + + +-- +-- Name: pit_vat id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_vat ALTER COLUMN id SET DEFAULT nextval('maker.pit_vat_id_seq'::regclass); + + -- -- Name: price_feeds id; Type: DEFAULT; Schema: maker; Owner: - -- @@ -2017,6 +2267,14 @@ ALTER TABLE ONLY maker.frob ADD CONSTRAINT frob_pkey PRIMARY KEY (id); +-- +-- Name: pit_drip pit_drip_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_drip + ADD CONSTRAINT pit_drip_pkey PRIMARY KEY (id); + + -- -- Name: pit_file_debt_ceiling pit_file_debt_ceiling_header_id_tx_idx_log_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - -- @@ -2049,6 +2307,46 @@ ALTER TABLE ONLY maker.pit_file_ilk ADD CONSTRAINT pit_file_ilk_pkey PRIMARY KEY (id); +-- +-- Name: pit_ilk_line pit_ilk_line_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_ilk_line + ADD CONSTRAINT pit_ilk_line_pkey PRIMARY KEY (id); + + +-- +-- Name: pit_ilk_spot pit_ilk_spot_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_ilk_spot + ADD CONSTRAINT pit_ilk_spot_pkey PRIMARY KEY (id); + + +-- +-- Name: pit_line pit_line_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_line + ADD CONSTRAINT pit_line_pkey PRIMARY KEY (id); + + +-- +-- Name: pit_live pit_live_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_live + ADD CONSTRAINT pit_live_pkey PRIMARY KEY (id); + + +-- +-- Name: pit_vat pit_vat_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.pit_vat + ADD CONSTRAINT pit_vat_pkey PRIMARY KEY (id); + + -- -- Name: price_feeds price_feeds_header_id_medianizer_address_tx_idx_log_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - -- @@ -2677,4 +2975,5 @@ ALTER TABLE ONLY public.logs -- -- PostgreSQL database dump complete --- \ No newline at end of file +-- + diff --git a/environments/staging.toml b/environments/staging.toml index 51569120..8954a483 100644 --- a/environments/staging.toml +++ b/environments/staging.toml @@ -11,6 +11,9 @@ [datadog] name = "maker_vdb_staging" +[filesystem] + storageDiffsPath = "INSERT-PATH-TO-STORAGE-DIFFS" + [contract] [contract.address] cat = "0x2f34f22a00ee4b7a8f8bbc4eaee1658774c624e0" diff --git a/libraries/shared/watcher.go b/libraries/shared/event_watcher.go similarity index 93% rename from libraries/shared/watcher.go rename to libraries/shared/event_watcher.go index eddf11c9..d17288be 100644 --- a/libraries/shared/watcher.go +++ b/libraries/shared/event_watcher.go @@ -25,7 +25,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" ) -type Watcher struct { +type EventWatcher struct { Transformers []shared.Transformer DB *postgres.DB Fetcher shared.LogFetcher @@ -35,10 +35,10 @@ type Watcher struct { StartingBlock *int64 } -func NewWatcher(db *postgres.DB, bc core.BlockChain) Watcher { +func NewEventWatcher(db *postgres.DB, bc core.BlockChain) EventWatcher { chunker := shared.NewLogChunker() fetcher := shared.NewFetcher(bc) - return Watcher{ + return EventWatcher{ DB: db, Fetcher: fetcher, Chunker: chunker, @@ -46,7 +46,7 @@ func NewWatcher(db *postgres.DB, bc core.BlockChain) Watcher { } // Adds transformers to the watcher and updates the chunker, so that it will consider the new transformers. -func (watcher *Watcher) AddTransformers(initializers []shared.TransformerInitializer) { +func (watcher *EventWatcher) AddTransformers(initializers []shared.TransformerInitializer) { var contractAddresses []common.Address var topic0s []common.Hash var configs []shared.TransformerConfig @@ -74,7 +74,7 @@ func (watcher *Watcher) AddTransformers(initializers []shared.TransformerInitial watcher.Chunker.AddConfigs(configs) } -func (watcher *Watcher) Execute() error { +func (watcher *EventWatcher) Execute() error { if watcher.Transformers == nil { return fmt.Errorf("No transformers added to watcher") } diff --git a/libraries/shared/watcher_test.go b/libraries/shared/event_watcher_test.go similarity index 94% rename from libraries/shared/watcher_test.go rename to libraries/shared/event_watcher_test.go index 94e8fb33..4a86e5af 100644 --- a/libraries/shared/watcher_test.go +++ b/libraries/shared/event_watcher_test.go @@ -35,12 +35,12 @@ import ( "github.com/vulcanize/vulcanizedb/test_config" ) -var _ = Describe("Watcher", func() { +var _ = Describe("EventWatcher", func() { It("initialises correctly", func() { db := test_config.NewTestDB(core.Node{ID: "testNode"}) bc := fakes.NewMockBlockChain() - watcher := shared.NewWatcher(db, bc) + watcher := shared.NewEventWatcher(db, bc) Expect(watcher.DB).To(Equal(db)) Expect(watcher.Fetcher).NotTo(BeNil()) @@ -48,7 +48,7 @@ var _ = Describe("Watcher", func() { }) It("adds transformers", func() { - watcher := shared.NewWatcher(nil, nil) + watcher := shared.NewEventWatcher(nil, nil) fakeTransformer := &mocks.MockTransformer{} fakeTransformer.SetTransformerConfig(mocks.FakeTransformerConfig) watcher.AddTransformers([]shared2.TransformerInitializer{fakeTransformer.FakeTransformerInitializer}) @@ -60,7 +60,7 @@ var _ = Describe("Watcher", func() { }) It("adds transformers from multiple sources", func() { - watcher := shared.NewWatcher(nil, nil) + watcher := shared.NewEventWatcher(nil, nil) fakeTransformer1 := &mocks.MockTransformer{} fakeTransformer1.SetTransformerConfig(mocks.FakeTransformerConfig) @@ -84,7 +84,7 @@ var _ = Describe("Watcher", func() { fakeTransformer2 := &mocks.MockTransformer{} fakeTransformer2.SetTransformerConfig(shared2.TransformerConfig{StartingBlockNumber: 3}) - watcher := shared.NewWatcher(nil, nil) + watcher := shared.NewEventWatcher(nil, nil) watcher.AddTransformers([]shared2.TransformerInitializer{ fakeTransformer1.FakeTransformerInitializer, fakeTransformer2.FakeTransformerInitializer, @@ -94,7 +94,7 @@ var _ = Describe("Watcher", func() { }) It("returns an error when run without transformers", func() { - watcher := shared.NewWatcher(nil, nil) + watcher := shared.NewEventWatcher(nil, nil) err := watcher.Execute() Expect(err).To(MatchError("No transformers added to watcher")) }) @@ -102,7 +102,7 @@ var _ = Describe("Watcher", func() { Describe("with missing headers", func() { var ( db *postgres.DB - watcher shared.Watcher + watcher shared.EventWatcher mockBlockChain fakes.MockBlockChain headerRepository repositories.HeaderRepository repository mocks.MockWatcherRepository @@ -117,7 +117,7 @@ var _ = Describe("Watcher", func() { Expect(err).NotTo(HaveOccurred()) repository = mocks.MockWatcherRepository{} - watcher = shared.NewWatcher(db, &mockBlockChain) + watcher = shared.NewEventWatcher(db, &mockBlockChain) }) It("executes each transformer", func() { @@ -163,7 +163,7 @@ var _ = Describe("Watcher", func() { mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{logA, logB}) repository.SetMissingHeaders([]core.Header{fakes.FakeHeader}) - watcher = shared.NewWatcher(db, &mockBlockChain) + watcher = shared.NewEventWatcher(db, &mockBlockChain) watcher.AddTransformers([]shared2.TransformerInitializer{ transformerA.FakeTransformerInitializer, transformerB.FakeTransformerInitializer}) diff --git a/libraries/shared/shared_suite_test.go b/libraries/shared/shared_suite_test.go index 8d079e5d..3e3b5fcc 100644 --- a/libraries/shared/shared_suite_test.go +++ b/libraries/shared/shared_suite_test.go @@ -17,8 +17,8 @@ package shared_test import ( + log "github.com/sirupsen/logrus" "io/ioutil" - "log" "testing" . "github.com/onsi/ginkgo" diff --git a/libraries/shared/storage_watcher.go b/libraries/shared/storage_watcher.go new file mode 100644 index 00000000..50e042b2 --- /dev/null +++ b/libraries/shared/storage_watcher.go @@ -0,0 +1,74 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/sirupsen/logrus" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/storage" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" + "strings" + + "github.com/vulcanize/vulcanizedb/pkg/fs" +) + +type StorageWatcher struct { + db *postgres.DB + tailer fs.Tailer + Transformers map[common.Address]storage.Transformer +} + +func NewStorageWatcher(tailer fs.Tailer, db *postgres.DB) StorageWatcher { + transformers := make(map[common.Address]storage.Transformer) + return StorageWatcher{ + db: db, + tailer: tailer, + Transformers: transformers, + } +} + +func (watcher StorageWatcher) AddTransformers(initializers []storage.TransformerInitializer) { + for _, initializer := range initializers { + transformer := initializer(watcher.db) + watcher.Transformers[transformer.ContractAddress()] = transformer + } +} + +func (watcher StorageWatcher) Execute() error { + t, tailErr := watcher.tailer.Tail() + if tailErr != nil { + return tailErr + } + for line := range t.Lines { + row, parseErr := shared.FromStrings(strings.Split(line.Text, ",")) + if parseErr != nil { + return parseErr + } + transformer, ok := watcher.Transformers[row.Contract] + if !ok { + logrus.Warn(shared.ErrContractNotFound{Contract: row.Contract.Hex()}.Error()) + continue + } + executeErr := transformer.Execute(row) + if executeErr != nil { + logrus.Warn(executeErr.Error()) + continue + } + } + return nil +} diff --git a/libraries/shared/storage_watcher_test.go b/libraries/shared/storage_watcher_test.go new file mode 100644 index 00000000..3f7b13d0 --- /dev/null +++ b/libraries/shared/storage_watcher_test.go @@ -0,0 +1,175 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared_test + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/hpcloud/tail" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" + + "github.com/vulcanize/vulcanizedb/libraries/shared" + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/storage" + shared2 "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("Storage Watcher", func() { + It("adds transformers", func() { + fakeAddress := common.HexToAddress("0x12345") + fakeTransformer := &mocks.MockStorageTransformer{Address: fakeAddress} + watcher := shared.NewStorageWatcher(&fakes.MockTailer{}, test_config.NewTestDB(core.Node{})) + + watcher.AddTransformers([]storage.TransformerInitializer{fakeTransformer.FakeTransformerInitializer}) + + Expect(watcher.Transformers[fakeAddress]).To(Equal(fakeTransformer)) + }) + + It("reads the tail of the storage diffs file", func() { + mockTailer := fakes.NewMockTailer() + watcher := shared.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{})) + + assert(func(err error) { + Expect(err).To(BeNil()) + Expect(mockTailer.TailCalled).To(BeTrue()) + }, watcher, mockTailer, []*tail.Line{}) + }) + + It("returns error if row parsing fails", func() { + mockTailer := fakes.NewMockTailer() + watcher := shared.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{})) + line := &tail.Line{Text: "oops"} + + assert(func(err error) { + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(shared2.ErrRowMalformed{Length: 1})) + }, watcher, mockTailer, []*tail.Line{line}) + }) + + It("logs error if no transformer can parse storage row", func() { + mockTailer := fakes.NewMockTailer() + line := &tail.Line{ + Text: "12345,block_hash,123,storage_key,storage_value", + Time: time.Time{}, + Err: nil, + } + watcher := shared.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{})) + tempFile, err := ioutil.TempFile("", "log") + defer os.Remove(tempFile.Name()) + Expect(err).NotTo(HaveOccurred()) + logrus.SetOutput(tempFile) + + assert(func(err error) { + Expect(err).NotTo(HaveOccurred()) + logContent, readErr := ioutil.ReadFile(tempFile.Name()) + Expect(readErr).NotTo(HaveOccurred()) + Expect(string(logContent)).To(ContainSubstring(shared2.ErrContractNotFound{Contract: common.HexToAddress("0x12345").Hex()}.Error())) + }, watcher, mockTailer, []*tail.Line{line}) + }) + + It("executes transformer with storage row", func() { + address := []byte{1, 2, 3} + blockHash := []byte{4, 5, 6} + blockHeight := int64(789) + storageKey := []byte{9, 8, 7} + storageValue := []byte{6, 5, 4} + mockTailer := fakes.NewMockTailer() + line := &tail.Line{ + Text: fmt.Sprintf("%s,%s,%d,%s,%s", common.Bytes2Hex(address), common.Bytes2Hex(blockHash), blockHeight, common.Bytes2Hex(storageKey), common.Bytes2Hex(storageValue)), + Time: time.Time{}, + Err: nil, + } + watcher := shared.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{})) + fakeTransformer := &mocks.MockStorageTransformer{Address: common.BytesToAddress(address)} + watcher.AddTransformers([]storage.TransformerInitializer{fakeTransformer.FakeTransformerInitializer}) + + assert(func(err error) { + Expect(err).To(BeNil()) + expectedRow, err := shared2.FromStrings(strings.Split(line.Text, ",")) + Expect(err).NotTo(HaveOccurred()) + Expect(fakeTransformer.PassedRow).To(Equal(expectedRow)) + }, watcher, mockTailer, []*tail.Line{line}) + }) + + It("logs error if executing transformer fails", func() { + address := []byte{1, 2, 3} + blockHash := []byte{4, 5, 6} + blockHeight := int64(789) + storageKey := []byte{9, 8, 7} + storageValue := []byte{6, 5, 4} + mockTailer := fakes.NewMockTailer() + line := &tail.Line{ + Text: fmt.Sprintf("%s,%s,%d,%s,%s", common.Bytes2Hex(address), common.Bytes2Hex(blockHash), blockHeight, common.Bytes2Hex(storageKey), common.Bytes2Hex(storageValue)), + Time: time.Time{}, + Err: nil, + } + watcher := shared.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{})) + executionError := errors.New("storage watcher failed attempting to execute transformer") + fakeTransformer := &mocks.MockStorageTransformer{Address: common.BytesToAddress(address), ExecuteErr: executionError} + watcher.AddTransformers([]storage.TransformerInitializer{fakeTransformer.FakeTransformerInitializer}) + tempFile, err := ioutil.TempFile("", "log") + defer os.Remove(tempFile.Name()) + Expect(err).NotTo(HaveOccurred()) + logrus.SetOutput(tempFile) + + assert(func(err error) { + Expect(err).NotTo(HaveOccurred()) + logContent, readErr := ioutil.ReadFile(tempFile.Name()) + Expect(readErr).NotTo(HaveOccurred()) + Expect(string(logContent)).To(ContainSubstring(executionError.Error())) + }, watcher, mockTailer, []*tail.Line{line}) + }) +}) + +func assert(assertion func(err error), watcher shared.StorageWatcher, mockTailer *fakes.MockTailer, lines []*tail.Line) { + errs := make(chan error, 1) + done := make(chan bool, 1) + go execute(watcher, mockTailer, errs, done) + for _, line := range lines { + mockTailer.Lines <- line + } + close(mockTailer.Lines) + + select { + case err := <-errs: + assertion(err) + break + case <-done: + assertion(nil) + break + } +} + +func execute(watcher shared.StorageWatcher, tailer *fakes.MockTailer, errs chan error, done chan bool) { + err := watcher.Execute() + if err != nil { + errs <- err + } else { + done <- true + } +} diff --git a/pkg/datastore/postgres/repositories/header_repository.go b/pkg/datastore/postgres/repositories/header_repository.go index 591496e5..30e4f6e4 100644 --- a/pkg/datastore/postgres/repositories/header_repository.go +++ b/pkg/datastore/postgres/repositories/header_repository.go @@ -52,7 +52,7 @@ func (repository HeaderRepository) CreateOrUpdateHeader(header core.Header) (int func (repository HeaderRepository) GetHeader(blockNumber int64) (core.Header, error) { var header core.Header - err := repository.database.Get(&header, `SELECT block_number, hash, raw 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) return header, err } diff --git a/pkg/datastore/postgres/repositories/header_repository_test.go b/pkg/datastore/postgres/repositories/header_repository_test.go index 8bcd1158..fbcd50bb 100644 --- a/pkg/datastore/postgres/repositories/header_repository_test.go +++ b/pkg/datastore/postgres/repositories/header_repository_test.go @@ -188,9 +188,11 @@ var _ = Describe("Block header repository", func() { dbHeader, err := repo.GetHeader(header.BlockNumber) Expect(err).NotTo(HaveOccurred()) + Expect(dbHeader.Id).NotTo(BeZero()) Expect(dbHeader.BlockNumber).To(Equal(header.BlockNumber)) Expect(dbHeader.Hash).To(Equal(header.Hash)) Expect(dbHeader.Raw).To(MatchJSON(header.Raw)) + Expect(dbHeader.Timestamp).To(Equal(header.Timestamp)) }) It("does not return header for a different node fingerprint", func() { diff --git a/pkg/fakes/mock_header_repository.go b/pkg/fakes/mock_header_repository.go index 1a9bf630..a8a4e6ba 100644 --- a/pkg/fakes/mock_header_repository.go +++ b/pkg/fakes/mock_header_repository.go @@ -27,8 +27,11 @@ type MockHeaderRepository struct { createOrUpdateHeaderErr error createOrUpdateHeaderPassedBlockNumbers []int64 createOrUpdateHeaderReturnID int64 + getHeaderError error + getHeaderReturnBlockHash string missingBlockNumbers []int64 headerExists bool + GetHeaderPassedBlockNumber int64 } func NewMockHeaderRepository() *MockHeaderRepository { @@ -53,8 +56,9 @@ func (repository *MockHeaderRepository) CreateOrUpdateHeader(header core.Header) return repository.createOrUpdateHeaderReturnID, repository.createOrUpdateHeaderErr } -func (*MockHeaderRepository) GetHeader(blockNumber int64) (core.Header, error) { - return core.Header{BlockNumber: blockNumber}, nil +func (repository *MockHeaderRepository) GetHeader(blockNumber int64) (core.Header, error) { + repository.GetHeaderPassedBlockNumber = blockNumber + return core.Header{BlockNumber: blockNumber, Hash: repository.getHeaderReturnBlockHash}, repository.getHeaderError } func (repository *MockHeaderRepository) MissingBlockNumbers(startingBlockNumber, endingBlockNumber int64, nodeID string) ([]int64, error) { @@ -69,6 +73,14 @@ func (repository *MockHeaderRepository) SetHeaderExists(headerExists bool) { repository.headerExists = headerExists } +func (repository *MockHeaderRepository) SetGetHeaderError(err error) { + repository.getHeaderError = err +} + +func (repository *MockHeaderRepository) SetGetHeaderReturnBlockHash(hash string) { + repository.getHeaderReturnBlockHash = hash +} + func (repository *MockHeaderRepository) AssertCreateOrUpdateHeaderCallCountAndPassedBlockNumbers(times int, blockNumbers []int64) { Expect(repository.createOrUpdateHeaderCallCount).To(Equal(times)) Expect(repository.createOrUpdateHeaderPassedBlockNumbers).To(Equal(blockNumbers)) diff --git a/pkg/fakes/mock_tailer.go b/pkg/fakes/mock_tailer.go new file mode 100644 index 00000000..888688d6 --- /dev/null +++ b/pkg/fakes/mock_tailer.go @@ -0,0 +1,29 @@ +package fakes + +import ( + "github.com/hpcloud/tail" + "gopkg.in/tomb.v1" +) + +type MockTailer struct { + Lines chan *tail.Line + TailCalled bool +} + +func NewMockTailer() *MockTailer { + return &MockTailer{ + Lines: make(chan *tail.Line, 1), + TailCalled: false, + } +} + +func (mock *MockTailer) Tail() (*tail.Tail, error) { + mock.TailCalled = true + fakeTail := &tail.Tail{ + Filename: "", + Lines: mock.Lines, + Config: tail.Config{}, + Tomb: tomb.Tomb{}, + } + return fakeTail, nil +} diff --git a/pkg/fs/reader.go b/pkg/fs/reader.go index fe9df6ac..8e886a7d 100644 --- a/pkg/fs/reader.go +++ b/pkg/fs/reader.go @@ -22,8 +22,7 @@ type Reader interface { Read(path string) ([]byte, error) } -type FsReader struct { -} +type FsReader struct{} func (FsReader) Read(path string) ([]byte, error) { return ioutil.ReadFile(path) diff --git a/pkg/fs/tail.go b/pkg/fs/tail.go new file mode 100644 index 00000000..7c84c692 --- /dev/null +++ b/pkg/fs/tail.go @@ -0,0 +1,15 @@ +package fs + +import "github.com/hpcloud/tail" + +type Tailer interface { + Tail() (*tail.Tail, error) +} + +type FileTailer struct { + Path string +} + +func (tailer FileTailer) Tail() (*tail.Tail, error) { + return tail.TailFile(tailer.Path, tail.Config{Follow: true}) +} diff --git a/pkg/transformers/bite/converter.go b/pkg/transformers/bite/converter.go index 6d5b7e2f..556a27dd 100644 --- a/pkg/transformers/bite/converter.go +++ b/pkg/transformers/bite/converter.go @@ -17,7 +17,6 @@ package bite import ( - "bytes" "encoding/json" "fmt" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -64,7 +63,7 @@ func (converter BiteConverter) ToModels(entities []interface{}) ([]interface{}, return nil, fmt.Errorf("entity of type %T, not %T", entity, BiteEntity{}) } - ilk := string(bytes.Trim(biteEntity.Ilk[:], "\x00")) + ilk := common.Bytes2Hex(biteEntity.Ilk[:]) urn := common.BytesToAddress(biteEntity.Urn[:]).String() ink := biteEntity.Ink art := biteEntity.Art diff --git a/pkg/transformers/bite/converter_test.go b/pkg/transformers/bite/converter_test.go index 4217defc..0bdbd0db 100644 --- a/pkg/transformers/bite/converter_test.go +++ b/pkg/transformers/bite/converter_test.go @@ -70,7 +70,7 @@ var _ = Describe("Bite Converter", func() { emptyLog, err := json.Marshal(types.Log{}) Expect(err).NotTo(HaveOccurred()) expectedModel := bite.BiteModel{ - Ilk: "", + Ilk: "0000000000000000000000000000000000000000000000000000000000000000", Urn: "0x0000000000000000000000000000000000000000", Ink: "", Art: "", diff --git a/pkg/transformers/cat_file/chop_lump/converter.go b/pkg/transformers/cat_file/chop_lump/converter.go index ca64a0ec..753dd76c 100644 --- a/pkg/transformers/cat_file/chop_lump/converter.go +++ b/pkg/transformers/cat_file/chop_lump/converter.go @@ -40,7 +40,7 @@ func (CatFileChopLumpConverter) ToModels(ethLogs []types.Log) ([]interface{}, er if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[2].Bytes()) what := string(bytes.Trim(ethLog.Topics[3].Bytes(), "\x00")) dataBytes := ethLog.Data[len(ethLog.Data)-constants.DataItemLength:] data := big.NewInt(0).SetBytes(dataBytes).String() diff --git a/pkg/transformers/cat_file/flip/converter.go b/pkg/transformers/cat_file/flip/converter.go index efa8a517..eb748972 100644 --- a/pkg/transformers/cat_file/flip/converter.go +++ b/pkg/transformers/cat_file/flip/converter.go @@ -22,6 +22,7 @@ import ( "errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/constants" ) @@ -34,7 +35,7 @@ func (CatFileFlipConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[2].Bytes()) what := string(bytes.Trim(ethLog.Topics[3].Bytes(), "\x00")) flipBytes := ethLog.Data[len(ethLog.Data)-constants.DataItemLength:] flip := common.BytesToAddress(flipBytes).String() diff --git a/pkg/transformers/drip_drip/converter.go b/pkg/transformers/drip_drip/converter.go index a50d9126..534242e1 100644 --- a/pkg/transformers/drip_drip/converter.go +++ b/pkg/transformers/drip_drip/converter.go @@ -17,10 +17,10 @@ package drip_drip import ( - "bytes" "encoding/json" "errors" "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" ) type DripDripConverter struct{} @@ -32,7 +32,7 @@ func (DripDripConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[2].Bytes()) raw, err := json.Marshal(ethLog) if err != nil { return nil, err diff --git a/pkg/transformers/drip_file/ilk/converter.go b/pkg/transformers/drip_file/ilk/converter.go index adfb2f3d..af155e5b 100644 --- a/pkg/transformers/drip_file/ilk/converter.go +++ b/pkg/transformers/drip_file/ilk/converter.go @@ -38,7 +38,7 @@ func (DripFileIlkConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[2].Bytes()) vow := string(bytes.Trim(ethLog.Topics[3].Bytes(), "\x00")) taxBytes := ethLog.Data[len(ethLog.Data)-constants.DataItemLength:] tax := shared.ConvertToRay(big.NewInt(0).SetBytes(taxBytes).String()) diff --git a/pkg/transformers/factories/storage/storage_suite_test.go b/pkg/transformers/factories/storage/storage_suite_test.go new file mode 100644 index 00000000..b8e3232e --- /dev/null +++ b/pkg/transformers/factories/storage/storage_suite_test.go @@ -0,0 +1,29 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package storage_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestStorage(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Storage Suite") +} diff --git a/pkg/transformers/factories/storage/transformer.go b/pkg/transformers/factories/storage/transformer.go new file mode 100644 index 00000000..68baf5f7 --- /dev/null +++ b/pkg/transformers/factories/storage/transformer.go @@ -0,0 +1,53 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package storage + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/storage" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type Transformer struct { + Address common.Address + Mappings storage_diffs.Mappings + Repository storage_diffs.Repository +} + +func (transformer Transformer) NewTransformer(db *postgres.DB) storage.Transformer { + transformer.Mappings.SetDB(db) + transformer.Repository.SetDB(db) + return transformer +} + +func (transformer Transformer) ContractAddress() common.Address { + return transformer.Address +} + +func (transformer Transformer) Execute(row shared.StorageDiffRow) error { + metadata, lookupErr := transformer.Mappings.Lookup(row.StorageKey) + if lookupErr != nil { + return lookupErr + } + value, decodeErr := shared.Decode(row, metadata) + if decodeErr != nil { + return decodeErr + } + return transformer.Repository.Create(row.BlockHeight, row.BlockHash.Hex(), metadata, value) +} diff --git a/pkg/transformers/factories/storage/transformer_test.go b/pkg/transformers/factories/storage/transformer_test.go new file mode 100644 index 00000000..43821e46 --- /dev/null +++ b/pkg/transformers/factories/storage/transformer_test.go @@ -0,0 +1,102 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package storage_test + +import ( + "github.com/ethereum/go-ethereum/common" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "github.com/vulcanize/vulcanizedb/pkg/transformers/factories/storage" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" +) + +var _ = Describe("Storage transformer", func() { + var ( + mappings *mocks.MockMappings + repository *mocks.MockStorageRepository + transformer storage.Transformer + ) + + BeforeEach(func() { + mappings = &mocks.MockMappings{} + repository = &mocks.MockStorageRepository{} + transformer = storage.Transformer{ + Address: common.Address{}, + Mappings: mappings, + Repository: repository, + } + }) + + It("returns the contract address being watched", func() { + fakeAddress := common.HexToAddress("0x12345") + transformer.Address = fakeAddress + + Expect(transformer.ContractAddress()).To(Equal(fakeAddress)) + }) + + It("looks up metadata for storage key", func() { + transformer.Execute(shared.StorageDiffRow{}) + + Expect(mappings.LookupCalled).To(BeTrue()) + }) + + It("returns error if lookup fails", func() { + mappings.LookupErr = fakes.FakeError + + err := transformer.Execute(shared.StorageDiffRow{}) + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("creates storage row with decoded data", func() { + fakeMetadata := shared.StorageValueMetadata{Type: shared.Address} + mappings.Metadata = fakeMetadata + rawValue := common.HexToAddress("0x12345") + fakeBlockNumber := 123 + fakeBlockHash := "0x67890" + fakeRow := shared.StorageDiffRow{ + Contract: common.Address{}, + BlockHash: common.HexToHash(fakeBlockHash), + BlockHeight: fakeBlockNumber, + StorageKey: common.Hash{}, + StorageValue: rawValue.Hash(), + } + + err := transformer.Execute(fakeRow) + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedBlockNumber).To(Equal(fakeBlockNumber)) + Expect(repository.PassedBlockHash).To(Equal(common.HexToHash(fakeBlockHash).Hex())) + Expect(repository.PassedMetadata).To(Equal(fakeMetadata)) + Expect(repository.PassedValue.(string)).To(Equal(rawValue.Hex())) + }) + + It("returns error if creating row fails", func() { + rawValue := common.HexToAddress("0x12345") + fakeMetadata := shared.StorageValueMetadata{Type: shared.Address} + mappings.Metadata = fakeMetadata + repository.CreateErr = fakes.FakeError + + err := transformer.Execute(shared.StorageDiffRow{StorageValue: rawValue.Hash()}) + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) +}) diff --git a/pkg/transformers/frob/converter.go b/pkg/transformers/frob/converter.go index 8b765b91..267626af 100644 --- a/pkg/transformers/frob/converter.go +++ b/pkg/transformers/frob/converter.go @@ -17,7 +17,6 @@ package frob import ( - "bytes" "encoding/json" "fmt" log "github.com/sirupsen/logrus" @@ -68,7 +67,7 @@ func (FrobConverter) ToModels(entities []interface{}) ([]interface{}, error) { return nil, err } model := FrobModel{ - Ilk: string(bytes.Trim(frobEntity.Ilk[:], "\x00)")), + Ilk: common.Bytes2Hex(frobEntity.Ilk[:]), Urn: common.BytesToAddress(frobEntity.Urn[:]).String(), Ink: frobEntity.Ink.String(), Art: frobEntity.Art.String(), diff --git a/pkg/transformers/integration_tests/bite.go b/pkg/transformers/integration_tests/bite.go index 708a98eb..96d7bc5f 100644 --- a/pkg/transformers/integration_tests/bite.go +++ b/pkg/transformers/integration_tests/bite.go @@ -83,7 +83,7 @@ var _ = Describe("Bite Transformer", func() { Expect(len(dbResult)).To(Equal(1)) Expect(dbResult[0].Art).To(Equal("149846666666666655744")) Expect(dbResult[0].IArt).To(Equal("1645356666666666655736")) - Expect(dbResult[0].Ilk).To(Equal("ETH")) + Expect(dbResult[0].Ilk).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].Ink).To(Equal("1000000000000000000")) Expect(dbResult[0].NFlip).To(Equal("2")) Expect(dbResult[0].Tab).To(Equal("149846666666666655744")) diff --git a/pkg/transformers/integration_tests/cat_file.go b/pkg/transformers/integration_tests/cat_file.go index bc9a2358..4b84c94d 100644 --- a/pkg/transformers/integration_tests/cat_file.go +++ b/pkg/transformers/integration_tests/cat_file.go @@ -97,12 +97,12 @@ var _ = Describe("Cat File transformer", func() { Expect(len(dbResult)).To(Equal(2)) sort.Sort(byLogIndexChopLump(dbResult)) - Expect(dbResult[0].Ilk).To(Equal("REP")) + Expect(dbResult[0].Ilk).To(Equal("5245500000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].What).To(Equal("lump")) Expect(dbResult[0].Data).To(Equal("10000.000000000000000000")) Expect(dbResult[0].LogIndex).To(Equal(uint(3))) - Expect(dbResult[1].Ilk).To(Equal("REP")) + Expect(dbResult[1].Ilk).To(Equal("5245500000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[1].What).To(Equal("chop")) Expect(dbResult[1].Data).To(Equal("1.000000000000000000000000000")) Expect(dbResult[1].LogIndex).To(Equal(uint(4))) @@ -145,7 +145,7 @@ var _ = Describe("Cat File transformer", func() { Expect(err).NotTo(HaveOccurred()) Expect(len(dbResult)).To(Equal(1)) - Expect(dbResult[0].Ilk).To(Equal("ETH")) + Expect(dbResult[0].Ilk).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].What).To(Equal("flip")) Expect(dbResult[0].Flip).To(Equal("0x32D496Ad866D110060866B7125981C73642cc509")) }) diff --git a/pkg/transformers/integration_tests/drip_drip.go b/pkg/transformers/integration_tests/drip_drip.go index 0ca6e487..385e6bd7 100644 --- a/pkg/transformers/integration_tests/drip_drip.go +++ b/pkg/transformers/integration_tests/drip_drip.go @@ -85,6 +85,6 @@ var _ = Describe("DripDrip Transformer", func() { Expect(len(dbResults)).To(Equal(1)) dbResult := dbResults[0] - Expect(dbResult.Ilk).To(Equal("ETH")) + Expect(dbResult.Ilk).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) }) }) diff --git a/pkg/transformers/integration_tests/frob.go b/pkg/transformers/integration_tests/frob.go index b3f01742..64de1ac6 100644 --- a/pkg/transformers/integration_tests/frob.go +++ b/pkg/transformers/integration_tests/frob.go @@ -94,7 +94,7 @@ var _ = Describe("Frob Transformer", func() { Expect(dbResult[0].Dart).To(Equal("0")) Expect(dbResult[0].Dink).To(Equal("10000000000000")) Expect(dbResult[0].IArt).To(Equal("1495509999999999999992")) - Expect(dbResult[0].Ilk).To(Equal("ETH")) + Expect(dbResult[0].Ilk).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].Ink).To(Equal("10050100000000000")) Expect(dbResult[0].Urn).To(Equal("0xc8E093e5f3F9B5Aa6A6b33ea45960b93C161430C")) }) diff --git a/pkg/transformers/integration_tests/pit_file_ilk.go b/pkg/transformers/integration_tests/pit_file_ilk.go index 5dfca2e6..85a9be27 100644 --- a/pkg/transformers/integration_tests/pit_file_ilk.go +++ b/pkg/transformers/integration_tests/pit_file_ilk.go @@ -87,7 +87,7 @@ var _ = Describe("PitFileIlk LogNoteTransformer", func() { Expect(err).NotTo(HaveOccurred()) Expect(len(dbResult)).To(Equal(1)) - Expect(dbResult[0].Ilk).To(Equal("ETH")) + Expect(dbResult[0].Ilk).To(Equal("0x4554480000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].What).To(Equal("spot")) Expect(dbResult[0].Data).To(Equal("139.840000000000003410605131648")) }) @@ -119,7 +119,7 @@ var _ = Describe("PitFileIlk LogNoteTransformer", func() { pitFileIlkLineModel = result } } - Expect(pitFileIlkLineModel.Ilk).To(Equal("REP")) + Expect(pitFileIlkLineModel.Ilk).To(Equal("0x5245500000000000000000000000000000000000000000000000000000000000")) Expect(pitFileIlkLineModel.Data).To(Equal("2000000.000000000000000000")) }) }) diff --git a/pkg/transformers/integration_tests/vat_flux.go b/pkg/transformers/integration_tests/vat_flux.go index 6f46e2ee..953b6e11 100644 --- a/pkg/transformers/integration_tests/vat_flux.go +++ b/pkg/transformers/integration_tests/vat_flux.go @@ -74,7 +74,7 @@ var _ = Describe("VatFlux LogNoteTransformer", func() { Expect(err).NotTo(HaveOccurred()) Expect(len(dbResult)).To(Equal(1)) - Expect(dbResult[0].Ilk).To(Equal("REP")) + Expect(dbResult[0].Ilk).To(Equal("5245500000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].Src).To(Equal("0xC0851F73CC8DD5c0765E71980eC7E7Fd1EF74434")) Expect(dbResult[0].Dst).To(Equal("0x0000d8b4147eDa80Fec7122AE16DA2479Cbd7ffB")) Expect(dbResult[0].Rad).To(Equal("1800000000000000000000000000000000000000000000")) diff --git a/pkg/transformers/integration_tests/vat_fold.go b/pkg/transformers/integration_tests/vat_fold.go index 8357f950..1b1e8113 100644 --- a/pkg/transformers/integration_tests/vat_fold.go +++ b/pkg/transformers/integration_tests/vat_fold.go @@ -82,7 +82,7 @@ var _ = Describe("VatFold Transformer", func() { Expect(len(dbResults)).To(Equal(1)) dbResult := dbResults[0] - Expect(dbResult.Ilk).To(Equal("REP")) + Expect(dbResult.Ilk).To(Equal("5245500000000000000000000000000000000000000000000000000000000000")) Expect(dbResult.Urn).To(Equal(common.HexToAddress("0x0000000000000000000000003728e9777b2a0a611ee0f89e00e01044ce4736d1").String())) Expect(dbResult.Rate).To(Equal("0.000000000000000000000000000")) }) diff --git a/pkg/transformers/integration_tests/vat_grab.go b/pkg/transformers/integration_tests/vat_grab.go index b29f1e7a..caef8338 100644 --- a/pkg/transformers/integration_tests/vat_grab.go +++ b/pkg/transformers/integration_tests/vat_grab.go @@ -75,7 +75,7 @@ var _ = Describe("Vat Grab Transformer", func() { Expect(err).NotTo(HaveOccurred()) Expect(len(dbResult)).To(Equal(1)) - Expect(dbResult[0].Ilk).To(Equal("REP")) + Expect(dbResult[0].Ilk).To(Equal("5245500000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].Urn).To(Equal("0x6a3AE20C315E845B2E398e68EfFe39139eC6060C")) Expect(dbResult[0].V).To(Equal("0x2F34f22a00eE4b7a8F8BBC4eAee1658774c624e0")) //cat contract address Expect(dbResult[0].W).To(Equal("0x3728e9777B2a0a611ee0F89e00E01044ce4736d1")) diff --git a/pkg/transformers/integration_tests/vat_init.go b/pkg/transformers/integration_tests/vat_init.go index bbc0e47a..caf390fc 100644 --- a/pkg/transformers/integration_tests/vat_init.go +++ b/pkg/transformers/integration_tests/vat_init.go @@ -74,6 +74,6 @@ var _ = Describe("VatInit LogNoteTransformer", func() { Expect(len(dbResults)).To(Equal(1)) dbResult := dbResults[0] - Expect(dbResult.Ilk).To(Equal("ETH")) + Expect(dbResult.Ilk).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) }) }) diff --git a/pkg/transformers/integration_tests/vat_slip.go b/pkg/transformers/integration_tests/vat_slip.go index 28a4b160..0cda5d49 100644 --- a/pkg/transformers/integration_tests/vat_slip.go +++ b/pkg/transformers/integration_tests/vat_slip.go @@ -66,7 +66,7 @@ var _ = Describe("Vat slip transformer", func() { var model vat_slip.VatSlipModel err = db.Get(&model, `SELECT ilk, guy, rad, tx_idx FROM maker.vat_slip WHERE header_id = $1`, headerID) Expect(err).NotTo(HaveOccurred()) - Expect(model.Ilk).To(Equal("ETH")) + Expect(model.Ilk).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) Expect(model.Guy).To(Equal("0xDA15dCE70ab462E66779f23ee14F21d993789eE3")) Expect(model.Rad).To(Equal("100000000000000000000000000000000000000000000000")) Expect(model.TransactionIndex).To(Equal(uint(0))) diff --git a/pkg/transformers/integration_tests/vat_tune.go b/pkg/transformers/integration_tests/vat_tune.go index ba443847..40c8a23a 100644 --- a/pkg/transformers/integration_tests/vat_tune.go +++ b/pkg/transformers/integration_tests/vat_tune.go @@ -75,7 +75,7 @@ var _ = Describe("VatTune LogNoteTransformer", func() { Expect(err).NotTo(HaveOccurred()) Expect(len(dbResult)).To(Equal(1)) - Expect(dbResult[0].Ilk).To(Equal("ETH")) + Expect(dbResult[0].Ilk).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) Expect(dbResult[0].Urn).To(Equal("0x4F26FfBe5F04ED43630fdC30A87638d53D0b0876")) Expect(dbResult[0].V).To(Equal("0x4F26FfBe5F04ED43630fdC30A87638d53D0b0876")) Expect(dbResult[0].W).To(Equal("0x4F26FfBe5F04ED43630fdC30A87638d53D0b0876")) diff --git a/pkg/transformers/pit_file/ilk/converter.go b/pkg/transformers/pit_file/ilk/converter.go index b1d4a94c..3945f22b 100644 --- a/pkg/transformers/pit_file/ilk/converter.go +++ b/pkg/transformers/pit_file/ilk/converter.go @@ -37,7 +37,7 @@ func (PitFileIlkConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + ilk := ethLog.Topics[2].Hex() what := string(bytes.Trim(ethLog.Topics[3].Bytes(), "\x00")) dataBytes := ethLog.Data[len(ethLog.Data)-constants.DataItemLength:] data, err := getData(dataBytes, what) diff --git a/pkg/transformers/shared/storage/transformer.go b/pkg/transformers/shared/storage/transformer.go new file mode 100644 index 00000000..240f8ca1 --- /dev/null +++ b/pkg/transformers/shared/storage/transformer.go @@ -0,0 +1,14 @@ +package storage + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type Transformer interface { + Execute(row shared.StorageDiffRow) error + ContractAddress() common.Address +} + +type TransformerInitializer func(db *postgres.DB) Transformer diff --git a/pkg/transformers/shared/utilities.go b/pkg/transformers/shared/utilities.go index 7ee4b5b6..a33587ae 100644 --- a/pkg/transformers/shared/utilities.go +++ b/pkg/transformers/shared/utilities.go @@ -17,6 +17,7 @@ package shared import ( + "github.com/ethereum/go-ethereum/common" "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/constants" "math/big" ) @@ -59,6 +60,10 @@ func GetDataBytesAtIndex(n int, logData []byte) []byte { return []byte{} } +func GetHexWithoutPrefix(raw []byte) string { + return common.Bytes2Hex(raw) +} + func ConvertToRay(value string) string { return convert(ray, value, rayPrecision) } diff --git a/pkg/transformers/shared/utilities_test.go b/pkg/transformers/shared/utilities_test.go index 905bc1be..56a02e95 100644 --- a/pkg/transformers/shared/utilities_test.go +++ b/pkg/transformers/shared/utilities_test.go @@ -62,4 +62,12 @@ var _ = Describe("Shared utilities", func() { Expect(wadTwo).To(Equal("1.234567890123456690")) }) }) + + Describe("getting hex without prefix", func() { + It("returns bytes as hex without 0x prefix", func() { + raw := common.HexToHash("0x4554480000000000000000000000000000000000000000000000000000000000").Bytes() + result := shared.GetHexWithoutPrefix(raw) + Expect(result).To(Equal("4554480000000000000000000000000000000000000000000000000000000000")) + }) + }) }) diff --git a/pkg/transformers/storage_diffs/maker/maker_storage_repository.go b/pkg/transformers/storage_diffs/maker/maker_storage_repository.go new file mode 100644 index 00000000..5f6cf6ae --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/maker_storage_repository.go @@ -0,0 +1,38 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package maker + +import "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + +type IMakerStorageRepository interface { + GetIlks() ([]string, error) + SetDB(db *postgres.DB) +} + +type MakerStorageRepository struct { + db *postgres.DB +} + +func (repository *MakerStorageRepository) SetDB(db *postgres.DB) { + repository.db = db +} + +func (repository MakerStorageRepository) GetIlks() ([]string, error) { + var ilks []string + err := repository.db.Select(&ilks, `SELECT DISTINCT ilk FROM maker.vat_init`) + return ilks, err +} diff --git a/pkg/transformers/storage_diffs/maker/maker_storage_repository_test.go b/pkg/transformers/storage_diffs/maker/maker_storage_repository_test.go new file mode 100644 index 00000000..262b12b1 --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/maker_storage_repository_test.go @@ -0,0 +1,57 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package maker_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("Maker storage repository", func() { + It("fetches unique ilks from vat init events", func() { + db := test_config.NewTestDB(test_config.NewTestNode()) + test_config.CleanTestDB(db) + insertVatInit("ilk1", 1, db) + insertVatInit("ilk2", 2, db) + insertVatInit("ilk2", 3, db) + repository := maker.MakerStorageRepository{} + repository.SetDB(db) + + ilks, err := repository.GetIlks() + + Expect(err).NotTo(HaveOccurred()) + Expect(len(ilks)).To(Equal(2)) + Expect(ilks).To(ConsistOf("ilk1", "ilk2")) + }) +}) + +func insertVatInit(ilk string, blockNumber int64, db *postgres.DB) { + headerRepository := repositories.NewHeaderRepository(db) + headerID, err := headerRepository.CreateOrUpdateHeader(fakes.GetFakeHeader(blockNumber)) + Expect(err).NotTo(HaveOccurred()) + _, execErr := db.Exec( + `INSERT INTO maker.vat_init (header_id, ilk, log_idx, tx_idx, raw_log) + VALUES($1, $2, $3, $4, $5)`, + headerID, ilk, 0, 0, "[]", + ) + Expect(execErr).NotTo(HaveOccurred()) +} diff --git a/pkg/transformers/storage_diffs/maker/maker_suite_test.go b/pkg/transformers/storage_diffs/maker/maker_suite_test.go new file mode 100644 index 00000000..601507b9 --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/maker_suite_test.go @@ -0,0 +1,29 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package maker_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestMaker(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Maker Suite") +} diff --git a/pkg/transformers/storage_diffs/maker/pit/mappings.go b/pkg/transformers/storage_diffs/maker/pit/mappings.go new file mode 100644 index 00000000..4b9d299a --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/pit/mappings.go @@ -0,0 +1,146 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package pit + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" + "math/big" +) + +const ( + IlkLine = "line" + IlkSpot = "spot" + PitDrip = "drip" + PitLine = "Line" + PitLive = "live" + PitVat = "vat" +) + +var ( + // storage key and value metadata for "drip" on the Pit contract + DripKey = common.HexToHash(storage_diffs.IndexFive) + DripMetadata = shared.StorageValueMetadata{ + Name: PitDrip, + Key: "", + Type: shared.Address, + } + + IlkSpotIndex = storage_diffs.IndexOne + + // storage key and value metadata for "Spot" on the Pit contract + LineKey = common.HexToHash(storage_diffs.IndexThree) + LineMetadata = shared.StorageValueMetadata{ + Name: PitLine, + Key: "", + Type: shared.Uint256, + } + + // storage key and value metadata for "live" on the Pit contract + LiveKey = common.HexToHash(storage_diffs.IndexTwo) + LiveMetadata = shared.StorageValueMetadata{ + Name: PitLive, + Key: "", + Type: shared.Uint256, + } + + // storage key and value metadata for "vat" on the Pit contract + VatKey = common.HexToHash(storage_diffs.IndexFour) + VatMetadata = shared.StorageValueMetadata{ + Name: PitVat, + Key: "", + Type: shared.Address, + } +) + +type PitMappings struct { + StorageRepository maker.IMakerStorageRepository + mappings map[common.Hash]shared.StorageValueMetadata +} + +func (mappings *PitMappings) SetDB(db *postgres.DB) { + mappings.StorageRepository.SetDB(db) +} + +func (mappings *PitMappings) Lookup(key common.Hash) (shared.StorageValueMetadata, error) { + metadata, ok := mappings.mappings[key] + if !ok { + err := mappings.loadMappings() + if err != nil { + return metadata, err + } + metadata, ok = mappings.mappings[key] + if !ok { + return metadata, shared.ErrStorageKeyNotFound{Key: key.Hex()} + } + } + return metadata, nil +} + +func (mappings *PitMappings) loadMappings() error { + mappings.mappings = getStaticMappings() + ilks, err := mappings.StorageRepository.GetIlks() + if err != nil { + return err + } + for _, ilk := range ilks { + mappings.mappings[getSpotKey(ilk)] = getSpotMetadata(ilk) + mappings.mappings[getLineKey(ilk)] = getLineMetadata(ilk) + } + return nil +} + +func getStaticMappings() map[common.Hash]shared.StorageValueMetadata { + mappings := make(map[common.Hash]shared.StorageValueMetadata) + mappings[DripKey] = DripMetadata + mappings[LineKey] = LineMetadata + mappings[LiveKey] = LiveMetadata + mappings[VatKey] = VatMetadata + return mappings +} + +func getSpotKey(ilk string) common.Hash { + keyBytes := common.FromHex("0x" + ilk + IlkSpotIndex) + encoded := crypto.Keccak256(keyBytes) + return common.BytesToHash(encoded) +} + +func getSpotMetadata(ilk string) shared.StorageValueMetadata { + return shared.StorageValueMetadata{ + Name: IlkSpot, + Key: ilk, + Type: shared.Uint256, + } +} + +func getLineKey(ilk string) common.Hash { + spotMappingAsInt := big.NewInt(0).SetBytes(getSpotKey(ilk).Bytes()) + incrementedByOne := big.NewInt(0).Add(spotMappingAsInt, big.NewInt(1)) + return common.BytesToHash(incrementedByOne.Bytes()) +} + +func getLineMetadata(ilk string) shared.StorageValueMetadata { + return shared.StorageValueMetadata{ + Name: IlkLine, + Key: ilk, + Type: shared.Uint256, + } +} diff --git a/pkg/transformers/storage_diffs/maker/pit/mappings_test.go b/pkg/transformers/storage_diffs/maker/pit/mappings_test.go new file mode 100644 index 00000000..2bbb22aa --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/pit/mappings_test.go @@ -0,0 +1,90 @@ +package pit_test + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker/pit" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker/test_helpers" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" + "math/big" +) + +var _ = Describe("Pit storage mappings", func() { + Describe("looking up static keys", func() { + It("returns value metadata if key exists", func() { + storageRepository := &test_helpers.MockMakerStorageRepository{} + mappings := pit.PitMappings{StorageRepository: storageRepository} + + Expect(mappings.Lookup(pit.DripKey)).To(Equal(pit.DripMetadata)) + Expect(mappings.Lookup(pit.LineKey)).To(Equal(pit.LineMetadata)) + Expect(mappings.Lookup(pit.LiveKey)).To(Equal(pit.LiveMetadata)) + Expect(mappings.Lookup(pit.VatKey)).To(Equal(pit.VatMetadata)) + }) + + It("returns error if key does not exist", func() { + mappings := pit.PitMappings{StorageRepository: &test_helpers.MockMakerStorageRepository{}} + + _, err := mappings.Lookup(common.HexToHash(fakes.FakeHash.Hex())) + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(shared.ErrStorageKeyNotFound{Key: fakes.FakeHash.Hex()})) + }) + }) + + Describe("looking up dynamic keys", func() { + It("refreshes mappings from repository if key not found", func() { + storageRepository := &test_helpers.MockMakerStorageRepository{} + mappings := pit.PitMappings{StorageRepository: storageRepository} + + mappings.Lookup(fakes.FakeHash) + + Expect(storageRepository.GetIlksCalled).To(BeTrue()) + }) + + It("returns value metadata for spot when ilk in the DB", func() { + storageRepository := &test_helpers.MockMakerStorageRepository{} + fakeIlk := "fakeIlk" + storageRepository.SetIlks([]string{fakeIlk}) + mappings := pit.PitMappings{StorageRepository: storageRepository} + ilkSpotKey := common.BytesToHash(crypto.Keccak256(common.FromHex("0x" + fakeIlk + pit.IlkSpotIndex))) + expectedMetadata := shared.StorageValueMetadata{ + Name: pit.IlkSpot, + Key: fakeIlk, + Type: shared.Uint256, + } + + Expect(mappings.Lookup(ilkSpotKey)).To(Equal(expectedMetadata)) + }) + + It("returns value metadata for line when ilk in the DB", func() { + storageRepository := &test_helpers.MockMakerStorageRepository{} + fakeIlk := "fakeIlk" + storageRepository.SetIlks([]string{fakeIlk}) + mappings := pit.PitMappings{StorageRepository: storageRepository} + ilkSpotKeyBytes := crypto.Keccak256(common.FromHex("0x" + fakeIlk + pit.IlkSpotIndex)) + ilkSpotAsInt := big.NewInt(0).SetBytes(ilkSpotKeyBytes) + incrementedIlkSpot := big.NewInt(0).Add(ilkSpotAsInt, big.NewInt(1)) + ilkLineKey := common.BytesToHash(incrementedIlkSpot.Bytes()) + expectedMetadata := shared.StorageValueMetadata{ + Name: pit.IlkLine, + Key: fakeIlk, + Type: shared.Uint256, + } + + Expect(mappings.Lookup(ilkLineKey)).To(Equal(expectedMetadata)) + }) + + It("returns error if key not found", func() { + storageRepository := &test_helpers.MockMakerStorageRepository{} + mappings := pit.PitMappings{StorageRepository: storageRepository} + + _, err := mappings.Lookup(fakes.FakeHash) + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(shared.ErrStorageKeyNotFound{Key: fakes.FakeHash.Hex()})) + }) + }) +}) diff --git a/pkg/transformers/storage_diffs/maker/pit/pit_suite_test.go b/pkg/transformers/storage_diffs/maker/pit/pit_suite_test.go new file mode 100644 index 00000000..2f78139f --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/pit/pit_suite_test.go @@ -0,0 +1,13 @@ +package pit_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestPit(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Pit Suite") +} diff --git a/pkg/transformers/storage_diffs/maker/pit/repository.go b/pkg/transformers/storage_diffs/maker/pit/repository.go new file mode 100644 index 00000000..a1c5c9df --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/pit/repository.go @@ -0,0 +1,79 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package pit + +import ( + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type PitStorageRepository struct { + db *postgres.DB +} + +func (repository *PitStorageRepository) SetDB(db *postgres.DB) { + repository.db = db +} + +func (repository PitStorageRepository) Create(blockNumber int, blockHash string, metadata shared.StorageValueMetadata, value interface{}) error { + switch metadata.Name { + case IlkLine: + return repository.insertIlkLine(blockNumber, blockHash, metadata.Key, value.(string)) + case IlkSpot: + return repository.insertIlkSpot(blockNumber, blockHash, metadata.Key, value.(string)) + case PitDrip: + return repository.insertPitDrip(blockNumber, blockHash, value.(string)) + case PitLine: + return repository.insertPitLine(blockNumber, blockHash, value.(string)) + case PitLive: + return repository.insertPitLive(blockNumber, blockHash, value.(string)) + case PitVat: + return repository.insertPitVat(blockNumber, blockHash, value.(string)) + default: + panic("unrecognized storage metadata name") + } +} + +func (repository PitStorageRepository) insertIlkLine(blockNumber int, blockHash string, ilk string, line string) error { + _, err := repository.db.Exec(`INSERT INTO maker.pit_ilk_line (block_number, block_hash, ilk, line) VALUES ($1, $2, $3, $4)`, blockNumber, blockHash, ilk, line) + return err +} + +func (repository PitStorageRepository) insertIlkSpot(blockNumber int, blockHash string, ilk string, spot string) error { + _, err := repository.db.Exec(`INSERT INTO maker.pit_ilk_spot (block_number, block_hash, ilk, spot) VALUES ($1, $2, $3, $4)`, blockNumber, blockHash, ilk, spot) + return err +} + +func (repository PitStorageRepository) insertPitDrip(blockNumber int, blockHash string, drip string) error { + _, err := repository.db.Exec(`INSERT INTO maker.pit_drip (block_number, block_hash, drip) VALUES ($1, $2, $3)`, blockNumber, blockHash, drip) + return err +} + +func (repository PitStorageRepository) insertPitLine(blockNumber int, blockHash string, line string) error { + _, err := repository.db.Exec(`INSERT INTO maker.pit_line (block_number, block_hash, line) VALUES ($1, $2, $3)`, blockNumber, blockHash, line) + return err +} + +func (repository PitStorageRepository) insertPitLive(blockNumber int, blockHash string, live string) error { + _, err := repository.db.Exec(`INSERT INTO maker.pit_live (block_number, block_hash, live) VALUES ($1, $2, $3)`, blockNumber, blockHash, live) + return err +} + +func (repository PitStorageRepository) insertPitVat(blockNumber int, blockHash string, vat string) error { + _, err := repository.db.Exec(`INSERT INTO maker.pit_vat (block_number, block_hash, vat) VALUES ($1, $2, $3)`, blockNumber, blockHash, vat) + return err +} diff --git a/pkg/transformers/storage_diffs/maker/pit/repository_test.go b/pkg/transformers/storage_diffs/maker/pit/repository_test.go new file mode 100644 index 00000000..97f3ac3f --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/pit/repository_test.go @@ -0,0 +1,172 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package pit_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker/pit" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("Pit storage repository", func() { + var ( + blockNumber int + blockHash string + db *postgres.DB + err error + repo pit.PitStorageRepository + ) + + BeforeEach(func() { + blockNumber = 123 + blockHash = "expected_block_hash" + db = test_config.NewTestDB(test_config.NewTestNode()) + test_config.CleanTestDB(db) + repo = pit.PitStorageRepository{} + repo.SetDB(db) + }) + + It("persists an ilk line", func() { + expectedIlk := "fake_ilk" + expectedLine := "12345" + ilkLineMetadata := shared.StorageValueMetadata{ + Name: pit.IlkLine, + Key: expectedIlk, + Type: shared.Uint256, + } + err = repo.Create(blockNumber, blockHash, ilkLineMetadata, expectedLine) + + Expect(err).NotTo(HaveOccurred()) + type IlkLine struct { + BlockMetadata + Ilk string + Line string + } + var result IlkLine + err = db.Get(&result, `SELECT block_number, block_hash, ilk, line FROM maker.pit_ilk_line`) + Expect(err).NotTo(HaveOccurred()) + Expect(result.BlockNumber).To(Equal(blockNumber)) + Expect(result.BlockHash).To(Equal(blockHash)) + Expect(result.Ilk).To(Equal(expectedIlk)) + Expect(result.Line).To(Equal(expectedLine)) + }) + + It("persists an ilk spot", func() { + expectedIlk := "fake_ilk" + expectedSpot := "12345" + ilkSpotMetadata := shared.StorageValueMetadata{ + Name: pit.IlkSpot, + Key: expectedIlk, + Type: shared.Uint256, + } + err = repo.Create(blockNumber, blockHash, ilkSpotMetadata, expectedSpot) + + Expect(err).NotTo(HaveOccurred()) + type IlkSpot struct { + BlockMetadata + Ilk string + Spot string + } + var result IlkSpot + err = db.Get(&result, `SELECT block_number, block_hash, ilk, spot FROM maker.pit_ilk_spot`) + Expect(err).NotTo(HaveOccurred()) + Expect(result.BlockNumber).To(Equal(blockNumber)) + Expect(result.BlockHash).To(Equal(blockHash)) + Expect(result.Ilk).To(Equal(expectedIlk)) + Expect(result.Spot).To(Equal(expectedSpot)) + }) + + It("persists a pit drip", func() { + expectedDrip := "0x0123456789abcdef0123" + + err = repo.Create(blockNumber, blockHash, pit.DripMetadata, expectedDrip) + + Expect(err).NotTo(HaveOccurred()) + type PitDrip struct { + BlockMetadata + Drip string + } + var result PitDrip + err = db.Get(&result, `SELECT block_number, block_hash, drip FROM maker.pit_drip`) + Expect(err).NotTo(HaveOccurred()) + Expect(result.BlockNumber).To(Equal(blockNumber)) + Expect(result.BlockHash).To(Equal(blockHash)) + Expect(result.Drip).To(Equal(expectedDrip)) + }) + + It("persists a pit line", func() { + expectedLine := "12345" + + err = repo.Create(blockNumber, blockHash, pit.LineMetadata, expectedLine) + + Expect(err).NotTo(HaveOccurred()) + type PitLine struct { + BlockMetadata + Line string + } + var result PitLine + err = db.Get(&result, `SELECT block_number, block_hash, line FROM maker.pit_line`) + Expect(err).NotTo(HaveOccurred()) + Expect(result.BlockNumber).To(Equal(blockNumber)) + Expect(result.BlockHash).To(Equal(blockHash)) + Expect(result.Line).To(Equal(expectedLine)) + }) + + It("persists a pit live", func() { + expectedLive := "12345" + + err = repo.Create(blockNumber, blockHash, pit.LiveMetadata, expectedLive) + + Expect(err).NotTo(HaveOccurred()) + type PitLive struct { + BlockMetadata + Live string + } + var result PitLive + err = db.Get(&result, `SELECT block_number, block_hash, live FROM maker.pit_live`) + Expect(err).NotTo(HaveOccurred()) + Expect(result.BlockNumber).To(Equal(blockNumber)) + Expect(result.BlockHash).To(Equal(blockHash)) + Expect(result.Live).To(Equal(expectedLive)) + }) + + It("persists a pit vat", func() { + expectedVat := "0x0123456789abcdef0123" + + err = repo.Create(blockNumber, blockHash, pit.VatMetadata, expectedVat) + + Expect(err).NotTo(HaveOccurred()) + type PitVat struct { + BlockMetadata + Vat string + } + var result PitVat + err = db.Get(&result, `SELECT block_number, block_hash, vat FROM maker.pit_vat`) + Expect(err).NotTo(HaveOccurred()) + Expect(result.BlockNumber).To(Equal(blockNumber)) + Expect(result.BlockHash).To(Equal(blockHash)) + Expect(result.Vat).To(Equal(expectedVat)) + }) +}) + +type BlockMetadata struct { + BlockNumber int `db:"block_number"` + BlockHash string `db:"block_hash"` +} diff --git a/pkg/transformers/storage_diffs/maker/test_helpers/maker_storage_repository.go b/pkg/transformers/storage_diffs/maker/test_helpers/maker_storage_repository.go new file mode 100644 index 00000000..6f259df3 --- /dev/null +++ b/pkg/transformers/storage_diffs/maker/test_helpers/maker_storage_repository.go @@ -0,0 +1,19 @@ +package test_helpers + +import "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + +type MockMakerStorageRepository struct { + GetIlksCalled bool + ilks []string +} + +func (repository *MockMakerStorageRepository) GetIlks() ([]string, error) { + repository.GetIlksCalled = true + return repository.ilks, nil +} + +func (repository *MockMakerStorageRepository) SetDB(db *postgres.DB) {} + +func (repository *MockMakerStorageRepository) SetIlks(ilks []string) { + repository.ilks = ilks +} diff --git a/pkg/transformers/storage_diffs/mappings.go b/pkg/transformers/storage_diffs/mappings.go new file mode 100644 index 00000000..6f3cb028 --- /dev/null +++ b/pkg/transformers/storage_diffs/mappings.go @@ -0,0 +1,39 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package storage_diffs + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type Mappings interface { + Lookup(key common.Hash) (shared.StorageValueMetadata, error) + SetDB(db *postgres.DB) +} + +const ( + IndexZero = "0000000000000000000000000000000000000000000000000000000000000000" + IndexOne = "0000000000000000000000000000000000000000000000000000000000000001" + IndexTwo = "0000000000000000000000000000000000000000000000000000000000000002" + IndexThree = "0000000000000000000000000000000000000000000000000000000000000003" + IndexFour = "0000000000000000000000000000000000000000000000000000000000000004" + IndexFive = "0000000000000000000000000000000000000000000000000000000000000005" + IndexSix = "0000000000000000000000000000000000000000000000000000000000000006" + IndexSeven = "0000000000000000000000000000000000000000000000000000000000000007" +) diff --git a/pkg/transformers/storage_diffs/repository.go b/pkg/transformers/storage_diffs/repository.go new file mode 100644 index 00000000..7baedb6a --- /dev/null +++ b/pkg/transformers/storage_diffs/repository.go @@ -0,0 +1,27 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package storage_diffs + +import ( + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type Repository interface { + Create(blockNumber int, blockHash string, metadata shared.StorageValueMetadata, value interface{}) error + SetDB(db *postgres.DB) +} diff --git a/pkg/transformers/storage_diffs/shared/decoder.go b/pkg/transformers/storage_diffs/shared/decoder.go new file mode 100644 index 00000000..753b2416 --- /dev/null +++ b/pkg/transformers/storage_diffs/shared/decoder.go @@ -0,0 +1,42 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared + +import ( + "github.com/ethereum/go-ethereum/common" + "math/big" +) + +func Decode(row StorageDiffRow, metadata StorageValueMetadata) (interface{}, error) { + switch metadata.Type { + case Uint256: + return decodeUint256(row.StorageValue.Bytes()), nil + case Address: + return decodeAddress(row.StorageValue.Bytes()), nil + default: + return nil, ErrTypeNotFound{} + } +} + +func decodeUint256(raw []byte) string { + n := big.NewInt(0).SetBytes(raw) + return n.String() +} + +func decodeAddress(raw []byte) string { + return common.BytesToAddress(raw).Hex() +} diff --git a/pkg/transformers/storage_diffs/shared/decoder_test.go b/pkg/transformers/storage_diffs/shared/decoder_test.go new file mode 100644 index 00000000..710e1e16 --- /dev/null +++ b/pkg/transformers/storage_diffs/shared/decoder_test.go @@ -0,0 +1,58 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared_test + +import ( + "github.com/ethereum/go-ethereum/common" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" + "math/big" +) + +var _ = Describe("Storage decoder", func() { + It("decodes uint256", func() { + fakeInt := common.HexToHash("0000000000000000000000000000000000000000000000000000000000000539") + row := shared.StorageDiffRow{StorageValue: fakeInt} + metadata := shared.StorageValueMetadata{Type: shared.Uint256} + + result, err := shared.Decode(row, metadata) + + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(big.NewInt(0).SetBytes(fakeInt.Bytes()).String())) + }) + + It("decodes address", func() { + fakeAddress := common.HexToAddress("0x12345") + row := shared.StorageDiffRow{StorageValue: fakeAddress.Hash()} + metadata := shared.StorageValueMetadata{Type: shared.Address} + + result, err := shared.Decode(row, metadata) + + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(fakeAddress.Hex())) + }) + + It("returns error if attempting to decode unknown type", func() { + metadata := shared.StorageValueMetadata{Type: 100} + + _, err := shared.Decode(shared.StorageDiffRow{}, metadata) + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(shared.ErrTypeNotFound{})) + }) +}) diff --git a/pkg/transformers/storage_diffs/shared/errors.go b/pkg/transformers/storage_diffs/shared/errors.go new file mode 100644 index 00000000..3d74d4a9 --- /dev/null +++ b/pkg/transformers/storage_diffs/shared/errors.go @@ -0,0 +1,61 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared + +import "fmt" + +type ErrContractNotFound struct { + Contract string +} + +func (e ErrContractNotFound) Error() string { + return fmt.Sprintf("transformer not found for contract: %s", e.Contract) +} + +type ErrHeaderMismatch struct { + BlockHeight int + DbHash string + DiffHash string +} + +func (e ErrHeaderMismatch) Error() string { + return fmt.Sprintf("header hash in row does not match db at height %d - row: %s, db: %s", e.BlockHeight, e.DbHash, e.DiffHash) +} + +type ErrRowMalformed struct { + Length int +} + +func (e ErrRowMalformed) Error() string { + return fmt.Sprintf("storage row malformed: length %d, expected %d", e.Length, ExpectedRowLength) +} + +type ErrStorageKeyNotFound struct { + Key string +} + +func (e ErrStorageKeyNotFound) Error() string { + return fmt.Sprintf("unknown storage key: %s", e.Key) +} + +type ErrTypeNotFound struct { + Type int +} + +func (e ErrTypeNotFound) Error() string { + return fmt.Sprintf("no decoder for type: %d", e.Type) +} diff --git a/pkg/transformers/storage_diffs/shared/row.go b/pkg/transformers/storage_diffs/shared/row.go new file mode 100644 index 00000000..3de11d41 --- /dev/null +++ b/pkg/transformers/storage_diffs/shared/row.go @@ -0,0 +1,49 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared + +import ( + "github.com/ethereum/go-ethereum/common" + "strconv" +) + +const ExpectedRowLength = 5 + +type StorageDiffRow struct { + Contract common.Address + BlockHash common.Hash + BlockHeight int + StorageKey common.Hash + StorageValue common.Hash +} + +func FromStrings(csvRow []string) (StorageDiffRow, error) { + if len(csvRow) != ExpectedRowLength { + return StorageDiffRow{}, ErrRowMalformed{Length: len(csvRow)} + } + height, err := strconv.Atoi(csvRow[2]) + if err != nil { + return StorageDiffRow{}, err + } + return StorageDiffRow{ + Contract: common.HexToAddress(csvRow[0]), + BlockHash: common.HexToHash(csvRow[1]), + BlockHeight: height, + StorageKey: common.HexToHash(csvRow[3]), + StorageValue: common.HexToHash(csvRow[4]), + }, nil +} diff --git a/pkg/transformers/storage_diffs/shared/row_test.go b/pkg/transformers/storage_diffs/shared/row_test.go new file mode 100644 index 00000000..a75201fb --- /dev/null +++ b/pkg/transformers/storage_diffs/shared/row_test.go @@ -0,0 +1,57 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared_test + +import ( + "github.com/ethereum/go-ethereum/common" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +var _ = Describe("Storage row parsing", func() { + It("converts an array of strings to a row struct", func() { + contract := "0x123" + blockHash := "0x456" + blockHeight := "789" + storageKey := "0x987" + storageValue := "0x654" + data := []string{contract, blockHash, blockHeight, storageKey, storageValue} + + result, err := shared.FromStrings(data) + + Expect(err).NotTo(HaveOccurred()) + Expect(result.Contract).To(Equal(common.HexToAddress(contract))) + Expect(result.BlockHash).To(Equal(common.HexToHash(blockHash))) + Expect(result.BlockHeight).To(Equal(789)) + Expect(result.StorageKey).To(Equal(common.HexToHash(storageKey))) + Expect(result.StorageValue).To(Equal(common.HexToHash(storageValue))) + }) + + It("returns an error if row is missing data", func() { + _, err := shared.FromStrings([]string{"0x123"}) + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(shared.ErrRowMalformed{Length: 1})) + }) + + It("returns error if block height malformed", func() { + _, err := shared.FromStrings([]string{"", "", "", "", ""}) + + Expect(err).To(HaveOccurred()) + }) +}) diff --git a/pkg/transformers/storage_diffs/shared/shared_suite_test.go b/pkg/transformers/storage_diffs/shared/shared_suite_test.go new file mode 100644 index 00000000..a36f747f --- /dev/null +++ b/pkg/transformers/storage_diffs/shared/shared_suite_test.go @@ -0,0 +1,29 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestShared(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Shared Suite") +} diff --git a/pkg/transformers/storage_diffs/shared/value.go b/pkg/transformers/storage_diffs/shared/value.go new file mode 100644 index 00000000..d19d95b5 --- /dev/null +++ b/pkg/transformers/storage_diffs/shared/value.go @@ -0,0 +1,31 @@ +// VulcanizeDB +// Copyright © 2018 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package shared + +type ValueType int + +const ( + Uint256 ValueType = iota + Bytes32 + Address +) + +type StorageValueMetadata struct { + Name string + Key string + Type ValueType +} diff --git a/pkg/transformers/storage_transformers.go b/pkg/transformers/storage_transformers.go new file mode 100644 index 00000000..ee811c66 --- /dev/null +++ b/pkg/transformers/storage_transformers.go @@ -0,0 +1,17 @@ +package transformers + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/vulcanize/vulcanizedb/pkg/transformers/factories/storage" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/constants" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker/pit" +) + +func GetPitStorageTransformer() storage.Transformer { + return storage.Transformer{ + Address: common.HexToAddress(constants.PitContractAddress()), + Mappings: &pit.PitMappings{StorageRepository: &maker.MakerStorageRepository{}}, + Repository: &pit.PitStorageRepository{}, + } +} diff --git a/pkg/transformers/test_data/bite.go b/pkg/transformers/test_data/bite.go index eb6ab6c7..f724d0b5 100644 --- a/pkg/transformers/test_data/bite.go +++ b/pkg/transformers/test_data/bite.go @@ -41,7 +41,7 @@ var ( biteRawJson, _ = json.Marshal(EthBiteLog) biteIlk = [32]byte{69, 84, 72, 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} biteLad = [32]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 180, 20, 126, 218, 128, 254, 199, 18, 42, 225, 109, 162, 71, 156, 189, 127, 251} - biteIlkString = "ETH" + biteIlkString = "4554480000000000000000000000000000000000000000000000000000000000" biteLadString = "0x0000d8b4147eDa80Fec7122AE16DA2479Cbd7ffB" ) diff --git a/pkg/transformers/test_data/cat_file.go b/pkg/transformers/test_data/cat_file.go index 6315cfe1..180262a6 100644 --- a/pkg/transformers/test_data/cat_file.go +++ b/pkg/transformers/test_data/cat_file.go @@ -45,7 +45,7 @@ var EthCatFileChopLog = types.Log{ } var rawCatFileChopLog, _ = json.Marshal(EthCatFileChopLog) var CatFileChopModel = chop_lump.CatFileChopLumpModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", What: "chop", Data: "123.456789012345680589533003513", TransactionIndex: EthCatFileChopLog.TxIndex, @@ -71,7 +71,7 @@ var EthCatFileLumpLog = types.Log{ } var rawCatFileLumpLog, _ = json.Marshal(EthCatFileLumpLog) var CatFileLumpModel = chop_lump.CatFileChopLumpModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", What: "lump", Data: "12345.678901234567092615", TransactionIndex: EthCatFileLumpLog.TxIndex, @@ -98,7 +98,7 @@ var EthCatFileFlipLog = types.Log{ var rawCatFileFlipLog, _ = json.Marshal(EthCatFileFlipLog) var CatFileFlipModel = flip.CatFileFlipModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", What: "flip", Flip: "0x07Fa9eF6609cA7921112231F8f195138ebbA2977", TransactionIndex: EthCatFileFlipLog.TxIndex, diff --git a/pkg/transformers/test_data/drip_drip.go b/pkg/transformers/test_data/drip_drip.go index 53754667..f87382e0 100644 --- a/pkg/transformers/test_data/drip_drip.go +++ b/pkg/transformers/test_data/drip_drip.go @@ -44,7 +44,7 @@ var EthDripDripLog = types.Log{ var rawDripDripLog, _ = json.Marshal(EthDripDripLog) var DripDripModel = drip_drip.DripDripModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", LogIndex: EthDripDripLog.Index, TransactionIndex: EthDripDripLog.TxIndex, Raw: rawDripDripLog, diff --git a/pkg/transformers/test_data/drip_file.go b/pkg/transformers/test_data/drip_file.go index ba0fe075..8203f07b 100644 --- a/pkg/transformers/test_data/drip_file.go +++ b/pkg/transformers/test_data/drip_file.go @@ -47,7 +47,7 @@ var EthDripFileIlkLog = types.Log{ var rawDripFileIlkLog, _ = json.Marshal(EthDripFileIlkLog) var DripFileIlkModel = ilk2.DripFileIlkModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", Vow: "fake vow", Tax: "12300.000000000000000000000000000", LogIndex: EthDripFileIlkLog.Index, diff --git a/pkg/transformers/test_data/frob.go b/pkg/transformers/test_data/frob.go index 7edf187b..f5dc873a 100644 --- a/pkg/transformers/test_data/frob.go +++ b/pkg/transformers/test_data/frob.go @@ -41,7 +41,7 @@ var ( frobLad = [32]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 217, 34, 137, 65, 83, 190, 158, 239, 123, 114, 24, 220, 86, 93, 29, 12, 226, 160, 146} ink = big.NewInt(15) ilk = [32]byte{102, 97, 107, 101, 32, 105, 108, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - frobIlkString = "fake ilk" + frobIlkString = "66616b6520696c6b000000000000000000000000000000000000000000000000" frobUrnString = "0x64d922894153BE9EEf7b7218dc565d1D0Ce2a092" ) diff --git a/pkg/transformers/test_data/mocks/mappings.go b/pkg/transformers/test_data/mocks/mappings.go new file mode 100644 index 00000000..18ab4a08 --- /dev/null +++ b/pkg/transformers/test_data/mocks/mappings.go @@ -0,0 +1,22 @@ +package mocks + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type MockMappings struct { + Metadata shared.StorageValueMetadata + LookupCalled bool + LookupErr error +} + +func (mappings *MockMappings) Lookup(key common.Hash) (shared.StorageValueMetadata, error) { + mappings.LookupCalled = true + return mappings.Metadata, mappings.LookupErr +} + +func (*MockMappings) SetDB(db *postgres.DB) { + panic("implement me") +} diff --git a/pkg/transformers/test_data/mocks/storage_repository.go b/pkg/transformers/test_data/mocks/storage_repository.go new file mode 100644 index 00000000..62e88186 --- /dev/null +++ b/pkg/transformers/test_data/mocks/storage_repository.go @@ -0,0 +1,26 @@ +package mocks + +import ( + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type MockStorageRepository struct { + CreateErr error + PassedBlockNumber int + PassedBlockHash string + PassedMetadata shared.StorageValueMetadata + PassedValue interface{} +} + +func (repository *MockStorageRepository) Create(blockNumber int, blockHash string, metadata shared.StorageValueMetadata, value interface{}) error { + repository.PassedBlockNumber = blockNumber + repository.PassedBlockHash = blockHash + repository.PassedMetadata = metadata + repository.PassedValue = value + return repository.CreateErr +} + +func (*MockStorageRepository) SetDB(db *postgres.DB) { + panic("implement me") +} diff --git a/pkg/transformers/test_data/mocks/storage_transformer.go b/pkg/transformers/test_data/mocks/storage_transformer.go new file mode 100644 index 00000000..34c00007 --- /dev/null +++ b/pkg/transformers/test_data/mocks/storage_transformer.go @@ -0,0 +1,27 @@ +package mocks + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared/storage" + "github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared" +) + +type MockStorageTransformer struct { + Address common.Address + ExecuteErr error + PassedRow shared.StorageDiffRow +} + +func (transformer *MockStorageTransformer) Execute(row shared.StorageDiffRow) error { + transformer.PassedRow = row + return transformer.ExecuteErr +} + +func (transformer *MockStorageTransformer) ContractAddress() common.Address { + return transformer.Address +} + +func (transformer *MockStorageTransformer) FakeTransformerInitializer(db *postgres.DB) storage.Transformer { + return transformer +} diff --git a/pkg/transformers/test_data/pit_file.go b/pkg/transformers/test_data/pit_file.go index f798b795..38895da9 100644 --- a/pkg/transformers/test_data/pit_file.go +++ b/pkg/transformers/test_data/pit_file.go @@ -75,7 +75,7 @@ var EthPitFileIlkLineLog = types.Log{ var rawPitFileIlkLineLog, _ = json.Marshal(EthPitFileIlkLineLog) var PitFileIlkLineModel = ilk2.PitFileIlkModel{ - Ilk: "fake ilk", + Ilk: "0x66616b6520696c6b000000000000000000000000000000000000000000000000", What: "line", Data: "0.000001000000000000", LogIndex: EthPitFileIlkLineLog.Index, @@ -102,7 +102,7 @@ var EthPitFileIlkSpotLog = types.Log{ var rawPitFileIlkSpotLog, _ = json.Marshal(EthPitFileIlkSpotLog) var PitFileIlkSpotModel = ilk2.PitFileIlkModel{ - Ilk: "fake ilk", + Ilk: "0x66616b6520696c6b000000000000000000000000000000000000000000000000", What: "spot", Data: "0.000000000000001000000000000", LogIndex: EthPitFileIlkSpotLog.Index, diff --git a/pkg/transformers/test_data/vat_flux.go b/pkg/transformers/test_data/vat_flux.go index 2a5654e7..543364f4 100644 --- a/pkg/transformers/test_data/vat_flux.go +++ b/pkg/transformers/test_data/vat_flux.go @@ -44,7 +44,7 @@ var VatFluxLog = types.Log{ var rawFluxLog, _ = json.Marshal(VatFluxLog) var VatFluxModel = vat_flux.VatFluxModel{ - Ilk: "REP", + Ilk: "5245500000000000000000000000000000000000000000000000000000000000", Src: "0x7FA9EF6609Ca7921112231f8f195138ebba29770", Dst: "0x93086347c52a8878af71bB818509d484c6a2e1bF", Rad: "123", diff --git a/pkg/transformers/test_data/vat_fold.go b/pkg/transformers/test_data/vat_fold.go index be7bf64e..d5357ace 100644 --- a/pkg/transformers/test_data/vat_fold.go +++ b/pkg/transformers/test_data/vat_fold.go @@ -46,7 +46,7 @@ var EthVatFoldLog = types.Log{ var rawVatFoldLog, _ = json.Marshal(EthVatFoldLog) var VatFoldModel = vat_fold.VatFoldModel{ - Ilk: "REP", + Ilk: "5245500000000000000000000000000000000000000000000000000000000000", Urn: "0x3728e9777B2a0a611ee0F89e00E01044ce4736d1", Rate: "0.000000000000000000000000002", LogIndex: EthVatFoldLog.Index, diff --git a/pkg/transformers/test_data/vat_grab.go b/pkg/transformers/test_data/vat_grab.go index f99adf87..5001095a 100644 --- a/pkg/transformers/test_data/vat_grab.go +++ b/pkg/transformers/test_data/vat_grab.go @@ -28,7 +28,7 @@ var EthVatGrabLog = types.Log{ var rawVatGrabLog, _ = json.Marshal(EthVatGrabLog) var VatGrabModel = vat_grab.VatGrabModel{ - Ilk: "REP", + Ilk: "5245500000000000000000000000000000000000000000000000000000000000", Urn: "0x6a3AE20C315E845B2E398e68EfFe39139eC6060C", V: "0x2F34f22a00eE4b7a8F8BBC4eAee1658774c624e0", W: "0x3728e9777B2a0a611ee0F89e00E01044ce4736d1", diff --git a/pkg/transformers/test_data/vat_init.go b/pkg/transformers/test_data/vat_init.go index d26d6311..bc566d24 100644 --- a/pkg/transformers/test_data/vat_init.go +++ b/pkg/transformers/test_data/vat_init.go @@ -46,7 +46,7 @@ var EthVatInitLog = types.Log{ var rawVatInitLog, _ = json.Marshal(EthVatInitLog) var VatInitModel = vat_init.VatInitModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", LogIndex: EthVatInitLog.Index, TransactionIndex: EthVatInitLog.TxIndex, Raw: rawVatInitLog, diff --git a/pkg/transformers/test_data/vat_slip.go b/pkg/transformers/test_data/vat_slip.go index 5b5efd9b..60e46a4b 100644 --- a/pkg/transformers/test_data/vat_slip.go +++ b/pkg/transformers/test_data/vat_slip.go @@ -46,7 +46,7 @@ var EthVatSlipLog = types.Log{ var rawVatSlipLog, _ = json.Marshal(EthVatSlipLog) var VatSlipModel = vat_slip.VatSlipModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", Guy: common.HexToAddress("0x7d7bEe5fCfD8028cf7b00876C5b1421c800561A6").String(), Rad: "987654321", TransactionIndex: EthVatSlipLog.TxIndex, diff --git a/pkg/transformers/test_data/vat_toll.go b/pkg/transformers/test_data/vat_toll.go index b1aed7fb..e3ee37d1 100644 --- a/pkg/transformers/test_data/vat_toll.go +++ b/pkg/transformers/test_data/vat_toll.go @@ -29,7 +29,7 @@ var EthVatTollLog = types.Log{ var rawVatTollLog, _ = json.Marshal(EthVatTollLog) var VatTollModel = vat_toll.VatTollModel{ - Ilk: "fake ilk", + Ilk: "66616b6520696c6b000000000000000000000000000000000000000000000000", Urn: "0xA3E37186E017747DbA34042e83e3F76Ad3CcE9b0", Take: big.NewInt(123456789).String(), TransactionIndex: EthVatTollLog.TxIndex, diff --git a/pkg/transformers/test_data/vat_tune.go b/pkg/transformers/test_data/vat_tune.go index 596bd7aa..0ad3b250 100644 --- a/pkg/transformers/test_data/vat_tune.go +++ b/pkg/transformers/test_data/vat_tune.go @@ -34,7 +34,7 @@ var dartString = "11579208923731619542357098500868790785326998466564056403945558 var vatTuneDart, _ = new(big.Int).SetString(dartString, 10) var urnAddress = "0x4F26FfBe5F04ED43630fdC30A87638d53D0b0876" var VatTuneModel = vat_tune.VatTuneModel{ - Ilk: "ETH", + Ilk: "4554480000000000000000000000000000000000000000000000000000000000", Urn: urnAddress, V: urnAddress, W: urnAddress, diff --git a/pkg/transformers/vat_flux/converter.go b/pkg/transformers/vat_flux/converter.go index cb2a35eb..a28f78d3 100644 --- a/pkg/transformers/vat_flux/converter.go +++ b/pkg/transformers/vat_flux/converter.go @@ -17,7 +17,6 @@ package vat_flux import ( - "bytes" "encoding/json" "errors" "github.com/ethereum/go-ethereum/common" @@ -36,7 +35,7 @@ func (VatFluxConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[1].Bytes()) src := common.BytesToAddress(ethLog.Topics[2].Bytes()) dst := common.BytesToAddress(ethLog.Topics[3].Bytes()) radBytes := shared.GetDataBytesAtIndex(-1, ethLog.Data) diff --git a/pkg/transformers/vat_fold/converter.go b/pkg/transformers/vat_fold/converter.go index dbfa8a8b..8d0db8a0 100644 --- a/pkg/transformers/vat_fold/converter.go +++ b/pkg/transformers/vat_fold/converter.go @@ -17,7 +17,6 @@ package vat_fold import ( - "bytes" "encoding/json" "errors" "github.com/ethereum/go-ethereum/common" @@ -35,7 +34,7 @@ func (VatFoldConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[1].Bytes()) urn := common.BytesToAddress(ethLog.Topics[2].Bytes()).String() rate := shared.ConvertToRay(ethLog.Topics[3].Big().String()) raw, err := json.Marshal(ethLog) diff --git a/pkg/transformers/vat_grab/converter.go b/pkg/transformers/vat_grab/converter.go index 9ed2d224..0123aa17 100644 --- a/pkg/transformers/vat_grab/converter.go +++ b/pkg/transformers/vat_grab/converter.go @@ -1,7 +1,6 @@ package vat_grab import ( - "bytes" "encoding/json" "errors" "github.com/ethereum/go-ethereum/common" @@ -20,7 +19,7 @@ func (VatGrabConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[1].Bytes()) urn := common.BytesToAddress(ethLog.Topics[2].Bytes()) v := common.BytesToAddress(ethLog.Topics[3].Bytes()) wBytes := shared.GetDataBytesAtIndex(-3, ethLog.Data) diff --git a/pkg/transformers/vat_init/converter.go b/pkg/transformers/vat_init/converter.go index d5efeb16..1b59c2c8 100644 --- a/pkg/transformers/vat_init/converter.go +++ b/pkg/transformers/vat_init/converter.go @@ -17,10 +17,10 @@ package vat_init import ( - "bytes" "encoding/json" "errors" "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" ) type VatInitConverter struct{} @@ -32,7 +32,7 @@ func (VatInitConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[1].Bytes()) raw, err := json.Marshal(ethLog) if err != nil { return nil, err diff --git a/pkg/transformers/vat_slip/converter.go b/pkg/transformers/vat_slip/converter.go index ec380c02..6258100e 100644 --- a/pkg/transformers/vat_slip/converter.go +++ b/pkg/transformers/vat_slip/converter.go @@ -17,11 +17,11 @@ package vat_slip import ( - "bytes" "encoding/json" "errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" ) type VatSlipConverter struct{} @@ -33,7 +33,7 @@ func (VatSlipConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[1].Bytes()) guy := common.BytesToAddress(ethLog.Topics[2].Bytes()) rad := ethLog.Topics[3].Big() diff --git a/pkg/transformers/vat_toll/converter.go b/pkg/transformers/vat_toll/converter.go index 7f99bb5c..42bb0b56 100644 --- a/pkg/transformers/vat_toll/converter.go +++ b/pkg/transformers/vat_toll/converter.go @@ -1,9 +1,9 @@ package vat_toll import ( - "bytes" "encoding/json" "errors" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -18,7 +18,7 @@ func (VatTollConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[1].Bytes()) urn := common.BytesToAddress(ethLog.Topics[2].Bytes()[:common.AddressLength]) take := ethLog.Topics[3].Big() diff --git a/pkg/transformers/vat_tune/converter.go b/pkg/transformers/vat_tune/converter.go index ecb7a233..6df0c1ea 100644 --- a/pkg/transformers/vat_tune/converter.go +++ b/pkg/transformers/vat_tune/converter.go @@ -1,7 +1,6 @@ package vat_tune import ( - "bytes" "encoding/json" "errors" "math/big" @@ -22,7 +21,7 @@ func (VatTuneConverter) ToModels(ethLogs []types.Log) ([]interface{}, error) { if err != nil { return nil, err } - ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) + ilk := shared.GetHexWithoutPrefix(ethLog.Topics[1].Bytes()) urn := common.BytesToAddress(ethLog.Topics[2].Bytes()) v := common.BytesToAddress(ethLog.Topics[3].Bytes()) wBytes := shared.GetDataBytesAtIndex(-3, ethLog.Data)