From be58dd4ac845e20b14245849af05069e5c509078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20H=C3=BCbinette?= Date: Fri, 12 Oct 2018 16:13:13 +0200 Subject: [PATCH] Add `vat_move` transformer (#47) * Add vat_move transformer base * Add vat_move migrations * Add test data for vat_move * Add vat_move transformer to initialisers * Add numeric cast to psql insert of Rad * Add new db schema * Dependency update * Expand abbreviation in repository * Add test suite for vat_move * Add header checking to transformer and mock repository * Remove trailing zero in test data * Fix minor mishaps * Go fmt nitpicking * Refactoring in tests * Add tests covering checked headers stuff (and fix revealed bugs) * go fmt fixes * Implement batching behaviour of transformer * Small fixes after review * Go fmt --- Gopkg.lock | 155 +++++++++++-- cmd/continuousLogSync.go | 1 + .../1538396815_create_vat_move.down.sql | 3 + .../1538396815_create_vat_move.up.sql | 13 ++ db/schema.sql | 67 ++++++ pkg/transformers/shared/constants.go | 2 + .../shared/event_signature_generator_test.go | 7 + pkg/transformers/shared/log_fetcher.go | 2 +- .../test_data/mocks/vat_move/converter.go | 35 +++ .../test_data/mocks/vat_move/repository.go | 65 ++++++ pkg/transformers/test_data/vat_move.go | 52 +++++ pkg/transformers/transformers.go | 3 + pkg/transformers/vat_move/config.go | 27 +++ pkg/transformers/vat_move/converter.go | 66 ++++++ pkg/transformers/vat_move/converter_test.go | 44 ++++ pkg/transformers/vat_move/model.go | 23 ++ pkg/transformers/vat_move/repository.go | 96 ++++++++ pkg/transformers/vat_move/repository_test.go | 192 ++++++++++++++++ pkg/transformers/vat_move/transformer.go | 83 +++++++ pkg/transformers/vat_move/transformer_test.go | 212 ++++++++++++++++++ .../vat_move/vat_move_suite_test.go | 33 +++ test_config/test_config.go | 1 + .../ens/contract/{ens.sol => ENS.sol} | 0 23 files changed, 1163 insertions(+), 19 deletions(-) create mode 100644 db/migrations/1538396815_create_vat_move.down.sql create mode 100644 db/migrations/1538396815_create_vat_move.up.sql create mode 100644 pkg/transformers/test_data/mocks/vat_move/converter.go create mode 100644 pkg/transformers/test_data/mocks/vat_move/repository.go create mode 100644 pkg/transformers/test_data/vat_move.go create mode 100644 pkg/transformers/vat_move/config.go create mode 100644 pkg/transformers/vat_move/converter.go create mode 100644 pkg/transformers/vat_move/converter_test.go create mode 100644 pkg/transformers/vat_move/model.go create mode 100644 pkg/transformers/vat_move/repository.go create mode 100644 pkg/transformers/vat_move/repository_test.go create mode 100644 pkg/transformers/vat_move/transformer.go create mode 100644 pkg/transformers/vat_move/transformer_test.go create mode 100644 pkg/transformers/vat_move/vat_move_suite_test.go rename vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/{ens.sol => ENS.sol} (100%) diff --git a/Gopkg.lock b/Gopkg.lock index 3928ef61..be36d73a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,23 +3,30 @@ [[projects]] branch = "master" + digest = "1:a313376bcbcce8ae8bddb8089a7293e0473a0f8e9e3710d6244e09e81875ccf0" name = "github.com/aristanetworks/goarista" packages = ["monotime"] + pruneopts = "" revision = "ff33da284e760fcdb03c33d37a719e5ed30ba844" [[projects]] branch = "master" + digest = "1:c6bf1ac7bbc0fe51637bf54d5a88ff79b171b3b42dbc665dec98303c862d8662" name = "github.com/btcsuite/btcd" packages = ["btcec"] + pruneopts = "" revision = "cff30e1d23fc9e800b2b5b4b41ef1817dda07e9f" [[projects]] + digest = "1:aaeffbff5bd24654cb4c190ed75d6c7b57b4f5d6741914c1a7a6bb7447e756c5" name = "github.com/deckarep/golang-set" packages = ["."] + pruneopts = "" revision = "cbaa98ba5575e67703b32b4b19f73c91f3c4159e" version = "v1.7.1" [[projects]] + digest = "1:8acdd83e35b407d026e546bf4d18cc778e9604433d03d468eccca62b9c51c718" name = "github.com/ethereum/go-ethereum" packages = [ ".", @@ -50,42 +57,54 @@ "params", "rlp", "rpc", - "trie" + "trie", ] + pruneopts = "" revision = "89451f7c382ad2185987ee369f16416f89c28a7d" version = "v1.8.15" [[projects]] + digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" name = "github.com/fsnotify/fsnotify" packages = ["."] + pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" version = "v1.4.7" [[projects]] + digest = "1:a01080d20c45c031c13f3828c56e58f4f51d926a482ad10cc0316225097eb7ea" name = "github.com/go-stack/stack" packages = ["."] + pruneopts = "" revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" version = "v1.8.0" [[projects]] + digest = "1:3dd078fda7500c341bc26cfbc6c6a34614f295a2457149fc1045cab767cbcf18" name = "github.com/golang/protobuf" packages = ["proto"] + pruneopts = "" revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:2a5888946cdbc8aa360fd43301f9fc7869d663f60d5eedae7d4e6e5e4f06f2bf" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] + digest = "1:5247b135b5492aa232a731acdcb52b08f32b874cb398f21ab460396eadbe866b" name = "github.com/google/uuid" packages = ["."] + pruneopts = "" revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" version = "v1.0.0" [[projects]] + digest = "1:d14365c51dd1d34d5c79833ec91413bfbb166be978724f15701e17080dc06dec" name = "github.com/hashicorp/hcl" packages = [ ".", @@ -97,25 +116,29 @@ "hcl/token", "json/parser", "json/scanner", - "json/token" + "json/token", ] + pruneopts = "" revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" version = "v1.0.0" [[projects]] + digest = "1:b3c5b95e56c06f5aa72cb2500e6ee5f44fcd122872d4fec2023a488e561218bc" name = "github.com/hpcloud/tail" packages = [ ".", "ratelimiter", "util", "watch", - "winfile" + "winfile", ] + pruneopts = "" revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:b6e4cc26365c004808649862e22069de09594a9222143399a7a04904e9f7018c" name = "github.com/huin/goupnp" packages = [ ".", @@ -124,59 +147,75 @@ "httpu", "scpd", "soap", - "ssdp" + "ssdp", ] + pruneopts = "" revision = "1395d1447324cbea88d249fbfcfd70ea878fdfca" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] + digest = "1:76f836364ae83ed811c415aa92e1209ce49de9f62aad85b85fca749a8b96a110" name = "github.com/jackpal/go-nat-pmp" packages = ["."] + pruneopts = "" revision = "c9cfead9f2a36ddf3daa40ba269aa7f4bbba6b62" version = "v1.0.1" [[projects]] branch = "master" + digest = "1:617ee2434b77e911fa26b678730be9a617f75243b194eadc8201c8ac860844aa" name = "github.com/jmoiron/sqlx" packages = [ ".", - "reflectx" + "reflectx", ] + pruneopts = "" revision = "0dae4fefe7c0e190f7b5a78dac28a1c82cc8d849" [[projects]] + branch = "master" + digest = "1:29145d7af4adafd72a79df5e41456ac9e232d5a28c1cd4dacf3ff008a217fc10" name = "github.com/lib/pq" packages = [ ".", - "oid" + "oid", ] + pruneopts = "" revision = "4ded0e9383f75c197b3a2aaa6d590ac52df6fd79" - version = "v1.0.0" [[projects]] + digest = "1:961dc3b1d11f969370533390fdf203813162980c858e1dabe827b60940c909a5" name = "github.com/magiconair/properties" packages = ["."] + pruneopts = "" revision = "c2353362d570a7bfa228149c62842019201cfb71" version = "v1.8.0" [[projects]] + digest = "1:096a8a9182648da3d00ff243b88407838902b6703fc12657f76890e08d1899bf" name = "github.com/mitchellh/go-homedir" packages = ["."] + pruneopts = "" revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4" version = "v1.0.0" [[projects]] + digest = "1:5219b4506253ccc598f9340677162a42d6a78f340a4cc6df2d62db4d0593c4e9" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "" revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8" version = "v1.0.0" [[projects]] + digest = "1:a7fd918fb5bd2188436785c0424f8a50b4addfedf37a2b14d796be2a927b8007" name = "github.com/onsi/ginkgo" packages = [ ".", @@ -196,12 +235,14 @@ "reporters/stenographer", "reporters/stenographer/support/go-colorable", "reporters/stenographer/support/go-isatty", - "types" + "types", ] + pruneopts = "" revision = "3774a09d95489ccaa16032e0770d08ea77ba6184" version = "v1.6.0" [[projects]] + digest = "1:3ecd0a37c4a90c12a97e31c398cdbc173824351aa891898ee178120bfe71c478" name = "github.com/onsi/gomega" packages = [ ".", @@ -216,82 +257,106 @@ "matchers/support/goraph/edge", "matchers/support/goraph/node", "matchers/support/goraph/util", - "types" + "types", ] + pruneopts = "" revision = "7615b9433f86a8bdf29709bf288bc4fd0636a369" version = "v1.4.2" [[projects]] + digest = "1:a5484d4fa43127138ae6e7b2299a6a52ae006c7f803d98d717f60abf3e97192e" name = "github.com/pborman/uuid" packages = ["."] + pruneopts = "" revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" version = "v1.2" [[projects]] + digest = "1:894aef961c056b6d85d12bac890bf60c44e99b46292888bfa66caf529f804457" name = "github.com/pelletier/go-toml" packages = ["."] + pruneopts = "" revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" version = "v1.2.0" [[projects]] + digest = "1:210286d0cb60ffe28f1ca00b664029e8943009f95d06d8f8c336301b28e1aee5" name = "github.com/philhofer/fwd" packages = ["."] + pruneopts = "" revision = "bb6d471dc95d4fe11e432687f8b70ff496cf3136" version = "v1.0.0" [[projects]] + digest = "1:7143292549152d009ca9e9c493b74736a2ebd93f921bea8a4b308d7cc5edc6b3" name = "github.com/rjeczalik/notify" packages = ["."] + pruneopts = "" revision = "0f065fa99b48b842c3fd3e2c8b194c6f2b69f6b8" version = "v0.9.1" [[projects]] + digest = "1:78c9cf43ddeacd0e472f412082227a0fac2ae107ee60e9112156f9371f9912cf" name = "github.com/rs/cors" packages = ["."] + pruneopts = "" revision = "3fb1b69b103a84de38a19c3c6ec073dd6caa4d3f" version = "v1.5.0" [[projects]] + digest = "1:d0431c2fd72e39ee43ea7742322abbc200c3e704c9102c5c3c2e2e667095b0ca" name = "github.com/spf13/afero" packages = [ ".", - "mem" + "mem", ] + pruneopts = "" revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" version = "v1.1.2" [[projects]] + digest = "1:d0b38ba6da419a6d4380700218eeec8623841d44a856bb57369c172fbf692ab4" name = "github.com/spf13/cast" packages = ["."] + pruneopts = "" revision = "8965335b8c7107321228e3e3702cab9832751bac" version = "v1.2.0" [[projects]] + digest = "1:a1403cc8a94b8d7956ee5e9694badef0e7b051af289caad1cf668331e3ffa4f6" name = "github.com/spf13/cobra" packages = ["."] + pruneopts = "" revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" version = "v0.0.3" [[projects]] + digest = "1:9ceffa4ab5f7195ecf18b3a7fff90c837a9ed5e22e66d18069e4bccfe1f52aa0" name = "github.com/spf13/jwalterweatherman" packages = ["."] + pruneopts = "" revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" version = "v1.0.0" [[projects]] + digest = "1:0a52bcb568386d98f4894575d53ce3e456f56471de6897bb8b9de13c33d9340e" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "" revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" version = "v1.0.2" [[projects]] + digest = "1:ac25ea6cc1156aca9611411274b4a0bdd83a623845df6985aab508253955cc66" name = "github.com/spf13/viper" packages = ["."] + pruneopts = "" revision = "8fb642006536c8d3760c99d4fa2389f5e2205631" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:ce5194e5afac308cc34e500cab45b4ce88a0742d689e3cf7e37b607ad76bed2f" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -305,53 +370,65 @@ "leveldb/opt", "leveldb/storage", "leveldb/table", - "leveldb/util" + "leveldb/util", ] + pruneopts = "" revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd" [[projects]] + digest = "1:cae65c1f3471b1f456a9d8a160440f4824ad50c718a15da4144490fbc3b12e49" name = "github.com/tinylib/msgp" packages = ["msgp"] + pruneopts = "" revision = "b2b6a672cf1e5b90748f79b8b81fc8c5cf0571a1" version = "1.0.2" [[projects]] branch = "master" + digest = "1:61a86f0be8b466d6e3fbdabb155aaa4006137cb5e3fd3b949329d103fa0ceb0f" name = "golang.org/x/crypto" packages = [ "pbkdf2", - "scrypt" + "scrypt", ] + pruneopts = "" revision = "0e37d006457bf46f9e6692014ba72ef82c33022c" [[projects]] branch = "master" + digest = "1:fbdbb6cf8db3278412c9425ad78b26bb8eb788181f26a3ffb3e4f216b314f86a" name = "golang.org/x/net" packages = [ "context", "html", "html/atom", "html/charset", - "websocket" + "websocket", ] + pruneopts = "" revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" [[projects]] branch = "master" + digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1" name = "golang.org/x/sync" packages = ["errgroup"] + pruneopts = "" revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" [[projects]] branch = "master" + digest = "1:70d519d5cddeb60ceda2db88c24c340b1b2d7efb25ab54bacb38f57ea1998df7" name = "golang.org/x/sys" packages = [ "unix", - "windows" + "windows", ] + pruneopts = "" revision = "d641721ec2dead6fe5ca284096fe4b1fcd49e427" [[projects]] + digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" name = "golang.org/x/text" packages = [ "encoding", @@ -373,66 +450,108 @@ "runes", "transform", "unicode/cldr", - "unicode/norm" + "unicode/norm", ] + pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:88401f863a34cfd82173ef2ad015652c2c47c866affc424bfdd4d5fafad1dd53" name = "golang.org/x/tools" packages = [ "go/ast/astutil", "imports", - "internal/fastwalk" + "internal/fastwalk", ] + pruneopts = "" revision = "90fa682c2a6e6a37b3a1364ce2fe1d5e41af9d6d" [[projects]] + digest = "1:be560af8c72c59788a6748eae60b8e1fe136fefdf049b7dfb6f5b591ea337984" name = "gopkg.in/DataDog/dd-trace-go.v1" packages = [ "ddtrace", "ddtrace/ext", "ddtrace/internal", - "ddtrace/tracer" + "ddtrace/tracer", ] + pruneopts = "" revision = "bcd20367df871708a36549e7fe36183ee5b4fc55" version = "v1.3.0" [[projects]] + digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" name = "gopkg.in/fsnotify.v1" packages = ["."] + pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" source = "gopkg.in/fsnotify/fsnotify.v1" version = "v1.4.7" [[projects]] branch = "v2" + digest = "1:a585c075875ab9c344f7840a927f09f3285563f7318e761a1d61d642316f2217" name = "gopkg.in/karalabe/cookiejar.v2" packages = ["collections/prque"] + pruneopts = "" revision = "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57" [[projects]] branch = "v2" + digest = "1:4f830ee018eb8c56d0def653ad7c9a1d2a053f0cef2ac6b2200f73b98fa6a681" name = "gopkg.in/natefinch/npipe.v2" packages = ["."] + pruneopts = "" revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6" [[projects]] branch = "v1" + digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd" name = "gopkg.in/tomb.v1" packages = ["."] + pruneopts = "" revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" [[projects]] + digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "fe44a35c252e3af131cb6073e683761afa0f3aa0f2d49d468520bfd6e453c5f8" + input-imports = [ + "github.com/ethereum/go-ethereum", + "github.com/ethereum/go-ethereum/accounts/abi", + "github.com/ethereum/go-ethereum/accounts/abi/bind", + "github.com/ethereum/go-ethereum/common", + "github.com/ethereum/go-ethereum/common/hexutil", + "github.com/ethereum/go-ethereum/core/rawdb", + "github.com/ethereum/go-ethereum/core/types", + "github.com/ethereum/go-ethereum/crypto", + "github.com/ethereum/go-ethereum/ethclient", + "github.com/ethereum/go-ethereum/ethdb", + "github.com/ethereum/go-ethereum/p2p", + "github.com/ethereum/go-ethereum/p2p/discover", + "github.com/ethereum/go-ethereum/params", + "github.com/ethereum/go-ethereum/rlp", + "github.com/ethereum/go-ethereum/rpc", + "github.com/jmoiron/sqlx", + "github.com/lib/pq", + "github.com/mitchellh/go-homedir", + "github.com/onsi/ginkgo", + "github.com/onsi/gomega", + "github.com/onsi/gomega/ghttp", + "github.com/spf13/cobra", + "github.com/spf13/viper", + "golang.org/x/net/context", + "golang.org/x/sync/errgroup", + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/continuousLogSync.go b/cmd/continuousLogSync.go index 6b4a3138..fefd665d 100644 --- a/cmd/continuousLogSync.go +++ b/cmd/continuousLogSync.go @@ -106,6 +106,7 @@ func buildTransformerInitializerMap() map[string]shared2.TransformerInitializer transformerInitializerMap["tend"] = transformers.TendTransformerInitializer transformerInitializerMap["vatGrab"] = transformers.VatGrabTransformerInitializer transformerInitializerMap["vatInit"] = transformers.VatInitTransformerInitializer + transformerInitializerMap["vatMove"] = transformers.VatMoveTransformerInitializer transformerInitializerMap["vatHeal"] = transformers.VatHealTransformerInitializer transformerInitializerMap["vatFold"] = transformers.VatFoldTransformerInitializer transformerInitializerMap["vatToll"] = transformers.VatTollTransformerInitializer diff --git a/db/migrations/1538396815_create_vat_move.down.sql b/db/migrations/1538396815_create_vat_move.down.sql new file mode 100644 index 00000000..2366c0ef --- /dev/null +++ b/db/migrations/1538396815_create_vat_move.down.sql @@ -0,0 +1,3 @@ +DROP TABLE maker.vat_move; +ALTER TABLE public.checked_headers + DROP COLUMN vat_move_checked; \ No newline at end of file diff --git a/db/migrations/1538396815_create_vat_move.up.sql b/db/migrations/1538396815_create_vat_move.up.sql new file mode 100644 index 00000000..60aaa703 --- /dev/null +++ b/db/migrations/1538396815_create_vat_move.up.sql @@ -0,0 +1,13 @@ +CREATE TABLE maker.vat_move ( + id SERIAL PRIMARY KEY, + header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, + src TEXT NOT NULL, + dst TEXT NOT NULL, + rad NUMERIC NOT NULL, + tx_idx INTEGER NOT NULL, + raw_log JSONB, + UNIQUE (header_id, tx_idx) +); + +ALTER TABLE public.checked_headers + ADD COLUMN vat_move_checked BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/db/schema.sql b/db/schema.sql index fdd64650..8bd1fe71 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -838,6 +838,41 @@ CREATE SEQUENCE maker.vat_init_id_seq ALTER SEQUENCE maker.vat_init_id_seq OWNED BY maker.vat_init.id; +-- +-- Name: vat_move; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.vat_move ( + id integer NOT NULL, + header_id integer NOT NULL, + src text NOT NULL, + dst text NOT NULL, + rad numeric NOT NULL, + tx_idx integer NOT NULL, + raw_log jsonb +); + + +-- +-- Name: vat_move_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.vat_move_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: vat_move_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.vat_move_id_seq OWNED BY maker.vat_move.id; + + -- -- Name: vat_toll; Type: TABLE; Schema: maker; Owner: - -- @@ -1012,6 +1047,7 @@ CREATE TABLE public.checked_headers ( pit_file_ilk_checked boolean DEFAULT false NOT NULL, pit_file_stability_fee_checked boolean DEFAULT false NOT NULL, vat_init_checked boolean DEFAULT false NOT NULL, + vat_move_checked boolean DEFAULT false NOT NULL, vat_fold_checked boolean DEFAULT false NOT NULL, vat_heal_checked boolean DEFAULT false NOT NULL, vat_toll_checked boolean DEFAULT false NOT NULL, @@ -1491,6 +1527,13 @@ ALTER TABLE ONLY maker.vat_heal ALTER COLUMN id SET DEFAULT nextval('maker.vat_h ALTER TABLE ONLY maker.vat_init ALTER COLUMN id SET DEFAULT nextval('maker.vat_init_id_seq'::regclass); +-- +-- Name: vat_move id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.vat_move ALTER COLUMN id SET DEFAULT nextval('maker.vat_move_id_seq'::regclass); + + -- -- Name: vat_toll id; Type: DEFAULT; Schema: maker; Owner: - -- @@ -1927,6 +1970,22 @@ ALTER TABLE ONLY maker.vat_init ADD CONSTRAINT vat_init_pkey PRIMARY KEY (id); +-- +-- Name: vat_move vat_move_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.vat_move + ADD CONSTRAINT vat_move_header_id_tx_idx_key UNIQUE (header_id, tx_idx); + + +-- +-- Name: vat_move vat_move_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.vat_move + ADD CONSTRAINT vat_move_pkey PRIMARY KEY (id); + + -- -- Name: vat_toll vat_toll_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - -- @@ -2289,6 +2348,14 @@ ALTER TABLE ONLY maker.vat_init ADD CONSTRAINT vat_init_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; +-- +-- Name: vat_move vat_move_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.vat_move + ADD CONSTRAINT vat_move_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; + + -- -- Name: vat_toll vat_toll_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - -- diff --git a/pkg/transformers/shared/constants.go b/pkg/transformers/shared/constants.go index 82b82834..40234ff6 100644 --- a/pkg/transformers/shared/constants.go +++ b/pkg/transformers/shared/constants.go @@ -58,6 +58,7 @@ var ( vatHealMethod = GetSolidityMethodSignature(VatABI, "heal") vatGrabMethod = GetSolidityMethodSignature(VatABI, "grab") vatInitMethod = GetSolidityMethodSignature(VatABI, "init") + vatMoveMethod = GetSolidityMethodSignature(VatABI, "move") vatFoldMethod = GetSolidityMethodSignature(VatABI, "fold") vatTollMethod = GetSolidityMethodSignature(VatABI, "toll") vatTuneMethod = GetSolidityMethodSignature(VatABI, "tune") @@ -83,6 +84,7 @@ var ( VatHealSignature = GetLogNoteSignature(vatHealMethod) VatGrabSignature = GetLogNoteSignature(vatGrabMethod) VatInitSignature = GetLogNoteSignature(vatInitMethod) + VatMoveSignature = GetLogNoteSignature(vatMoveMethod) VatFoldSignature = GetLogNoteSignature(vatFoldMethod) VatTollSignature = GetLogNoteSignature(vatTollMethod) VatTuneSignature = GetLogNoteSignature(vatTuneMethod) diff --git a/pkg/transformers/shared/event_signature_generator_test.go b/pkg/transformers/shared/event_signature_generator_test.go index c040ebdf..7c96cf99 100644 --- a/pkg/transformers/shared/event_signature_generator_test.go +++ b/pkg/transformers/shared/event_signature_generator_test.go @@ -146,6 +146,13 @@ var _ = Describe("Event signature generator", func() { Expect(expected).To(Equal(actual)) }) + + It("gets the vat move method signature", func() { + expected := "move(bytes32,bytes32,int256)" + actual := shared.GetSolidityMethodSignature(shared.VatABI, "move") + + Expect(expected).To(Equal(actual)) + }) }) Describe("it handles events", func() { diff --git a/pkg/transformers/shared/log_fetcher.go b/pkg/transformers/shared/log_fetcher.go index c1fc3015..a42a18ff 100644 --- a/pkg/transformers/shared/log_fetcher.go +++ b/pkg/transformers/shared/log_fetcher.go @@ -58,4 +58,4 @@ func hexStringsToAddresses(hexStrings []string) []common.Address { } return addresses -} \ No newline at end of file +} diff --git a/pkg/transformers/test_data/mocks/vat_move/converter.go b/pkg/transformers/test_data/mocks/vat_move/converter.go new file mode 100644 index 00000000..b11d1830 --- /dev/null +++ b/pkg/transformers/test_data/mocks/vat_move/converter.go @@ -0,0 +1,35 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move + +import ( + "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move" +) + +type MockVatMoveConverter struct { + converterError error + PassedLogs []types.Log +} + +func (converter *MockVatMoveConverter) ToModels(ethLogs []types.Log) ([]vat_move.VatMoveModel, error) { + converter.PassedLogs = ethLogs + return []vat_move.VatMoveModel{test_data.VatMoveModel}, converter.converterError +} + +func (converter *MockVatMoveConverter) SetConverterError(e error) { + converter.converterError = e +} diff --git a/pkg/transformers/test_data/mocks/vat_move/repository.go b/pkg/transformers/test_data/mocks/vat_move/repository.go new file mode 100644 index 00000000..20d578e7 --- /dev/null +++ b/pkg/transformers/test_data/mocks/vat_move/repository.go @@ -0,0 +1,65 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move" +) + +type MockVatMoveRepository struct { + createError error + missingHeaders []core.Header + missingHeadersError error + PassedStartingBlockNumber int64 + PassedEndingBlockNumber int64 + PassedHeaderID int64 + PassedModels []vat_move.VatMoveModel + CheckedHeaderIDs []int64 + CheckedHeaderError error +} + +func (repository *MockVatMoveRepository) Create(headerID int64, models []vat_move.VatMoveModel) error { + repository.PassedHeaderID = headerID + repository.PassedModels = models + return repository.createError +} + +func (repository *MockVatMoveRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + repository.PassedStartingBlockNumber = startingBlockNumber + repository.PassedEndingBlockNumber = endingBlockNumber + return repository.missingHeaders, repository.missingHeadersError +} + +func (repository *MockVatMoveRepository) SetMissingHeadersError(e error) { + repository.missingHeadersError = e +} + +func (repository *MockVatMoveRepository) SetMissingHeaders(headers []core.Header) { + repository.missingHeaders = headers +} + +func (repository *MockVatMoveRepository) SetCreateError(e error) { + repository.createError = e +} + +func (repository *MockVatMoveRepository) MarkHeaderChecked(headerId int64) error { + repository.CheckedHeaderIDs = append(repository.CheckedHeaderIDs, headerId) + return repository.CheckedHeaderError +} + +func (repository *MockVatMoveRepository) SetCheckedHeaderError(e error) { + repository.CheckedHeaderError = e +} diff --git a/pkg/transformers/test_data/vat_move.go b/pkg/transformers/test_data/vat_move.go new file mode 100644 index 00000000..bd91cb4e --- /dev/null +++ b/pkg/transformers/test_data/vat_move.go @@ -0,0 +1,52 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test_data + +import ( + "encoding/json" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +var EthVatMoveLog = types.Log{ + Address: common.HexToAddress(shared.VatContractAddress), + Topics: []common.Hash{ + common.HexToHash("0x78f1947000000000000000000000000000000000000000000000000000000000"), + common.HexToHash("0xa730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000"), + common.HexToHash("0xb730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000"), + common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000002a"), + }, + Data: hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006478f19470a730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000b730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a"), + BlockNumber: 10, + TxHash: common.HexToHash("0xe8f39fbb7fea3621f543868f19b1114e305aff6a063a30d32835ff1012526f91"), + TxIndex: 7, + BlockHash: common.HexToHash("0xe3dd2e05bd8b92833e20ed83e2171bbc06a9ec823232eca1730a807bd8f5edc0"), + Index: 0, + Removed: false, +} + +var rawVatMoveLog, _ = json.Marshal(EthVatMoveLog) +var VatMoveModel = vat_move.VatMoveModel{ + Src: "0xA730d1FF8B6Bc74a26d54c20a9dda539909BaB0e", + Dst: "0xB730D1fF8b6BC74a26D54c20a9ddA539909BAb0e", + Rad: "42", + TransactionIndex: EthVatMoveLog.TxIndex, + Raw: rawVatMoveLog, +} diff --git a/pkg/transformers/transformers.go b/pkg/transformers/transformers.go index d8a2058c..e13e6c59 100644 --- a/pkg/transformers/transformers.go +++ b/pkg/transformers/transformers.go @@ -41,6 +41,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_grab" "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_heal" "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_init" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move" "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_toll" "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_tune" ) @@ -69,6 +70,7 @@ var ( TendTransformerInitializer = tend.TendTransformerInitializer{Config: tend.TendConfig}.NewTendTransformer VatGrabTransformerInitializer = vat_grab.VatGrabTransformerInitializer{Config: vat_grab.VatGrabConfig}.NewVatGrabTransformer VatInitTransformerInitializer = vat_init.VatInitTransformerInitializer{Config: vat_init.VatInitConfig}.NewVatInitTransformer + VatMoveTransformerInitializer = vat_move.VatMoveTransformerInitializer{Config: vat_move.VatMoveConfig}.NewVatMoveTransformer VatHealTransformerInitializer = vat_heal.VatHealTransformerInitializer{Config: vat_heal.VatHealConfig}.NewVatHealTransformer VatFoldTransformerInitializer = vat_fold.VatFoldTransformerInitializer{Config: vat_fold.VatFoldConfig}.NewVatFoldTransformer VatTollTransformerInitializer = vat_toll.VatTollTransformerInitializer{Config: vat_toll.VatTollConfig}.NewVatTollTransformer @@ -97,6 +99,7 @@ func TransformerInitializers() []shared.TransformerInitializer { TendTransformerInitializer, VatGrabTransformerInitializer, VatInitTransformerInitializer, + VatMoveTransformerInitializer, VatHealTransformerInitializer, VatFoldTransformerInitializer, VatTollTransformerInitializer, diff --git a/pkg/transformers/vat_move/config.go b/pkg/transformers/vat_move/config.go new file mode 100644 index 00000000..32077783 --- /dev/null +++ b/pkg/transformers/vat_move/config.go @@ -0,0 +1,27 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move + +import ( + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +var VatMoveConfig = shared.TransformerConfig{ + ContractAddresses: []string{shared.VatContractAddress}, + ContractAbi: shared.VatABI, + Topics: []string{shared.VatMoveSignature}, + StartingBlockNumber: 0, + EndingBlockNumber: 10000000, +} diff --git a/pkg/transformers/vat_move/converter.go b/pkg/transformers/vat_move/converter.go new file mode 100644 index 00000000..528dcc3c --- /dev/null +++ b/pkg/transformers/vat_move/converter.go @@ -0,0 +1,66 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move + +import ( + "encoding/json" + "errors" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type Converter interface { + ToModels(ethLog []types.Log) ([]VatMoveModel, error) +} + +type VatMoveConverter struct{} + +func (VatMoveConverter) ToModels(ethLogs []types.Log) ([]VatMoveModel, error) { + var models []VatMoveModel + for _, ethLog := range ethLogs { + err := verifyLog(ethLog) + if err != nil { + return []VatMoveModel{}, err + } + + src := common.BytesToAddress(ethLog.Topics[1].Bytes()[:common.AddressLength]) + dst := common.BytesToAddress(ethLog.Topics[2].Bytes()[:common.AddressLength]) + rad := ethLog.Topics[3].Big() + raw, err := json.Marshal(ethLog) + if err != nil { + return []VatMoveModel{}, err + } + + models = append(models, VatMoveModel{ + Src: src.String(), + Dst: dst.String(), + Rad: rad.String(), + TransactionIndex: ethLog.TxIndex, + Raw: raw, + }) + } + + return models, nil +} + +func verifyLog(ethLog types.Log) error { + if len(ethLog.Data) <= 0 { + return errors.New("log data is empty") + } + if len(ethLog.Topics) < 4 { + return errors.New("log missing topics") + } + return nil +} diff --git a/pkg/transformers/vat_move/converter_test.go b/pkg/transformers/vat_move/converter_test.go new file mode 100644 index 00000000..b4246865 --- /dev/null +++ b/pkg/transformers/vat_move/converter_test.go @@ -0,0 +1,44 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move_test + +import ( + "github.com/ethereum/go-ethereum/core/types" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move" +) + +var _ = Describe("Vat move converter", func() { + It("returns err if logs are missing topics", func() { + converter := vat_move.VatMoveConverter{} + badLog := types.Log{} + + _, err := converter.ToModels([]types.Log{badLog}) + + Expect(err).To(HaveOccurred()) + }) + + It("converts a log to a model", func() { + converter := vat_move.VatMoveConverter{} + + models, err := converter.ToModels([]types.Log{test_data.EthVatMoveLog}) + + Expect(err).NotTo(HaveOccurred()) + Expect(models[0]).To(Equal(test_data.VatMoveModel)) + }) +}) diff --git a/pkg/transformers/vat_move/model.go b/pkg/transformers/vat_move/model.go new file mode 100644 index 00000000..cedf8e79 --- /dev/null +++ b/pkg/transformers/vat_move/model.go @@ -0,0 +1,23 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move + +type VatMoveModel struct { + Src string + Dst string + Rad string + TransactionIndex uint `db:"tx_idx"` + Raw []byte `db:"raw_log"` +} diff --git a/pkg/transformers/vat_move/repository.go b/pkg/transformers/vat_move/repository.go new file mode 100644 index 00000000..821c36d5 --- /dev/null +++ b/pkg/transformers/vat_move/repository.go @@ -0,0 +1,96 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" +) + +type Repository interface { + Create(headerID int64, models []VatMoveModel) error + MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) + MarkHeaderChecked(headerID int64) error +} + +type VatMoveRepository struct { + db *postgres.DB +} + +func NewVatMoveRepository(db *postgres.DB) VatMoveRepository { + return VatMoveRepository{ + db: db, + } +} + +func (repository VatMoveRepository) Create(headerID int64, models []VatMoveModel) error { + tx, err := repository.db.Begin() + if err != nil { + return err + } + + for _, vatMove := range models { + _, err = tx.Exec( + `INSERT INTO maker.vat_move (header_id, src, dst, rad, tx_idx, raw_log) + VALUES ($1, $2, $3, $4::NUMERIC, $5, $6)`, + headerID, vatMove.Src, vatMove.Dst, vatMove.Rad, vatMove.TransactionIndex, vatMove.Raw, + ) + + if err != nil { + tx.Rollback() + return err + } + } + + _, err = tx.Exec( + `INSERT INTO public.checked_headers (header_id, vat_move_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET vat_move_checked = $2`, + headerID, true) + + if err != nil { + tx.Rollback() + return err + } + + return tx.Commit() +} + +func (repository VatMoveRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + var result []core.Header + err := repository.db.Select( + &result, + `SELECT headers.id, headers.block_number FROM headers + LEFT JOIN checked_headers on headers.id = header_id + WHERE (header_id ISNULL OR vat_move_checked IS FALSE) + AND headers.block_number >= $1 + AND headers.block_number <= $2 + AND headers.eth_node_fingerprint = $3`, + startingBlockNumber, + endingBlockNumber, + repository.db.Node.ID, + ) + + return result, err +} + +func (repository VatMoveRepository) MarkHeaderChecked(headerID int64) error { + _, err := repository.db.Exec(`INSERT INTO public.checked_headers (header_id, vat_move_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET vat_move_checked = $2`, headerID, true) + return err +} diff --git a/pkg/transformers/vat_move/repository_test.go b/pkg/transformers/vat_move/repository_test.go new file mode 100644 index 00000000..635b666b --- /dev/null +++ b/pkg/transformers/vat_move/repository_test.go @@ -0,0 +1,192 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move_test + +import ( + "database/sql" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move" + "github.com/vulcanize/vulcanizedb/test_config" + "math/rand" +) + +var _ = Describe("Vat Move", func() { + var db *postgres.DB + var headerRepository repositories.HeaderRepository + var vatMoveRepository vat_move.VatMoveRepository + + BeforeEach(func() { + db = test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository = repositories.NewHeaderRepository(db) + vatMoveRepository = vat_move.NewVatMoveRepository(db) + }) + + Describe("Create", func() { + var headerID int64 + var err error + + BeforeEach(func() { + headerID, err = headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + err = vatMoveRepository.Create(headerID, []vat_move.VatMoveModel{test_data.VatMoveModel}) + Expect(err).NotTo(HaveOccurred()) + }) + + It("adds a vat move event", func() { + var dbVatMove vat_move.VatMoveModel + err = db.Get(&dbVatMove, `SELECT src, dst, rad, tx_idx, raw_log FROM maker.vat_move WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(dbVatMove.Src).To(Equal(test_data.VatMoveModel.Src)) + Expect(dbVatMove.Dst).To(Equal(test_data.VatMoveModel.Dst)) + Expect(dbVatMove.Rad).To(Equal(test_data.VatMoveModel.Rad)) + Expect(dbVatMove.TransactionIndex).To(Equal(test_data.VatMoveModel.TransactionIndex)) + Expect(dbVatMove.Raw).To(MatchJSON(test_data.VatMoveModel.Raw)) + }) + + It("marks header id as checked for vat move logs", func() { + var headerChecked bool + err = db.Get(&headerChecked, `SELECT vat_move_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + + It("returns an error if insertion fails", func() { + err = vatMoveRepository.Create(headerID, []vat_move.VatMoveModel{test_data.VatMoveModel}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint")) + }) + + It("removes vat move event if corresponding header is deleted", func() { + _, err = db.Exec(`DELETE FROM headers WHERE id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + + var dbVatMove vat_move.VatMoveModel + err = db.Get(&dbVatMove, `SELECT src, dst, rad, tx_idx, raw_log FROM maker.vat_move WHERE header_id = $1`, headerID) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(sql.ErrNoRows)) + }) + }) + + Describe("MissingHeaders", func() { + var eventBlockNumber = rand.Int63() + var startingBlockNumber = eventBlockNumber - 1 + var endingBlockNumber = eventBlockNumber + 1 + var outOfRangeBlockNumber = eventBlockNumber + 2 + + It("returns headers haven't been checked", func() { + var headerIds []int64 + + for _, number := range []int64{startingBlockNumber, eventBlockNumber, endingBlockNumber, outOfRangeBlockNumber} { + headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number}) + Expect(err).NotTo(HaveOccurred()) + headerIds = append(headerIds, headerId) + } + + err := vatMoveRepository.MarkHeaderChecked(headerIds[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := vatMoveRepository.MissingHeaders(startingBlockNumber, endingBlockNumber) + Expect(err).NotTo(HaveOccurred()) + Expect(len(headers)).To(Equal(2)) + Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber))) + Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber))) + }) + + It("only treats headers as checked if vat_move has been checked", func() { + var headerIds []int64 + for _, number := range []int64{startingBlockNumber, eventBlockNumber, endingBlockNumber, outOfRangeBlockNumber} { + headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number}) + Expect(err).NotTo(HaveOccurred()) + headerIds = append(headerIds, headerId) + } + + // Just creates row, doesn't set this header as checked + _, err := db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIds[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := vatMoveRepository.MissingHeaders(startingBlockNumber, endingBlockNumber) + + Expect(err).NotTo(HaveOccurred()) + Expect(len(headers)).To(Equal(3)) + Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventBlockNumber))) + Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventBlockNumber))) + Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventBlockNumber))) + }) + + It("only returns headers associated with the current node", func() { + blockNumbers := []int64{1, 2, 3} + dbTwo := test_config.NewTestDB(core.Node{ID: "second"}) + headerRepositoryTwo := repositories.NewHeaderRepository(dbTwo) + var headerIDs []int64 + for _, n := range blockNumbers { + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + Expect(err).NotTo(HaveOccurred()) + headerIDs = append(headerIDs, headerID) + _, err = headerRepositoryTwo.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + Expect(err).NotTo(HaveOccurred()) + } + vatMoveRepositoryTwo := vat_move.NewVatMoveRepository(dbTwo) + err := vatMoveRepository.Create(headerIDs[0], []vat_move.VatMoveModel{test_data.VatMoveModel}) + Expect(err).NotTo(HaveOccurred()) + + nodeOneMissingHeaders, err := vatMoveRepository.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeOneMissingHeaders)).To(Equal(len(blockNumbers) - 1)) + + nodeTwoMissingHeaders, err := vatMoveRepositoryTwo.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeTwoMissingHeaders)).To(Equal(len(blockNumbers))) + }) + + }) + + Describe("MarkHeaderChecked", func() { + var headerID int64 + var err error + + BeforeEach(func() { + headerID, err = headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + }) + + It("creates a new row for a new headerID", func() { + err = vatMoveRepository.MarkHeaderChecked(headerID) + Expect(err).NotTo(HaveOccurred()) + + var headerChecked bool + err = db.Get(&headerChecked, `SELECT vat_move_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + + It("updates row when headerId already exists", func() { + _, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerID) + Expect(err).NotTo(HaveOccurred()) + err = vatMoveRepository.MarkHeaderChecked(headerID) + Expect(err).NotTo(HaveOccurred()) + + var headerChecked bool + err = db.Get(&headerChecked, `SELECT vat_move_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + }) +}) diff --git a/pkg/transformers/vat_move/transformer.go b/pkg/transformers/vat_move/transformer.go new file mode 100644 index 00000000..5318143c --- /dev/null +++ b/pkg/transformers/vat_move/transformer.go @@ -0,0 +1,83 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move + +import ( + "log" + + "github.com/ethereum/go-ethereum/common" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +type VatMoveTransformerInitializer struct { + Config shared.TransformerConfig +} + +func (initializer VatMoveTransformerInitializer) NewVatMoveTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { + converter := VatMoveConverter{} + fetcher := shared.NewFetcher(blockChain) + repository := NewVatMoveRepository(db) + return VatMoveTransformer{ + Config: initializer.Config, + Converter: converter, + Fetcher: fetcher, + Repository: repository, + } +} + +type VatMoveTransformer struct { + Config shared.TransformerConfig + Converter Converter + Fetcher shared.LogFetcher + Repository Repository +} + +func (transformer VatMoveTransformer) Execute() error { + missingHeaders, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber) + if err != nil { + return err + } + + log.Printf("Fetching vat move event logs for %d headers \n", len(missingHeaders)) + for _, header := range missingHeaders { + topics := [][]common.Hash{{common.HexToHash(shared.VatMoveSignature)}} + matchingLogs, err := transformer.Fetcher.FetchLogs(VatMoveConfig.ContractAddresses, topics, header.BlockNumber) + if err != nil { + return err + } + + if len(matchingLogs) < 1 { + err := transformer.Repository.MarkHeaderChecked(header.Id) + if err != nil { + return err + } + continue + } + + models, err := transformer.Converter.ToModels(matchingLogs) + if err != nil { + return err + } + + err = transformer.Repository.Create(header.Id, models) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/transformers/vat_move/transformer_test.go b/pkg/transformers/vat_move/transformer_test.go new file mode 100644 index 00000000..38371787 --- /dev/null +++ b/pkg/transformers/vat_move/transformer_test.go @@ -0,0 +1,212 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move_test + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" + vat_move_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/vat_move" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move" +) + +var _ = Describe("Vat move transformer", func() { + var fetcher mocks.MockLogFetcher + var converter vat_move_mocks.MockVatMoveConverter + var repository vat_move_mocks.MockVatMoveRepository + var config = vat_move.VatMoveConfig + var headerOne core.Header + var headerTwo core.Header + + BeforeEach(func() { + fetcher = mocks.MockLogFetcher{} + converter = vat_move_mocks.MockVatMoveConverter{} + repository = vat_move_mocks.MockVatMoveRepository{} + headerOne = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()} + headerTwo = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()} + }) + + It("gets missing headers for block numbers specified in config", func() { + transformer := vat_move.VatMoveTransformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedStartingBlockNumber).To(Equal(vat_move.VatMoveConfig.StartingBlockNumber)) + Expect(repository.PassedEndingBlockNumber).To(Equal(vat_move.VatMoveConfig.EndingBlockNumber)) + }) + + It("returns error if repository returns error for missing headers", func() { + repository.SetMissingHeadersError(fakes.FakeError) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("fetches logs for missing headers", func() { + repository.SetMissingHeaders([]core.Header{headerOne, headerTwo}) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Fetcher: &fetcher, + Converter: &vat_move_mocks.MockVatMoveConverter{}, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(fetcher.FetchedBlocks).To(Equal([]int64{headerOne.BlockNumber, headerTwo.BlockNumber})) + Expect(fetcher.FetchedContractAddresses).To(Equal([][]string{ + vat_move.VatMoveConfig.ContractAddresses, + vat_move.VatMoveConfig.ContractAddresses, + })) + Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.VatMoveSignature)}})) + }) + + It("returns error if fetcher returns error", func() { + fetcher.SetFetcherError(fakes.FakeError) + repository.SetMissingHeaders([]core.Header{headerOne}) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Fetcher: &fetcher, + Converter: &converter, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("converts matching logs", func() { + fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog}) + repository.SetMissingHeaders([]core.Header{headerOne}) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Fetcher: &fetcher, + Converter: &converter, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(converter.PassedLogs).To(Equal([]types.Log{test_data.EthVatMoveLog})) + }) + + It("returns error if converter returns error", func() { + converter.SetConverterError(fakes.FakeError) + fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog}) + repository.SetMissingHeaders([]core.Header{headerOne}) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Fetcher: &fetcher, + Converter: &converter, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("marks header as checked even if no logs were returned", func() { + repository.SetMissingHeaders([]core.Header{headerOne, headerTwo}) + fetcher.SetFetchedLogs([]types.Log{}) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + + err := transformer.Execute() + Expect(err).NotTo(HaveOccurred()) + Expect(repository.CheckedHeaderIDs).To(ContainElement(headerOne.Id)) + Expect(repository.CheckedHeaderIDs).To(ContainElement(headerTwo.Id)) + }) + + It("returns error if marking header checked returns err", func() { + repository.SetMissingHeaders([]core.Header{headerOne, headerTwo}) + repository.SetCheckedHeaderError(fakes.FakeError) + fetcher.SetFetchedLogs([]types.Log{}) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("persists vat move model", func() { + fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog}) + repository.SetMissingHeaders([]core.Header{headerOne}) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Fetcher: &fetcher, + Converter: &converter, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedHeaderID).To(Equal(headerOne.Id)) + Expect(repository.PassedModels).To(Equal([]vat_move.VatMoveModel{test_data.VatMoveModel})) + }) + + It("returns error if repository returns error for create", func() { + fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog}) + repository.SetMissingHeaders([]core.Header{headerOne}) + repository.SetCreateError(fakes.FakeError) + transformer := vat_move.VatMoveTransformer{ + Config: config, + Fetcher: &fetcher, + Converter: &converter, + Repository: &repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) +}) diff --git a/pkg/transformers/vat_move/vat_move_suite_test.go b/pkg/transformers/vat_move/vat_move_suite_test.go new file mode 100644 index 00000000..c93fbab1 --- /dev/null +++ b/pkg/transformers/vat_move/vat_move_suite_test.go @@ -0,0 +1,33 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_move_test + +import ( + "io/ioutil" + "log" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestVatMove(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "VatMove Suite") +} + +var _ = BeforeSuite(func() { + log.SetOutput(ioutil.Discard) +}) diff --git a/test_config/test_config.go b/test_config/test_config.go index 09e86234..a0c03d9e 100644 --- a/test_config/test_config.go +++ b/test_config/test_config.go @@ -99,6 +99,7 @@ func CleanTestDB(db *postgres.DB) { db.MustExec("DELETE FROM maker.vat_grab") db.MustExec("DELETE FROM maker.vat_heal") db.MustExec("DELETE FROM maker.vat_init") + db.MustExec("DELETE FROM maker.vat_move") db.MustExec("DELETE FROM maker.vat_fold") db.MustExec("DELETE FROM maker.vat_toll") db.MustExec("DELETE FROM maker.vat_tune") diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ENS.sol similarity index 100% rename from vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol rename to vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ENS.sol