diff --git a/Gopkg.lock b/Gopkg.lock index 2aa7bfdb..3928ef61 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -433,6 +433,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "d1f001d06a295f55fcb3dd1f38744ca0d088b9c4bf88f251ef3e043c8852fe76" + inputs-digest = "fe44a35c252e3af131cb6073e683761afa0f3aa0f2d49d468520bfd6e453c5f8" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 80b170d2..f2dc7160 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -25,6 +25,10 @@ name = "gopkg.in/fsnotify.v1" source = "gopkg.in/fsnotify/fsnotify.v1" +[[override]] + name = "github.com/pressly/sup" + version = "0.5.3" + [[constraint]] name = "github.com/onsi/ginkgo" version = "1.4.0" @@ -44,7 +48,3 @@ [[constraint]] name = "github.com/ethereum/go-ethereum" version = "1.8.15" - -[[constraint]] - name = "github.com/pressly/sup" - version = "0.5.3" diff --git a/db/migrations/1536765047_create_cat_file_tables.down.sql b/db/migrations/1536765047_create_cat_file_tables.down.sql new file mode 100644 index 00000000..eb2bdb55 --- /dev/null +++ b/db/migrations/1536765047_create_cat_file_tables.down.sql @@ -0,0 +1,3 @@ +DROP TABLE maker.cat_file_chop_lump; +DROP TABLE maker.cat_file_flip; +DROP TABLE maker.cat_file_pit_vow; \ No newline at end of file diff --git a/db/migrations/1536765047_create_cat_file_tables.up.sql b/db/migrations/1536765047_create_cat_file_tables.up.sql new file mode 100644 index 00000000..1241c8f7 --- /dev/null +++ b/db/migrations/1536765047_create_cat_file_tables.up.sql @@ -0,0 +1,31 @@ +CREATE TABLE maker.cat_file_chop_lump ( + id SERIAL PRIMARY KEY, + header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, + ilk TEXT, + what TEXT, + data NUMERIC, + tx_idx INTEGER NOT NUll, + raw_log JSONB, + UNIQUE (header_id, tx_idx) +); + +CREATE TABLE maker.cat_file_flip ( + id SERIAL PRIMARY KEY, + header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, + ilk TEXT, + what TEXT, + flip TEXT, + tx_idx INTEGER NOT NUll, + raw_log JSONB, + UNIQUE (header_id, tx_idx) +); + +CREATE TABLE maker.cat_file_pit_vow ( + id SERIAL PRIMARY KEY, + header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, + what TEXT, + data TEXT, + tx_idx INTEGER NOT NUll, + raw_log JSONB, + UNIQUE (header_id, tx_idx) +); \ No newline at end of file diff --git a/db/migrations/1537899079_add_cat_file_checked_fields_to_checked_headers.down.sql b/db/migrations/1537899079_add_cat_file_checked_fields_to_checked_headers.down.sql new file mode 100644 index 00000000..46694237 --- /dev/null +++ b/db/migrations/1537899079_add_cat_file_checked_fields_to_checked_headers.down.sql @@ -0,0 +1,6 @@ +ALTER TABLE public.checked_headers + DROP COLUMN cat_file_chop_lump_checked; +ALTER TABLE public.checked_headers + DROP COLUMN cat_file_flip_checked; +ALTER TABLE public.checked_headers + DROP COLUMN cat_file_pit_vow_checked; diff --git a/db/migrations/1537899079_add_cat_file_checked_fields_to_checked_headers.up.sql b/db/migrations/1537899079_add_cat_file_checked_fields_to_checked_headers.up.sql new file mode 100644 index 00000000..ef608eb9 --- /dev/null +++ b/db/migrations/1537899079_add_cat_file_checked_fields_to_checked_headers.up.sql @@ -0,0 +1,6 @@ +ALTER TABLE public.checked_headers + ADD COLUMN cat_file_chop_lump_checked BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE public.checked_headers + ADD COLUMN cat_file_flip_checked BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE public.checked_headers + ADD COLUMN cat_file_pit_vow_checked BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/db/migrations/1537970059_rename_bite_lad_to_urn.down.sql b/db/migrations/1537970059_rename_bite_lad_to_urn.down.sql new file mode 100644 index 00000000..85562980 --- /dev/null +++ b/db/migrations/1537970059_rename_bite_lad_to_urn.down.sql @@ -0,0 +1 @@ +ALTER TABLE maker.bite RENAME COLUMN urn TO lad; \ No newline at end of file diff --git a/db/migrations/1537970059_rename_bite_lad_to_urn.up.sql b/db/migrations/1537970059_rename_bite_lad_to_urn.up.sql new file mode 100644 index 00000000..c0155ec4 --- /dev/null +++ b/db/migrations/1537970059_rename_bite_lad_to_urn.up.sql @@ -0,0 +1 @@ +ALTER TABLE maker.bite RENAME COLUMN lad TO urn; \ No newline at end of file diff --git a/db/schema.sql b/db/schema.sql index 779749d1..4a00afc8 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -65,7 +65,7 @@ CREATE TABLE maker.bite ( id integer NOT NULL, header_id integer NOT NULL, ilk bytea, - lad bytea, + urn bytea, ink character varying, art character varying, iart character varying, @@ -96,6 +96,110 @@ CREATE SEQUENCE maker.bite_id_seq ALTER SEQUENCE maker.bite_id_seq OWNED BY maker.bite.id; +-- +-- Name: cat_file_chop_lump; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.cat_file_chop_lump ( + id integer NOT NULL, + header_id integer NOT NULL, + ilk text, + what text, + data numeric, + tx_idx integer NOT NULL, + raw_log jsonb +); + + +-- +-- Name: cat_file_chop_lump_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.cat_file_chop_lump_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: cat_file_chop_lump_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.cat_file_chop_lump_id_seq OWNED BY maker.cat_file_chop_lump.id; + + +-- +-- Name: cat_file_flip; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.cat_file_flip ( + id integer NOT NULL, + header_id integer NOT NULL, + ilk text, + what text, + flip text, + tx_idx integer NOT NULL, + raw_log jsonb +); + + +-- +-- Name: cat_file_flip_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.cat_file_flip_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: cat_file_flip_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.cat_file_flip_id_seq OWNED BY maker.cat_file_flip.id; + + +-- +-- Name: cat_file_pit_vow; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.cat_file_pit_vow ( + id integer NOT NULL, + header_id integer NOT NULL, + what text, + data text, + tx_idx integer NOT NULL, + raw_log jsonb +); + + +-- +-- Name: cat_file_pit_vow_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.cat_file_pit_vow_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: cat_file_pit_vow_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.cat_file_pit_vow_id_seq OWNED BY maker.cat_file_pit_vow.id; + + -- -- Name: deal; Type: TABLE; Schema: maker; Owner: - -- @@ -713,7 +817,10 @@ CREATE TABLE public.checked_headers ( deal_checked boolean DEFAULT false NOT NULL, dent_checked boolean DEFAULT false NOT NULL, flip_kick_checked boolean DEFAULT false NOT NULL, - tend_checked boolean DEFAULT false NOT NULL + tend_checked boolean DEFAULT false NOT NULL, + cat_file_chop_lump_checked boolean DEFAULT false NOT NULL, + cat_file_flip_checked boolean DEFAULT false NOT NULL, + cat_file_pit_vow_checked boolean DEFAULT false NOT NULL ); @@ -1041,6 +1148,27 @@ CREATE VIEW public.watched_event_logs AS ALTER TABLE ONLY maker.bite ALTER COLUMN id SET DEFAULT nextval('maker.bite_id_seq'::regclass); +-- +-- Name: cat_file_chop_lump id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_chop_lump ALTER COLUMN id SET DEFAULT nextval('maker.cat_file_chop_lump_id_seq'::regclass); + + +-- +-- Name: cat_file_flip id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_flip ALTER COLUMN id SET DEFAULT nextval('maker.cat_file_flip_id_seq'::regclass); + + +-- +-- Name: cat_file_pit_vow id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_pit_vow ALTER COLUMN id SET DEFAULT nextval('maker.cat_file_pit_vow_id_seq'::regclass); + + -- -- Name: deal id; Type: DEFAULT; Schema: maker; Owner: - -- @@ -1232,6 +1360,54 @@ ALTER TABLE ONLY maker.bite ADD CONSTRAINT bite_pkey PRIMARY KEY (id); +-- +-- Name: cat_file_chop_lump cat_file_chop_lump_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_chop_lump + ADD CONSTRAINT cat_file_chop_lump_header_id_tx_idx_key UNIQUE (header_id, tx_idx); + + +-- +-- Name: cat_file_chop_lump cat_file_chop_lump_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_chop_lump + ADD CONSTRAINT cat_file_chop_lump_pkey PRIMARY KEY (id); + + +-- +-- Name: cat_file_flip cat_file_flip_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_flip + ADD CONSTRAINT cat_file_flip_header_id_tx_idx_key UNIQUE (header_id, tx_idx); + + +-- +-- Name: cat_file_flip cat_file_flip_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_flip + ADD CONSTRAINT cat_file_flip_pkey PRIMARY KEY (id); + + +-- +-- Name: cat_file_pit_vow cat_file_pit_vow_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_pit_vow + ADD CONSTRAINT cat_file_pit_vow_header_id_tx_idx_key UNIQUE (header_id, tx_idx); + + +-- +-- Name: cat_file_pit_vow cat_file_pit_vow_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_pit_vow + ADD CONSTRAINT cat_file_pit_vow_pkey PRIMARY KEY (id); + + -- -- Name: deal deal_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - -- @@ -1650,6 +1826,30 @@ ALTER TABLE ONLY maker.bite ADD CONSTRAINT bite_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; +-- +-- Name: cat_file_chop_lump cat_file_chop_lump_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_chop_lump + ADD CONSTRAINT cat_file_chop_lump_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; + + +-- +-- Name: cat_file_flip cat_file_flip_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_flip + ADD CONSTRAINT cat_file_flip_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; + + +-- +-- Name: cat_file_pit_vow cat_file_pit_vow_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.cat_file_pit_vow + ADD CONSTRAINT cat_file_pit_vow_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; + + -- -- Name: deal deal_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - -- diff --git a/pkg/geth/blockchain.go b/pkg/geth/blockchain.go index 5f50e213..0f4d62bb 100644 --- a/pkg/geth/blockchain.go +++ b/pkg/geth/blockchain.go @@ -1,6 +1,7 @@ package geth import ( + "errors" "math/big" "github.com/ethereum/go-ethereum" @@ -11,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/vulcanize/vulcanizedb/pkg/core" vulcCommon "github.com/vulcanize/vulcanizedb/pkg/geth/converters/common" - "errors" ) var ErrEmptyHeader = errors.New("empty header returned over RPC") diff --git a/pkg/transformers/bite/converter.go b/pkg/transformers/bite/converter.go index 56a461c9..2c145b3f 100644 --- a/pkg/transformers/bite/converter.go +++ b/pkg/transformers/bite/converter.go @@ -56,7 +56,7 @@ func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) { id := entity.Id ilk := entity.Ilk[:] - lad := entity.Lad[:] + urn := entity.Urn[:] ink := entity.Ink art := entity.Art iArt := entity.IArt @@ -72,7 +72,7 @@ func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) { return BiteModel{ Id: shared.BigIntToString(id), Ilk: ilk, - Lad: lad, + Urn: urn, Ink: shared.BigIntToString(ink), Art: shared.BigIntToString(art), IArt: shared.BigIntToString(iArt), diff --git a/pkg/transformers/bite/converter_test.go b/pkg/transformers/bite/converter_test.go index c351cb87..dd526ec9 100644 --- a/pkg/transformers/bite/converter_test.go +++ b/pkg/transformers/bite/converter_test.go @@ -38,7 +38,7 @@ var _ = Describe("Bite Converter", func() { Expect(err).NotTo(HaveOccurred()) Expect(entity.Ilk).To(Equal(test_data.BiteEntity.Ilk)) - Expect(entity.Lad).To(Equal(test_data.BiteEntity.Lad)) + Expect(entity.Urn).To(Equal(test_data.BiteEntity.Urn)) Expect(entity.Ink).To(Equal(test_data.BiteEntity.Ink)) Expect(entity.Art).To(Equal(test_data.BiteEntity.Art)) Expect(entity.Tab).To(Equal(test_data.BiteEntity.Tab)) @@ -76,7 +76,7 @@ var _ = Describe("Bite Converter", func() { expectedModel := bite.BiteModel{ Id: "1", Ilk: []byte{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, 0, 0, 0}, - Lad: []byte{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, 0, 0, 0}, + Urn: []byte{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, 0, 0, 0}, Ink: "", Art: "", IArt: "", diff --git a/pkg/transformers/bite/entity.go b/pkg/transformers/bite/entity.go index 489aba0e..aa80b81e 100644 --- a/pkg/transformers/bite/entity.go +++ b/pkg/transformers/bite/entity.go @@ -22,7 +22,7 @@ import ( type BiteEntity struct { Id *big.Int Ilk [32]byte - Lad [32]byte + Urn [32]byte Ink *big.Int Art *big.Int Tab *big.Int diff --git a/pkg/transformers/bite/model.go b/pkg/transformers/bite/model.go index 5e58f998..a951eb74 100644 --- a/pkg/transformers/bite/model.go +++ b/pkg/transformers/bite/model.go @@ -17,7 +17,7 @@ package bite type BiteModel struct { Id string Ilk []byte - Lad []byte + Urn []byte Ink string Art string IArt string diff --git a/pkg/transformers/bite/repository.go b/pkg/transformers/bite/repository.go index 8db052e6..8baa7ca6 100644 --- a/pkg/transformers/bite/repository.go +++ b/pkg/transformers/bite/repository.go @@ -34,9 +34,9 @@ func NewBiteRepository(db *postgres.DB) Repository { func (repository BiteRepository) Create(headerID int64, model BiteModel) error { _, err := repository.db.Exec( - `INSERT into maker.bite (header_id, id, ilk, lad, ink, art, iart, tab, flip, tx_idx, raw_log) + `INSERT into maker.bite (header_id, id, ilk, urn, ink, art, iart, tab, flip, tx_idx, raw_log) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`, - headerID, model.Id, model.Ilk, model.Lad, model.Ink, model.Art, model.IArt, model.Tab, model.Flip, model.TransactionIndex, model.Raw, + headerID, model.Id, model.Ilk, model.Urn, model.Ink, model.Art, model.IArt, model.Tab, model.Flip, model.TransactionIndex, model.Raw, ) if err != nil { diff --git a/pkg/transformers/bite/repository_test.go b/pkg/transformers/bite/repository_test.go index dd26d89b..19f3d023 100644 --- a/pkg/transformers/bite/repository_test.go +++ b/pkg/transformers/bite/repository_test.go @@ -41,11 +41,11 @@ var _ = Describe("Bite repository", func() { Expect(err).NotTo(HaveOccurred()) var dbBite bite.BiteModel - err = db.Get(&dbBite, `SELECT id, ilk, lad, ink, art, tab, flip, tx_idx, raw_log FROM maker.bite WHERE header_id = $1`, headerID) + err = db.Get(&dbBite, `SELECT id, ilk, urn, ink, art, tab, flip, tx_idx, raw_log FROM maker.bite WHERE header_id = $1`, headerID) Expect(err).NotTo(HaveOccurred()) Expect(dbBite.Id).To(Equal(test_data.BiteModel.Id)) Expect(dbBite.Ilk).To(Equal(test_data.BiteModel.Ilk)) - Expect(dbBite.Lad).To(Equal(test_data.BiteModel.Lad)) + Expect(dbBite.Urn).To(Equal(test_data.BiteModel.Urn)) Expect(dbBite.Art).To(Equal(test_data.BiteModel.Art)) Expect(dbBite.Tab).To(Equal(test_data.BiteModel.Tab)) Expect(dbBite.Flip).To(Equal(test_data.BiteModel.Flip)) @@ -67,7 +67,7 @@ var _ = Describe("Bite repository", func() { var anotherBiteModel = bite.BiteModel{ Id: "11", Ilk: test_data.BiteModel.Ilk, - Lad: test_data.BiteModel.Lad, + Urn: test_data.BiteModel.Urn, Ink: test_data.BiteModel.Ink, Art: test_data.BiteModel.Art, Tab: test_data.BiteModel.Tab, @@ -98,7 +98,7 @@ var _ = Describe("Bite repository", func() { Expect(err).NotTo(HaveOccurred()) var dbBite bite.BiteModel - err = db.Get(&dbBite, `SELECT id, ilk, lad, ink, art, tab, flip, tx_idx, raw_log FROM maker.bite WHERE header_id = $1`, headerID) + err = db.Get(&dbBite, `SELECT id, ilk, urn, ink, art, tab, flip, tx_idx, raw_log FROM maker.bite WHERE header_id = $1`, headerID) Expect(err).To(HaveOccurred()) Expect(err).To(MatchError(sql.ErrNoRows)) }) diff --git a/pkg/transformers/cat_file/chop_lump/chop_lump_suite_test.go b/pkg/transformers/cat_file/chop_lump/chop_lump_suite_test.go new file mode 100644 index 00000000..61cd6347 --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/chop_lump_suite_test.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 chop_lump_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestChopLump(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "ChopLump Suite") +} diff --git a/pkg/transformers/cat_file/chop_lump/converter.go b/pkg/transformers/cat_file/chop_lump/converter.go new file mode 100644 index 00000000..1495d459 --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/converter.go @@ -0,0 +1,68 @@ +// 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 chop_lump + +import ( + "bytes" + "encoding/json" + "errors" + "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "math/big" +) + +type Converter interface { + ToModels(ethLogs []types.Log) ([]CatFileChopLumpModel, error) +} + +type CatFileChopLumpConverter struct{} + +func (CatFileChopLumpConverter) ToModels(ethLogs []types.Log) ([]CatFileChopLumpModel, error) { + var results []CatFileChopLumpModel + for _, ethLog := range ethLogs { + err := verifyLog(ethLog) + if err != nil { + return nil, err + } + ilk := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + what := string(bytes.Trim(ethLog.Topics[3].Bytes(), "\x00")) + dataBytes := ethLog.Data[len(ethLog.Data)-shared.DataItemLength:] + data := big.NewInt(0).SetBytes(dataBytes).String() + + raw, err := json.Marshal(ethLog) + if err != nil { + return nil, err + } + result := CatFileChopLumpModel{ + Ilk: ilk, + What: what, + Data: data, + TransactionIndex: ethLog.TxIndex, + Raw: raw, + } + results = append(results, result) + } + return results, nil +} + +func verifyLog(log types.Log) error { + if len(log.Topics) < 4 { + return errors.New("log missing topics") + } + if len(log.Data) < shared.DataItemLength { + return errors.New("log missing data") + } + return nil +} diff --git a/pkg/transformers/cat_file/chop_lump/converter_test.go b/pkg/transformers/cat_file/chop_lump/converter_test.go new file mode 100644 index 00000000..ba2fa1ab --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/converter_test.go @@ -0,0 +1,58 @@ +// 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 chop_lump_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/transformers/cat_file/chop_lump" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +var _ = Describe("Cat file chop lump converter", func() { + It("returns err if log is missing topics", func() { + converter := chop_lump.CatFileChopLumpConverter{} + badLog := types.Log{ + Data: []byte{1, 1, 1, 1, 1}, + } + + _, err := converter.ToModels([]types.Log{badLog}) + + Expect(err).To(HaveOccurred()) + }) + + It("returns err if log is missing data", func() { + converter := chop_lump.CatFileChopLumpConverter{} + badLog := types.Log{ + Topics: []common.Hash{{}, {}, {}, {}}, + } + + _, err := converter.ToModels([]types.Log{badLog}) + + Expect(err).To(HaveOccurred()) + }) + + It("converts a log to an model", func() { + converter := chop_lump.CatFileChopLumpConverter{} + + model, err := converter.ToModels([]types.Log{test_data.EthCatFileChopLumpLog}) + + Expect(err).NotTo(HaveOccurred()) + Expect(model).To(Equal([]chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel})) + }) +}) diff --git a/pkg/transformers/cat_file/chop_lump/model.go b/pkg/transformers/cat_file/chop_lump/model.go new file mode 100644 index 00000000..c49bac9e --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/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 chop_lump + +type CatFileChopLumpModel struct { + Ilk string + What string + Data string + TransactionIndex uint `db:"tx_idx"` + Raw []byte `db:"raw_log"` +} diff --git a/pkg/transformers/cat_file/chop_lump/repository.go b/pkg/transformers/cat_file/chop_lump/repository.go new file mode 100644 index 00000000..c6e83219 --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/repository.go @@ -0,0 +1,86 @@ +// 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 chop_lump + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" +) + +type Repository interface { + Create(headerID int64, models []CatFileChopLumpModel) error + MarkHeaderChecked(headerID int64) error + MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) +} + +type CatFileChopLumpRepository struct { + db *postgres.DB +} + +func NewCatFileChopLumpRepository(db *postgres.DB) CatFileChopLumpRepository { + return CatFileChopLumpRepository{db: db} +} + +func (repository CatFileChopLumpRepository) Create(headerID int64, models []CatFileChopLumpModel) error { + tx, err := repository.db.Begin() + if err != nil { + return err + } + for _, model := range models { + _, err := tx.Exec( + `INSERT into maker.cat_file_chop_lump (header_id, ilk, what, data, tx_idx, raw_log) + VALUES($1, $2, $3, $4::NUMERIC, $5, $6)`, + headerID, model.Ilk, model.What, model.Data, model.TransactionIndex, model.Raw, + ) + if err != nil { + tx.Rollback() + return err + } + } + _, err = tx.Exec(`INSERT INTO public.checked_headers (header_id, cat_file_chop_lump_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET cat_file_chop_lump_checked = $2`, headerID, true) + if err != nil { + tx.Rollback() + return err + } + return tx.Commit() +} + +func (repository CatFileChopLumpRepository) MarkHeaderChecked(headerID int64) error { + _, err := repository.db.Exec(`INSERT INTO public.checked_headers (header_id, cat_file_chop_lump_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET cat_file_chop_lump_checked = $2`, headerID, true) + return err +} + +func (repository CatFileChopLumpRepository) 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 cat_file_chop_lump_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 +} diff --git a/pkg/transformers/cat_file/chop_lump/repository_test.go b/pkg/transformers/cat_file/chop_lump/repository_test.go new file mode 100644 index 00000000..394969e5 --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/repository_test.go @@ -0,0 +1,215 @@ +// 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 chop_lump_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/cat_file/chop_lump" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("Cat file chop lump repository", func() { + Describe("Create", func() { + var catFileRepository chop_lump.CatFileChopLumpRepository + var db *postgres.DB + var err error + var headerID int64 + + BeforeEach(func() { + db = test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err = headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository = chop_lump.NewCatFileChopLumpRepository(db) + }) + + It("adds a cat file chop lump event", func() { + err = catFileRepository.Create(headerID, []chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel}) + + Expect(err).NotTo(HaveOccurred()) + var dbPitFile chop_lump.CatFileChopLumpModel + err = db.Get(&dbPitFile, `SELECT ilk, what, data, tx_idx, raw_log FROM maker.cat_file_chop_lump WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(dbPitFile.Ilk).To(Equal(test_data.CatFileChopLumpModel.Ilk)) + Expect(dbPitFile.What).To(Equal(test_data.CatFileChopLumpModel.What)) + Expect(dbPitFile.Data).To(Equal(test_data.CatFileChopLumpModel.Data)) + Expect(dbPitFile.TransactionIndex).To(Equal(test_data.CatFileChopLumpModel.TransactionIndex)) + Expect(dbPitFile.Raw).To(MatchJSON(test_data.CatFileChopLumpModel.Raw)) + }) + + It("marks header as checked for logs", func() { + err = catFileRepository.Create(headerID, []chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel}) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_chop_lump_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + + It("does not duplicate cat file chop lump events", func() { + err = catFileRepository.Create(headerID, []chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel}) + Expect(err).NotTo(HaveOccurred()) + + err = catFileRepository.Create(headerID, []chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel}) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint")) + }) + + It("removes cat file chop lump if corresponding header is deleted", func() { + err = catFileRepository.Create(headerID, []chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel}) + Expect(err).NotTo(HaveOccurred()) + + _, err = db.Exec(`DELETE FROM headers WHERE id = $1`, headerID) + + Expect(err).NotTo(HaveOccurred()) + var dbPitFile chop_lump.CatFileChopLumpModel + err = db.Get(&dbPitFile, `SELECT ilk, what, data, tx_idx, raw_log FROM maker.cat_file_chop_lump WHERE header_id = $1`, headerID) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(sql.ErrNoRows)) + }) + }) + + Describe("MarkHeaderChecked", func() { + It("creates a row for a new headerID", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository := chop_lump.NewCatFileChopLumpRepository(db) + + err = catFileRepository.MarkHeaderChecked(headerID) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_chop_lump_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() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository := chop_lump.NewCatFileChopLumpRepository(db) + _, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerID) + + err = catFileRepository.MarkHeaderChecked(headerID) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_chop_lump_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + }) + + Describe("MissingHeaders", func() { + It("returns headers that haven't been checked", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + startingBlockNumber := int64(1) + catFileBlockNumber := int64(2) + endingBlockNumber := int64(3) + blockNumbers := []int64{startingBlockNumber, catFileBlockNumber, endingBlockNumber, endingBlockNumber + 1} + var headerIDs []int64 + for _, n := range blockNumbers { + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + headerIDs = append(headerIDs, headerID) + Expect(err).NotTo(HaveOccurred()) + } + catFileRepository := chop_lump.NewCatFileChopLumpRepository(db) + err := catFileRepository.MarkHeaderChecked(headerIDs[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := catFileRepository.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 cat file chop lump logs have been checked", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + startingBlockNumber := int64(1) + catFiledBlockNumber := int64(2) + endingBlockNumber := int64(3) + blockNumbers := []int64{startingBlockNumber, catFiledBlockNumber, endingBlockNumber, endingBlockNumber + 1} + var headerIDs []int64 + for _, n := range blockNumbers { + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + headerIDs = append(headerIDs, headerID) + Expect(err).NotTo(HaveOccurred()) + } + catFiledRepository := chop_lump.NewCatFileChopLumpRepository(db) + _, err := db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIDs[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := catFiledRepository.MissingHeaders(startingBlockNumber, endingBlockNumber) + + Expect(err).NotTo(HaveOccurred()) + Expect(len(headers)).To(Equal(3)) + Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + }) + + It("only returns headers associated with the current node", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + blockNumbers := []int64{1, 2, 3} + headerRepository := repositories.NewHeaderRepository(db) + 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()) + } + catFileRepository := chop_lump.NewCatFileChopLumpRepository(db) + catFileRepositoryTwo := chop_lump.NewCatFileChopLumpRepository(dbTwo) + err := catFileRepository.MarkHeaderChecked(headerIDs[0]) + Expect(err).NotTo(HaveOccurred()) + + nodeOneMissingHeaders, err := catFileRepository.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeOneMissingHeaders)).To(Equal(len(blockNumbers) - 1)) + + nodeTwoMissingHeaders, err := catFileRepositoryTwo.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeTwoMissingHeaders)).To(Equal(len(blockNumbers))) + }) + }) +}) diff --git a/pkg/transformers/cat_file/chop_lump/transformer.go b/pkg/transformers/cat_file/chop_lump/transformer.go new file mode 100644 index 00000000..17d75548 --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/transformer.go @@ -0,0 +1,76 @@ +// 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 chop_lump + +import ( + "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/cat_file" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +type CatFileChopLumpTransformerInitializer struct { + Config shared.TransformerConfig +} + +func (initializer CatFileChopLumpTransformerInitializer) NewCatFileChopLumpTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { + converter := CatFileChopLumpConverter{} + fetcher := shared.NewFetcher(blockChain) + repository := NewCatFileChopLumpRepository(db) + return CatFileChopLumpTransformer{ + Config: initializer.Config, + Converter: converter, + Fetcher: fetcher, + Repository: repository, + } +} + +type CatFileChopLumpTransformer struct { + Config shared.TransformerConfig + Converter Converter + Fetcher shared.LogFetcher + Repository Repository +} + +func (transformer CatFileChopLumpTransformer) Execute() error { + missingHeaders, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber) + if err != nil { + return err + } + for _, header := range missingHeaders { + topics := [][]common.Hash{{common.HexToHash(shared.CatFileChopLumpSignature)}} + matchingLogs, err := transformer.Fetcher.FetchLogs(cat_file.CatFileConfig.ContractAddresses, topics, header.BlockNumber) + if err != nil { + return err + } + if len(matchingLogs) < 1 { + err = transformer.Repository.MarkHeaderChecked(header.Id) + if err != nil { + return err + } + } + 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/cat_file/chop_lump/transformer_test.go b/pkg/transformers/cat_file/chop_lump/transformer_test.go new file mode 100644 index 00000000..bfb025f5 --- /dev/null +++ b/pkg/transformers/cat_file/chop_lump/transformer_test.go @@ -0,0 +1,211 @@ +// 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 chop_lump_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/cat_file" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/chop_lump" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" + chop_lump_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/cat_file/chop_lump" +) + +var _ = Describe("Cat file chop lump transformer", func() { + It("gets missing headers for block numbers specified in config", func() { + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + transformer := chop_lump.CatFileChopLumpTransformer{ + Config: cat_file.CatFileConfig, + Fetcher: &mocks.MockLogFetcher{}, + Converter: &chop_lump_mocks.MockCatFileChopLumpConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedStartingBlockNumber).To(Equal(cat_file.CatFileConfig.StartingBlockNumber)) + Expect(repository.PassedEndingBlockNumber).To(Equal(cat_file.CatFileConfig.EndingBlockNumber)) + }) + + It("returns error if repository returns error for missing headers", func() { + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + repository.SetMissingHeadersErr(fakes.FakeError) + transformer := chop_lump.CatFileChopLumpTransformer{ + Fetcher: &mocks.MockLogFetcher{}, + Converter: &chop_lump_mocks.MockCatFileChopLumpConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("fetches logs for missing headers", func() { + fetcher := &mocks.MockLogFetcher{} + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}, {BlockNumber: 2}}) + transformer := chop_lump.CatFileChopLumpTransformer{ + Fetcher: fetcher, + Converter: &chop_lump_mocks.MockCatFileChopLumpConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(fetcher.FetchedBlocks).To(Equal([]int64{1, 2})) + Expect(fetcher.FetchedContractAddresses).To(Equal([][]string{cat_file.CatFileConfig.ContractAddresses, cat_file.CatFileConfig.ContractAddresses})) + Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.CatFileChopLumpSignature)}})) + }) + + It("returns error if fetcher returns error", func() { + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetcherError(fakes.FakeError) + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := chop_lump.CatFileChopLumpTransformer{ + Fetcher: fetcher, + Converter: &chop_lump_mocks.MockCatFileChopLumpConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("marks header checked if no logs returned", func() { + mockConverter := &chop_lump_mocks.MockCatFileChopLumpConverter{} + mockRepository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + headerID := int64(123) + mockRepository.SetMissingHeaders([]core.Header{{Id: headerID}}) + mockFetcher := &mocks.MockLogFetcher{} + transformer := chop_lump.CatFileChopLumpTransformer{ + Converter: mockConverter, + Fetcher: mockFetcher, + Repository: mockRepository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + mockRepository.AssertMarkHeaderCheckedCalledWith(headerID) + }) + + It("returns error if marking header checked returns err", func() { + mockConverter := &chop_lump_mocks.MockCatFileChopLumpConverter{} + mockRepository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + mockRepository.SetMissingHeaders([]core.Header{{Id: int64(123)}}) + mockRepository.SetMarkHeaderCheckedErr(fakes.FakeError) + mockFetcher := &mocks.MockLogFetcher{} + transformer := chop_lump.CatFileChopLumpTransformer{ + Converter: mockConverter, + Fetcher: mockFetcher, + Repository: mockRepository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("converts matching logs", func() { + converter := &chop_lump_mocks.MockCatFileChopLumpConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileChopLumpLog}) + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := chop_lump.CatFileChopLumpTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(converter.PassedLogs).To(Equal([]types.Log{test_data.EthCatFileChopLumpLog})) + }) + + It("returns error if converter returns error", func() { + converter := &chop_lump_mocks.MockCatFileChopLumpConverter{} + converter.SetConverterError(fakes.FakeError) + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileChopLumpLog}) + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := chop_lump.CatFileChopLumpTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("persists cat file chop lump model", func() { + converter := &chop_lump_mocks.MockCatFileChopLumpConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileChopLumpLog}) + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + fakeHeader := core.Header{BlockNumber: 1, Id: 2} + repository.SetMissingHeaders([]core.Header{fakeHeader}) + transformer := chop_lump.CatFileChopLumpTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedHeaderID).To(Equal(fakeHeader.Id)) + Expect(repository.PassedModels).To(Equal([]chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel})) + }) + + It("returns error if repository returns error for create", func() { + converter := &chop_lump_mocks.MockCatFileChopLumpConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileChopLumpLog}) + repository := &chop_lump_mocks.MockCatFileChopLumpRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1, Id: 2}}) + repository.SetCreateError(fakes.FakeError) + transformer := chop_lump.CatFileChopLumpTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) +}) diff --git a/pkg/transformers/cat_file/config.go b/pkg/transformers/cat_file/config.go new file mode 100644 index 00000000..50d6ed98 --- /dev/null +++ b/pkg/transformers/cat_file/config.go @@ -0,0 +1,25 @@ +// 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 cat_file + +import "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + +var CatFileConfig = shared.TransformerConfig{ + ContractAddresses: []string{shared.CatContractAddress}, + ContractAbi: shared.CatABI, + Topics: []string{shared.CatFileChopLumpSignature, shared.CatFileFlipSignature, shared.CatFilePitVowSignature}, + StartingBlockNumber: 0, + EndingBlockNumber: 10000000, +} diff --git a/pkg/transformers/cat_file/flip/converter.go b/pkg/transformers/cat_file/flip/converter.go new file mode 100644 index 00000000..43ba63c7 --- /dev/null +++ b/pkg/transformers/cat_file/flip/converter.go @@ -0,0 +1,68 @@ +// 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 flip + +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 Converter interface { + ToModels(ethLogs []types.Log) ([]CatFileFlipModel, error) +} + +type CatFileFlipConverter struct{} + +func (CatFileFlipConverter) ToModels(ethLogs []types.Log) ([]CatFileFlipModel, error) { + var results []CatFileFlipModel + for _, ethLog := range ethLogs { + err := verifyLog(ethLog) + if err != nil { + return nil, err + } + ilk := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + what := string(bytes.Trim(ethLog.Topics[3].Bytes(), "\x00")) + flipBytes := ethLog.Data[len(ethLog.Data)-shared.DataItemLength:] + flip := common.BytesToAddress(flipBytes).String() + + raw, err := json.Marshal(ethLog) + if err != nil { + return nil, err + } + result := CatFileFlipModel{ + Ilk: ilk, + What: what, + Flip: flip, + TransactionIndex: ethLog.TxIndex, + Raw: raw, + } + results = append(results, result) + } + return results, nil +} + +func verifyLog(log types.Log) error { + if len(log.Topics) < 4 { + return errors.New("log missing topics") + } + if len(log.Data) < shared.DataItemLength { + return errors.New("log missing data") + } + return nil +} diff --git a/pkg/transformers/cat_file/flip/converter_test.go b/pkg/transformers/cat_file/flip/converter_test.go new file mode 100644 index 00000000..459b4aec --- /dev/null +++ b/pkg/transformers/cat_file/flip/converter_test.go @@ -0,0 +1,58 @@ +// 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 flip_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/transformers/cat_file/flip" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +var _ = Describe("Cat file flip converter", func() { + It("returns err if log is missing topics", func() { + converter := flip.CatFileFlipConverter{} + badLog := types.Log{ + Data: []byte{1, 1, 1, 1, 1}, + } + + _, err := converter.ToModels([]types.Log{badLog}) + + Expect(err).To(HaveOccurred()) + }) + + It("returns err if log is missing data", func() { + converter := flip.CatFileFlipConverter{} + badLog := types.Log{ + Topics: []common.Hash{{}, {}, {}, {}}, + } + + _, err := converter.ToModels([]types.Log{badLog}) + + Expect(err).To(HaveOccurred()) + }) + + It("converts a log to an model", func() { + converter := flip.CatFileFlipConverter{} + + model, err := converter.ToModels([]types.Log{test_data.EthCatFileFlipLog}) + + Expect(err).NotTo(HaveOccurred()) + Expect(model).To(Equal([]flip.CatFileFlipModel{test_data.CatFileFlipModel})) + }) +}) diff --git a/pkg/transformers/cat_file/flip/flip_suite_test.go b/pkg/transformers/cat_file/flip/flip_suite_test.go new file mode 100644 index 00000000..6d7e0ae0 --- /dev/null +++ b/pkg/transformers/cat_file/flip/flip_suite_test.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 flip_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestFlip(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Flip Suite") +} diff --git a/pkg/transformers/cat_file/flip/model.go b/pkg/transformers/cat_file/flip/model.go new file mode 100644 index 00000000..cf3c35f7 --- /dev/null +++ b/pkg/transformers/cat_file/flip/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 flip + +type CatFileFlipModel struct { + Ilk string + What string + Flip string + TransactionIndex uint `db:"tx_idx"` + Raw []byte `db:"raw_log"` +} diff --git a/pkg/transformers/cat_file/flip/repository.go b/pkg/transformers/cat_file/flip/repository.go new file mode 100644 index 00000000..a53c1bf8 --- /dev/null +++ b/pkg/transformers/cat_file/flip/repository.go @@ -0,0 +1,86 @@ +// 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 flip + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" +) + +type Repository interface { + Create(headerID int64, models []CatFileFlipModel) error + MarkHeaderChecked(headerID int64) error + MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) +} + +type CatFileFlipRepository struct { + db *postgres.DB +} + +func NewCatFileFlipRepository(db *postgres.DB) CatFileFlipRepository { + return CatFileFlipRepository{db: db} +} + +func (repository CatFileFlipRepository) Create(headerID int64, models []CatFileFlipModel) error { + tx, err := repository.db.Begin() + if err != nil { + return err + } + for _, model := range models { + _, err = repository.db.Exec( + `INSERT into maker.cat_file_flip (header_id, ilk, what, flip, tx_idx, raw_log) + VALUES($1, $2, $3, $4, $5, $6)`, + headerID, model.Ilk, model.What, model.Flip, model.TransactionIndex, model.Raw, + ) + if err != nil { + tx.Rollback() + return err + } + } + _, err = tx.Exec(`INSERT INTO public.checked_headers (header_id, cat_file_flip_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET cat_file_flip_checked = $2`, headerID, true) + if err != nil { + tx.Rollback() + return err + } + return tx.Commit() +} + +func (repository CatFileFlipRepository) MarkHeaderChecked(headerID int64) error { + _, err := repository.db.Exec(`INSERT INTO public.checked_headers (header_id, cat_file_flip_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET cat_file_flip_checked = $2`, headerID, true) + return err +} + +func (repository CatFileFlipRepository) 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 cat_file_flip_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 +} diff --git a/pkg/transformers/cat_file/flip/repository_test.go b/pkg/transformers/cat_file/flip/repository_test.go new file mode 100644 index 00000000..281976f4 --- /dev/null +++ b/pkg/transformers/cat_file/flip/repository_test.go @@ -0,0 +1,215 @@ +// 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 flip_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/cat_file/flip" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("Cat file flip repository", func() { + Describe("Create", func() { + var catFileRepository flip.CatFileFlipRepository + var db *postgres.DB + var err error + var headerID int64 + + BeforeEach(func() { + db = test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err = headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository = flip.NewCatFileFlipRepository(db) + }) + + It("adds a cat file flip event", func() { + err = catFileRepository.Create(headerID, []flip.CatFileFlipModel{test_data.CatFileFlipModel}) + + Expect(err).NotTo(HaveOccurred()) + var dbPitFile flip.CatFileFlipModel + err = db.Get(&dbPitFile, `SELECT ilk, what, flip, tx_idx, raw_log FROM maker.cat_file_flip WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(dbPitFile.Ilk).To(Equal(test_data.CatFileFlipModel.Ilk)) + Expect(dbPitFile.What).To(Equal(test_data.CatFileFlipModel.What)) + Expect(dbPitFile.Flip).To(Equal(test_data.CatFileFlipModel.Flip)) + Expect(dbPitFile.TransactionIndex).To(Equal(test_data.CatFileFlipModel.TransactionIndex)) + Expect(dbPitFile.Raw).To(MatchJSON(test_data.CatFileFlipModel.Raw)) + }) + + It("marks header as checked for logs", func() { + err = catFileRepository.Create(headerID, []flip.CatFileFlipModel{test_data.CatFileFlipModel}) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_flip_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + + It("does not duplicate cat file flip events", func() { + err = catFileRepository.Create(headerID, []flip.CatFileFlipModel{test_data.CatFileFlipModel}) + Expect(err).NotTo(HaveOccurred()) + + err = catFileRepository.Create(headerID, []flip.CatFileFlipModel{test_data.CatFileFlipModel}) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint")) + }) + + It("removes cat file flip if corresponding header is deleted", func() { + err = catFileRepository.Create(headerID, []flip.CatFileFlipModel{test_data.CatFileFlipModel}) + Expect(err).NotTo(HaveOccurred()) + + _, err = db.Exec(`DELETE FROM headers WHERE id = $1`, headerID) + + Expect(err).NotTo(HaveOccurred()) + var dbPitFile flip.CatFileFlipModel + err = db.Get(&dbPitFile, `SELECT ilk, what, flip, tx_idx, raw_log FROM maker.cat_file_flip WHERE header_id = $1`, headerID) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(sql.ErrNoRows)) + }) + }) + + Describe("MarkHeaderChecked", func() { + It("creates a row for a new headerID", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository := flip.NewCatFileFlipRepository(db) + + err = catFileRepository.MarkHeaderChecked(headerID) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_flip_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() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository := flip.NewCatFileFlipRepository(db) + _, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerID) + + err = catFileRepository.MarkHeaderChecked(headerID) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_flip_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + }) + + Describe("MissingHeaders", func() { + It("returns headers that haven't been checked", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + startingBlockNumber := int64(1) + catFileBlockNumber := int64(2) + endingBlockNumber := int64(3) + blockNumbers := []int64{startingBlockNumber, catFileBlockNumber, endingBlockNumber, endingBlockNumber + 1} + var headerIDs []int64 + for _, n := range blockNumbers { + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + headerIDs = append(headerIDs, headerID) + Expect(err).NotTo(HaveOccurred()) + } + catFileRepository := flip.NewCatFileFlipRepository(db) + err := catFileRepository.MarkHeaderChecked(headerIDs[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := catFileRepository.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 cat file flip logs have been checked", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + startingBlockNumber := int64(1) + catFiledBlockNumber := int64(2) + endingBlockNumber := int64(3) + blockNumbers := []int64{startingBlockNumber, catFiledBlockNumber, endingBlockNumber, endingBlockNumber + 1} + var headerIDs []int64 + for _, n := range blockNumbers { + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + headerIDs = append(headerIDs, headerID) + Expect(err).NotTo(HaveOccurred()) + } + catFiledRepository := flip.NewCatFileFlipRepository(db) + _, err := db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIDs[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := catFiledRepository.MissingHeaders(startingBlockNumber, endingBlockNumber) + + Expect(err).NotTo(HaveOccurred()) + Expect(len(headers)).To(Equal(3)) + Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + }) + + It("only returns headers associated with the current node", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + blockNumbers := []int64{1, 2, 3} + headerRepository := repositories.NewHeaderRepository(db) + 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()) + } + catFileRepository := flip.NewCatFileFlipRepository(db) + catFileRepositoryTwo := flip.NewCatFileFlipRepository(dbTwo) + err := catFileRepository.MarkHeaderChecked(headerIDs[0]) + Expect(err).NotTo(HaveOccurred()) + + nodeOneMissingHeaders, err := catFileRepository.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeOneMissingHeaders)).To(Equal(len(blockNumbers) - 1)) + + nodeTwoMissingHeaders, err := catFileRepositoryTwo.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeTwoMissingHeaders)).To(Equal(len(blockNumbers))) + }) + }) +}) diff --git a/pkg/transformers/cat_file/flip/transformer.go b/pkg/transformers/cat_file/flip/transformer.go new file mode 100644 index 00000000..2be24b6b --- /dev/null +++ b/pkg/transformers/cat_file/flip/transformer.go @@ -0,0 +1,76 @@ +// 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 flip + +import ( + "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/cat_file" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +type CatFileFlipTransformerInitializer struct { + Config shared.TransformerConfig +} + +func (initializer CatFileFlipTransformerInitializer) NewCatFileFlipTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { + converter := CatFileFlipConverter{} + fetcher := shared.NewFetcher(blockChain) + repository := NewCatFileFlipRepository(db) + return CatFileFlipTransformer{ + Config: initializer.Config, + Converter: converter, + Fetcher: fetcher, + Repository: repository, + } +} + +type CatFileFlipTransformer struct { + Config shared.TransformerConfig + Converter Converter + Fetcher shared.LogFetcher + Repository Repository +} + +func (transformer CatFileFlipTransformer) Execute() error { + missingHeaders, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber) + if err != nil { + return err + } + for _, header := range missingHeaders { + topics := [][]common.Hash{{common.HexToHash(shared.CatFileFlipSignature)}} + matchingLogs, err := transformer.Fetcher.FetchLogs(cat_file.CatFileConfig.ContractAddresses, topics, header.BlockNumber) + if err != nil { + return err + } + if len(matchingLogs) < 1 { + err = transformer.Repository.MarkHeaderChecked(header.Id) + if err != nil { + return err + } + } + 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/cat_file/flip/transformer_test.go b/pkg/transformers/cat_file/flip/transformer_test.go new file mode 100644 index 00000000..e47c3db2 --- /dev/null +++ b/pkg/transformers/cat_file/flip/transformer_test.go @@ -0,0 +1,211 @@ +// 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 flip_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/cat_file" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/flip" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" + flip_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/cat_file/flip" +) + +var _ = Describe("Cat file flip transformer", func() { + It("gets missing headers for block numbers specified in config", func() { + repository := &flip_mocks.MockCatFileFlipRepository{} + transformer := flip.CatFileFlipTransformer{ + Config: cat_file.CatFileConfig, + Fetcher: &mocks.MockLogFetcher{}, + Converter: &flip_mocks.MockCatFileFlipConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedStartingBlockNumber).To(Equal(cat_file.CatFileConfig.StartingBlockNumber)) + Expect(repository.PassedEndingBlockNumber).To(Equal(cat_file.CatFileConfig.EndingBlockNumber)) + }) + + It("returns error if repository returns error for missing headers", func() { + repository := &flip_mocks.MockCatFileFlipRepository{} + repository.SetMissingHeadersErr(fakes.FakeError) + transformer := flip.CatFileFlipTransformer{ + Fetcher: &mocks.MockLogFetcher{}, + Converter: &flip_mocks.MockCatFileFlipConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("fetches logs for missing headers", func() { + fetcher := &mocks.MockLogFetcher{} + repository := &flip_mocks.MockCatFileFlipRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}, {BlockNumber: 2}}) + transformer := flip.CatFileFlipTransformer{ + Fetcher: fetcher, + Converter: &flip_mocks.MockCatFileFlipConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(fetcher.FetchedBlocks).To(Equal([]int64{1, 2})) + Expect(fetcher.FetchedContractAddresses).To(Equal([][]string{cat_file.CatFileConfig.ContractAddresses, cat_file.CatFileConfig.ContractAddresses})) + Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.CatFileFlipSignature)}})) + }) + + It("returns error if fetcher returns error", func() { + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetcherError(fakes.FakeError) + repository := &flip_mocks.MockCatFileFlipRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := flip.CatFileFlipTransformer{ + Fetcher: fetcher, + Converter: &flip_mocks.MockCatFileFlipConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("marks header checked if no logs returned", func() { + mockConverter := &flip_mocks.MockCatFileFlipConverter{} + mockRepository := &flip_mocks.MockCatFileFlipRepository{} + headerID := int64(123) + mockRepository.SetMissingHeaders([]core.Header{{Id: headerID}}) + mockFetcher := &mocks.MockLogFetcher{} + transformer := flip.CatFileFlipTransformer{ + Converter: mockConverter, + Fetcher: mockFetcher, + Repository: mockRepository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + mockRepository.AssertMarkHeaderCheckedCalledWith(headerID) + }) + + It("returns error if marking header checked returns err", func() { + mockConverter := &flip_mocks.MockCatFileFlipConverter{} + mockRepository := &flip_mocks.MockCatFileFlipRepository{} + mockRepository.SetMissingHeaders([]core.Header{{Id: int64(123)}}) + mockRepository.SetMarkHeaderCheckedErr(fakes.FakeError) + mockFetcher := &mocks.MockLogFetcher{} + transformer := flip.CatFileFlipTransformer{ + Converter: mockConverter, + Fetcher: mockFetcher, + Repository: mockRepository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("converts matching logs", func() { + converter := &flip_mocks.MockCatFileFlipConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileFlipLog}) + repository := &flip_mocks.MockCatFileFlipRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := flip.CatFileFlipTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(converter.PassedLogs).To(Equal([]types.Log{test_data.EthCatFileFlipLog})) + }) + + It("returns error if converter returns error", func() { + converter := &flip_mocks.MockCatFileFlipConverter{} + converter.SetConverterError(fakes.FakeError) + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileFlipLog}) + repository := &flip_mocks.MockCatFileFlipRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := flip.CatFileFlipTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("persists cat file flip model", func() { + converter := &flip_mocks.MockCatFileFlipConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileFlipLog}) + repository := &flip_mocks.MockCatFileFlipRepository{} + fakeHeader := core.Header{BlockNumber: 1, Id: 2} + repository.SetMissingHeaders([]core.Header{fakeHeader}) + transformer := flip.CatFileFlipTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedHeaderID).To(Equal(fakeHeader.Id)) + Expect(repository.PassedModels).To(Equal([]flip.CatFileFlipModel{test_data.CatFileFlipModel})) + }) + + It("returns error if repository returns error for create", func() { + converter := &flip_mocks.MockCatFileFlipConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFileFlipLog}) + repository := &flip_mocks.MockCatFileFlipRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1, Id: 2}}) + repository.SetCreateError(fakes.FakeError) + transformer := flip.CatFileFlipTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) +}) diff --git a/pkg/transformers/cat_file/pit_vow/converter.go b/pkg/transformers/cat_file/pit_vow/converter.go new file mode 100644 index 00000000..7eb22b1e --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/converter.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 pit_vow + +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 Converter interface { + ToModels(ethLogs []types.Log) ([]CatFilePitVowModel, error) +} + +type CatFilePitVowConverter struct{} + +func (CatFilePitVowConverter) ToModels(ethLogs []types.Log) ([]CatFilePitVowModel, error) { + var results []CatFilePitVowModel + for _, ethLog := range ethLogs { + err := verifyLog(ethLog) + if err != nil { + return nil, err + } + what := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) + data := common.BytesToAddress(ethLog.Topics[3].Bytes()).String() + + raw, err := json.Marshal(ethLog) + if err != nil { + return nil, err + } + result := CatFilePitVowModel{ + What: what, + Data: data, + TransactionIndex: ethLog.TxIndex, + Raw: raw, + } + results = append(results, result) + } + return results, nil +} + +func verifyLog(log types.Log) error { + if len(log.Topics) < 4 { + return errors.New("log missing topics") + } + if len(log.Data) < shared.DataItemLength { + return errors.New("log missing data") + } + return nil +} diff --git a/pkg/transformers/cat_file/pit_vow/converter_test.go b/pkg/transformers/cat_file/pit_vow/converter_test.go new file mode 100644 index 00000000..01588a4f --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/converter_test.go @@ -0,0 +1,58 @@ +// 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 pit_vow_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/transformers/cat_file/pit_vow" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +var _ = Describe("Cat file pit vow converter", func() { + It("returns err if log is missing topics", func() { + converter := pit_vow.CatFilePitVowConverter{} + badLog := types.Log{ + Data: []byte{1, 1, 1, 1, 1}, + } + + _, err := converter.ToModels([]types.Log{badLog}) + + Expect(err).To(HaveOccurred()) + }) + + It("returns err if log is missing data", func() { + converter := pit_vow.CatFilePitVowConverter{} + badLog := types.Log{ + Topics: []common.Hash{{}, {}, {}, {}}, + } + + _, err := converter.ToModels([]types.Log{badLog}) + + Expect(err).To(HaveOccurred()) + }) + + It("converts a log to an model", func() { + converter := pit_vow.CatFilePitVowConverter{} + + model, err := converter.ToModels([]types.Log{test_data.EthCatFilePitVowLog}) + + Expect(err).NotTo(HaveOccurred()) + Expect(model).To(Equal([]pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel})) + }) +}) diff --git a/pkg/transformers/cat_file/pit_vow/model.go b/pkg/transformers/cat_file/pit_vow/model.go new file mode 100644 index 00000000..b0e4cf62 --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/model.go @@ -0,0 +1,22 @@ +// 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 pit_vow + +type CatFilePitVowModel struct { + What string + Data string + TransactionIndex uint `db:"tx_idx"` + Raw []byte `db:"raw_log"` +} diff --git a/pkg/transformers/cat_file/pit_vow/pit_vow_suite_test.go b/pkg/transformers/cat_file/pit_vow/pit_vow_suite_test.go new file mode 100644 index 00000000..56a4aa36 --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/pit_vow_suite_test.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 pit_vow_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestPitVow(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "PitVow Suite") +} diff --git a/pkg/transformers/cat_file/pit_vow/repository.go b/pkg/transformers/cat_file/pit_vow/repository.go new file mode 100644 index 00000000..1e81949b --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/repository.go @@ -0,0 +1,86 @@ +// 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 pit_vow + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" +) + +type Repository interface { + Create(headerID int64, models []CatFilePitVowModel) error + MarkHeaderChecked(headerID int64) error + MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) +} + +type CatFilePitVowRepository struct { + db *postgres.DB +} + +func NewCatFilePitVowRepository(db *postgres.DB) CatFilePitVowRepository { + return CatFilePitVowRepository{db: db} +} + +func (repository CatFilePitVowRepository) Create(headerID int64, models []CatFilePitVowModel) error { + tx, err := repository.db.Begin() + if err != nil { + return err + } + for _, model := range models { + _, err = repository.db.Exec( + `INSERT into maker.cat_file_pit_vow (header_id, what, data, tx_idx, raw_log) + VALUES($1, $2, $3, $4, $5)`, + headerID, model.What, model.Data, model.TransactionIndex, model.Raw, + ) + if err != nil { + tx.Rollback() + return err + } + } + _, err = tx.Exec(`INSERT INTO public.checked_headers (header_id, cat_file_pit_vow_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET cat_file_pit_vow_checked = $2`, headerID, true) + if err != nil { + tx.Rollback() + return err + } + return tx.Commit() +} + +func (repository CatFilePitVowRepository) MarkHeaderChecked(headerID int64) error { + _, err := repository.db.Exec(`INSERT INTO public.checked_headers (header_id, cat_file_pit_vow_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET cat_file_pit_vow_checked = $2`, headerID, true) + return err +} + +func (repository CatFilePitVowRepository) 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 cat_file_pit_vow_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 +} diff --git a/pkg/transformers/cat_file/pit_vow/repository_test.go b/pkg/transformers/cat_file/pit_vow/repository_test.go new file mode 100644 index 00000000..0542410c --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/repository_test.go @@ -0,0 +1,214 @@ +// 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 pit_vow_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/cat_file/pit_vow" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("Cat file pit vow repository", func() { + Describe("Create", func() { + var catFileRepository pit_vow.CatFilePitVowRepository + var db *postgres.DB + var err error + var headerID int64 + + BeforeEach(func() { + db = test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err = headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository = pit_vow.NewCatFilePitVowRepository(db) + }) + + It("adds a cat file pit vow event", func() { + err = catFileRepository.Create(headerID, []pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel}) + + Expect(err).NotTo(HaveOccurred()) + var dbPitFile pit_vow.CatFilePitVowModel + err = db.Get(&dbPitFile, `SELECT what, data, tx_idx, raw_log FROM maker.cat_file_pit_vow WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(dbPitFile.What).To(Equal(test_data.CatFilePitVowModel.What)) + Expect(dbPitFile.Data).To(Equal(test_data.CatFilePitVowModel.Data)) + Expect(dbPitFile.TransactionIndex).To(Equal(test_data.CatFilePitVowModel.TransactionIndex)) + Expect(dbPitFile.Raw).To(MatchJSON(test_data.CatFilePitVowModel.Raw)) + }) + + It("marks header as checked for logs", func() { + err = catFileRepository.Create(headerID, []pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel}) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_pit_vow_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + + It("does not duplicate cat file pit vow events", func() { + err = catFileRepository.Create(headerID, []pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel}) + Expect(err).NotTo(HaveOccurred()) + + err = catFileRepository.Create(headerID, []pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel}) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint")) + }) + + It("removes cat file pit vow if corresponding header is deleted", func() { + err = catFileRepository.Create(headerID, []pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel}) + Expect(err).NotTo(HaveOccurred()) + + _, err = db.Exec(`DELETE FROM headers WHERE id = $1`, headerID) + + Expect(err).NotTo(HaveOccurred()) + var dbPitFile pit_vow.CatFilePitVowModel + err = db.Get(&dbPitFile, `SELECT what, data, tx_idx, raw_log FROM maker.cat_file_pit_vow WHERE header_id = $1`, headerID) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(sql.ErrNoRows)) + }) + }) + + Describe("MarkHeaderChecked", func() { + It("creates a row for a new headerID", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository := pit_vow.NewCatFilePitVowRepository(db) + + err = catFileRepository.MarkHeaderChecked(headerID) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_pit_vow_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() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + catFileRepository := pit_vow.NewCatFilePitVowRepository(db) + _, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerID) + + err = catFileRepository.MarkHeaderChecked(headerID) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT cat_file_pit_vow_checked FROM public.checked_headers WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + }) + + Describe("MissingHeaders", func() { + It("returns headers that haven't been checked", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + startingBlockNumber := int64(1) + catFileBlockNumber := int64(2) + endingBlockNumber := int64(3) + blockNumbers := []int64{startingBlockNumber, catFileBlockNumber, endingBlockNumber, endingBlockNumber + 1} + var headerIDs []int64 + for _, n := range blockNumbers { + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + headerIDs = append(headerIDs, headerID) + Expect(err).NotTo(HaveOccurred()) + } + catFileRepository := pit_vow.NewCatFilePitVowRepository(db) + err := catFileRepository.MarkHeaderChecked(headerIDs[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := catFileRepository.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 cat file pit vow logs have been checked", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + startingBlockNumber := int64(1) + catFiledBlockNumber := int64(2) + endingBlockNumber := int64(3) + blockNumbers := []int64{startingBlockNumber, catFiledBlockNumber, endingBlockNumber, endingBlockNumber + 1} + var headerIDs []int64 + for _, n := range blockNumbers { + headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n}) + headerIDs = append(headerIDs, headerID) + Expect(err).NotTo(HaveOccurred()) + } + catFiledRepository := pit_vow.NewCatFilePitVowRepository(db) + _, err := db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIDs[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := catFiledRepository.MissingHeaders(startingBlockNumber, endingBlockNumber) + + Expect(err).NotTo(HaveOccurred()) + Expect(len(headers)).To(Equal(3)) + Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(catFiledBlockNumber))) + }) + + It("only returns headers associated with the current node", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + blockNumbers := []int64{1, 2, 3} + headerRepository := repositories.NewHeaderRepository(db) + 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()) + } + catFileRepository := pit_vow.NewCatFilePitVowRepository(db) + catFileRepositoryTwo := pit_vow.NewCatFilePitVowRepository(dbTwo) + err := catFileRepository.MarkHeaderChecked(headerIDs[0]) + Expect(err).NotTo(HaveOccurred()) + + nodeOneMissingHeaders, err := catFileRepository.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeOneMissingHeaders)).To(Equal(len(blockNumbers) - 1)) + + nodeTwoMissingHeaders, err := catFileRepositoryTwo.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeTwoMissingHeaders)).To(Equal(len(blockNumbers))) + }) + }) +}) diff --git a/pkg/transformers/cat_file/pit_vow/transformer.go b/pkg/transformers/cat_file/pit_vow/transformer.go new file mode 100644 index 00000000..c748b572 --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/transformer.go @@ -0,0 +1,75 @@ +// 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 pit_vow + +import ( + "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/cat_file" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +type CatFilePitVowTransformerInitializer struct { + Config shared.TransformerConfig +} + +func (initializer CatFilePitVowTransformerInitializer) NewCatFilePitVowTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { + converter := CatFilePitVowConverter{} + fetcher := shared.NewFetcher(blockChain) + repository := NewCatFilePitVowRepository(db) + return CatFilePitVowTransformer{ + Config: initializer.Config, + Converter: converter, + Fetcher: fetcher, + Repository: repository, + } +} + +type CatFilePitVowTransformer struct { + Config shared.TransformerConfig + Converter Converter + Fetcher shared.LogFetcher + Repository Repository +} + +func (transformer CatFilePitVowTransformer) Execute() error { + missingHeaders, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber) + if err != nil { + return err + } + for _, header := range missingHeaders { + topics := [][]common.Hash{{common.HexToHash(shared.CatFilePitVowSignature)}} + matchingLogs, err := transformer.Fetcher.FetchLogs(cat_file.CatFileConfig.ContractAddresses, topics, header.BlockNumber) + if err != nil { + return err + } + if len(matchingLogs) < 1 { + err = transformer.Repository.MarkHeaderChecked(header.Id) + if err != nil { + return err + } + } + 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/cat_file/pit_vow/transformer_test.go b/pkg/transformers/cat_file/pit_vow/transformer_test.go new file mode 100644 index 00000000..b9e894b1 --- /dev/null +++ b/pkg/transformers/cat_file/pit_vow/transformer_test.go @@ -0,0 +1,211 @@ +// 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 pit_vow_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/cat_file" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/pit_vow" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" + pit_vow_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/cat_file/pit_vow" +) + +var _ = Describe("Cat file pit vow transformer", func() { + It("gets missing headers for block numbers specified in config", func() { + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + transformer := pit_vow.CatFilePitVowTransformer{ + Config: cat_file.CatFileConfig, + Fetcher: &mocks.MockLogFetcher{}, + Converter: &pit_vow_mocks.MockCatFilePitVowConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedStartingBlockNumber).To(Equal(cat_file.CatFileConfig.StartingBlockNumber)) + Expect(repository.PassedEndingBlockNumber).To(Equal(cat_file.CatFileConfig.EndingBlockNumber)) + }) + + It("returns error if repository returns error for missing headers", func() { + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + repository.SetMissingHeadersErr(fakes.FakeError) + transformer := pit_vow.CatFilePitVowTransformer{ + Fetcher: &mocks.MockLogFetcher{}, + Converter: &pit_vow_mocks.MockCatFilePitVowConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("fetches logs for missing headers", func() { + fetcher := &mocks.MockLogFetcher{} + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}, {BlockNumber: 2}}) + transformer := pit_vow.CatFilePitVowTransformer{ + Fetcher: fetcher, + Converter: &pit_vow_mocks.MockCatFilePitVowConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(fetcher.FetchedBlocks).To(Equal([]int64{1, 2})) + Expect(fetcher.FetchedContractAddresses).To(Equal([][]string{cat_file.CatFileConfig.ContractAddresses, cat_file.CatFileConfig.ContractAddresses})) + Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.CatFilePitVowSignature)}})) + }) + + It("returns error if fetcher returns error", func() { + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetcherError(fakes.FakeError) + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := pit_vow.CatFilePitVowTransformer{ + Fetcher: fetcher, + Converter: &pit_vow_mocks.MockCatFilePitVowConverter{}, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("marks header checked if no logs returned", func() { + mockConverter := &pit_vow_mocks.MockCatFilePitVowConverter{} + mockRepository := &pit_vow_mocks.MockCatFilePitVowRepository{} + headerID := int64(123) + mockRepository.SetMissingHeaders([]core.Header{{Id: headerID}}) + mockFetcher := &mocks.MockLogFetcher{} + transformer := pit_vow.CatFilePitVowTransformer{ + Converter: mockConverter, + Fetcher: mockFetcher, + Repository: mockRepository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + mockRepository.AssertMarkHeaderCheckedCalledWith(headerID) + }) + + It("returns error if marking header checked returns err", func() { + mockConverter := &pit_vow_mocks.MockCatFilePitVowConverter{} + mockRepository := &pit_vow_mocks.MockCatFilePitVowRepository{} + mockRepository.SetMissingHeaders([]core.Header{{Id: int64(123)}}) + mockRepository.SetMarkHeaderCheckedErr(fakes.FakeError) + mockFetcher := &mocks.MockLogFetcher{} + transformer := pit_vow.CatFilePitVowTransformer{ + Converter: mockConverter, + Fetcher: mockFetcher, + Repository: mockRepository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("converts matching logs", func() { + converter := &pit_vow_mocks.MockCatFilePitVowConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFilePitVowLog}) + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := pit_vow.CatFilePitVowTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(converter.PassedLogs).To(Equal([]types.Log{test_data.EthCatFilePitVowLog})) + }) + + It("returns error if converter returns error", func() { + converter := &pit_vow_mocks.MockCatFilePitVowConverter{} + converter.SetConverterError(fakes.FakeError) + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFilePitVowLog}) + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1}}) + transformer := pit_vow.CatFilePitVowTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("persists cat file pit vow model", func() { + converter := &pit_vow_mocks.MockCatFilePitVowConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFilePitVowLog}) + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + fakeHeader := core.Header{BlockNumber: 1, Id: 2} + repository.SetMissingHeaders([]core.Header{fakeHeader}) + transformer := pit_vow.CatFilePitVowTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedHeaderID).To(Equal(fakeHeader.Id)) + Expect(repository.PassedModels).To(Equal([]pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel})) + }) + + It("returns error if repository returns error for create", func() { + converter := &pit_vow_mocks.MockCatFilePitVowConverter{} + fetcher := &mocks.MockLogFetcher{} + fetcher.SetFetchedLogs([]types.Log{test_data.EthCatFilePitVowLog}) + repository := &pit_vow_mocks.MockCatFilePitVowRepository{} + repository.SetMissingHeaders([]core.Header{{BlockNumber: 1, Id: 2}}) + repository.SetCreateError(fakes.FakeError) + transformer := pit_vow.CatFilePitVowTransformer{ + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) +}) diff --git a/pkg/transformers/flop_kick/converter_test.go b/pkg/transformers/flop_kick/converter_test.go index 6575da73..f7b5219b 100644 --- a/pkg/transformers/flop_kick/converter_test.go +++ b/pkg/transformers/flop_kick/converter_test.go @@ -20,10 +20,10 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/ethereum/go-ethereum/core/types" "github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick" "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" - "github.com/ethereum/go-ethereum/core/types" ) var _ = Describe("FlopKick Converter", func() { diff --git a/pkg/transformers/shared/constants.go b/pkg/transformers/shared/constants.go index 1b7a329d..fd3f03d3 100644 --- a/pkg/transformers/shared/constants.go +++ b/pkg/transformers/shared/constants.go @@ -17,7 +17,7 @@ package shared var ( DataItemLength = 32 - CatABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"flips\",\"outputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"},{\"name\":\"ink\",\"type\":\"uint256\"},{\"name\":\"tab\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"nflip\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"name\":\"flip\",\"type\":\"address\"},{\"name\":\"chop\",\"type\":\"uint256\"},{\"name\":\"lump\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pit\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"vat_\",\"type\":\"address\"},{\"name\":\"pit_\",\"type\":\"address\"},{\"name\":\"vow_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"lad\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ink\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"art\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"flip\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"iArt\",\"type\":\"uint256\"}],\"name\":\"Bite\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"flip\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"}],\"name\":\"bite\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"n\",\"type\":\"uint256\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"flip\",\"outputs\":[{\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + CatABI = `[{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x36569e77"},{"constant":true,"inputs":[],"name":"vow","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x626cb3c5"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"flips","outputs":[{"name":"ilk","type":"bytes32"},{"name":"urn","type":"bytes32"},{"name":"ink","type":"uint256"},{"name":"tab","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70d9235a"},{"constant":true,"inputs":[],"name":"nflip","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x76181a51"},{"constant":true,"inputs":[],"name":"live","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x957aa58c"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"flip","type":"address"},{"name":"chop","type":"uint256"},{"name":"lump","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"constant":true,"inputs":[],"name":"pit","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf03c7c6e"},{"inputs":[{"name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"ilk","type":"bytes32"},{"indexed":true,"name":"urn","type":"bytes32"},{"indexed":false,"name":"ink","type":"uint256"},{"indexed":false,"name":"art","type":"uint256"},{"indexed":false,"name":"tab","type":"uint256"},{"indexed":false,"name":"flip","type":"uint256"},{"indexed":false,"name":"iArt","type":"uint256"}],"name":"Bite","type":"event","signature":"0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x1a0b287e"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xd4e8be83"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"what","type":"bytes32"},{"name":"flip","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xebecb39d"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"urn","type":"bytes32"}],"name":"bite","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x72f7b593"},{"constant":false,"inputs":[{"name":"n","type":"uint256"},{"name":"wad","type":"uint256"}],"name":"flip","outputs":[{"name":"id","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe6f95917"}]` DripABI = `[{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x36569e77"},{"constant":true,"inputs":[],"name":"repo","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x56ff3122"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"vow","type":"bytes32"},{"name":"tax","type":"uint256"},{"name":"rho","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"inputs":[{"name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x143e55e0"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"vow","type":"bytes32"},{"name":"tax","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x1a0b287e"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x29ae8114"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"}],"name":"drip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x44e2a5a8"}]` FlipperABI = `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"bids","outputs":[{"name":"bid","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"guy","type":"address"},{"name":"tic","type":"uint48"},{"name":"end","type":"uint48"},{"name":"urn","type":"bytes32"},{"name":"gal","type":"address"},{"name":"tab","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4423c5f1"},{"constant":true,"inputs":[],"name":"ttl","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4e8b1dd5"},{"constant":true,"inputs":[],"name":"gem","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x7bd2bea7"},{"constant":true,"inputs":[],"name":"beg","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x7d780d82"},{"constant":true,"inputs":[],"name":"tau","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xcfc4af55"},{"constant":true,"inputs":[],"name":"kicks","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xcfdd3302"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf4b9fa75"},{"inputs":[{"name":"dai_","type":"address"},{"name":"gem_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"id","type":"uint256"},{"indexed":false,"name":"lot","type":"uint256"},{"indexed":false,"name":"bid","type":"uint256"},{"indexed":false,"name":"gal","type":"address"},{"indexed":false,"name":"end","type":"uint48"},{"indexed":true,"name":"urn","type":"bytes32"},{"indexed":false,"name":"tab","type":"uint256"}],"name":"Kick","type":"event","signature":"0xbac86238bdba81d21995024470425ecb370078fa62b7271b90cf28cbd1e3e87e"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x143e55e0"},{"constant":false,"inputs":[{"name":"urn","type":"bytes32"},{"name":"gal","type":"address"},{"name":"tab","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"kick","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xeae19d9e"},{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"tick","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfc7b6aee"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"tend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4b43ed12"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"dent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5ff3a382"},{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"deal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc959c42b"}]` FlopperABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"era\",\"outputs\":[{\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"bids\",\"outputs\":[{\"name\":\"bid\",\"type\":\"uint256\"},{\"name\":\"lot\",\"type\":\"uint256\"},{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"tic\",\"type\":\"uint48\"},{\"name\":\"end\",\"type\":\"uint48\"},{\"name\":\"vow\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ttl\",\"outputs\":[{\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"id\",\"type\":\"uint256\"},{\"name\":\"lot\",\"type\":\"uint256\"},{\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"dent\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"beg\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"gal\",\"type\":\"address\"},{\"name\":\"lot\",\"type\":\"uint256\"},{\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"kick\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"deal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tau\",\"outputs\":[{\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"kicks\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dai\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"dai_\",\"type\":\"address\"},{\"name\":\"gem_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"bid\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"gal\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"end\",\"type\":\"uint48\"}],\"name\":\"Kick\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"}]" @@ -38,6 +38,9 @@ var ( //TODO: get pit and drip file method signatures directly from the ABI biteMethod = GetSolidityMethodSignature(CatABI, "Bite") + catFileChopLumpMethod = "file(bytes32,bytes32,uint256)" + catFileFlipMethod = GetSolidityMethodSignature(CatABI, "file") + catFilePitVowMethod = "file(bytes32,address)" dealMethod = GetSolidityMethodSignature(FlipperABI, "deal") dentMethod = GetSolidityMethodSignature(FlipperABI, "dent") dripDripMethod = GetSolidityMethodSignature(DripABI, "drip") @@ -56,6 +59,9 @@ var ( BiteSignature = GetEventSignature(biteMethod) DealSignature = GetLogNoteSignature(dealMethod) + CatFileChopLumpSignature = GetLogNoteSignature(catFileChopLumpMethod) + CatFileFlipSignature = GetLogNoteSignature(catFileFlipMethod) + CatFilePitVowSignature = GetLogNoteSignature(catFilePitVowMethod) DentFunctionSignature = GetLogNoteSignature(dentMethod) DripDripSignature = GetLogNoteSignature(dripDripMethod) DripFileIlkSignature = GetLogNoteSignature(dripFileIlkMethod) diff --git a/pkg/transformers/shared/event_signature_generator_test.go b/pkg/transformers/shared/event_signature_generator_test.go index f45a3c01..3bed6e80 100644 --- a/pkg/transformers/shared/event_signature_generator_test.go +++ b/pkg/transformers/shared/event_signature_generator_test.go @@ -84,6 +84,13 @@ var _ = Describe("Event signature generator", func() { Describe("getting the solidity method/event signature from the abi", func() { Describe("it handles methods", func() { + It("gets the cat file method signature", func() { + expected := "file(bytes32,bytes32,address)" + actual := shared.GetSolidityMethodSignature(shared.CatABI, "file") + + Expect(expected).To(Equal(actual)) + }) + It("gets the flip dent method signature", func() { expected := "dent(uint256,uint256,uint256)" actual := shared.GetSolidityMethodSignature(shared.FlipperABI, "dent") diff --git a/pkg/transformers/tend/integration_test.go b/pkg/transformers/tend/integration_test.go index 33904c34..ecb77a60 100644 --- a/pkg/transformers/tend/integration_test.go +++ b/pkg/transformers/tend/integration_test.go @@ -26,9 +26,9 @@ import ( rpc2 "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc" "github.com/vulcanize/vulcanizedb/pkg/geth/node" "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/tend" "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" "github.com/vulcanize/vulcanizedb/test_config" - "github.com/vulcanize/vulcanizedb/pkg/transformers/tend" ) // These test are marked as pending until the Flip contract is deployed to Kovan. diff --git a/pkg/transformers/test_data/bite.go b/pkg/transformers/test_data/bite.go index 7d30aabd..efdccb8e 100644 --- a/pkg/transformers/test_data/bite.go +++ b/pkg/transformers/test_data/bite.go @@ -64,7 +64,7 @@ var EthBiteLog = types.Log{ var BiteEntity = bite.BiteEntity{ Id: big.NewInt(biteId), Ilk: biteIlk, - Lad: biteLad, + Urn: biteLad, Ink: biteInk, Art: biteArt, Tab: biteTab, @@ -77,7 +77,7 @@ var BiteEntity = bite.BiteEntity{ var BiteModel = bite.BiteModel{ Id: strconv.FormatInt(biteId, 10), Ilk: biteIlk[:], - Lad: biteLad[:], + Urn: biteLad[:], Ink: biteInk.String(), Art: biteArt.String(), Tab: biteTab.String(), diff --git a/pkg/transformers/test_data/cat_file.go b/pkg/transformers/test_data/cat_file.go new file mode 100644 index 00000000..660b6a20 --- /dev/null +++ b/pkg/transformers/test_data/cat_file.go @@ -0,0 +1,104 @@ +// 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/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/chop_lump" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/flip" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/pit_vow" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "math/big" +) + +var EthCatFileChopLumpLog = types.Log{ + Address: common.HexToAddress(shared.CatContractAddress), + Topics: []common.Hash{ + common.HexToHash("0x1a0b287e00000000000000000000000000000000000000000000000000000000"), + common.HexToHash("0x00000000000000000000000064d922894153be9eef7b7218dc565d1d0ce2a092"), + common.HexToHash("0x66616b6520696c6b000000000000000000000000000000000000000000000000"), + common.HexToHash("0x63686f7000000000000000000000000000000000000000000000000000000000"), + }, + Data: hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000641a0b287e66616b6520696c6b00000000000000000000000000000000000000000000000063686f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075bcd15"), + BlockNumber: 110, + TxHash: common.HexToHash("0xe32dfe6afd7ea28475569756fc30f0eea6ad4cfd32f67436ff1d1c805e4382df"), + TxIndex: 13, + BlockHash: common.HexToHash("0x2764998a4e048d4c4ba45ea40fd5efaa8e2d4f1dd2b15425a6c6a3dea7f1064a"), + Index: 0, + Removed: false, +} + +var rawCatFileChopLumpLog, _ = json.Marshal(EthCatFileChopLumpLog) +var CatFileChopLumpModel = chop_lump.CatFileChopLumpModel{ + Ilk: "fake ilk", + What: "chop", + Data: big.NewInt(123456789).String(), + TransactionIndex: EthCatFileChopLumpLog.TxIndex, + Raw: rawCatFileChopLumpLog, +} + +var EthCatFileFlipLog = types.Log{ + Address: common.HexToAddress(shared.CatContractAddress), + Topics: []common.Hash{ + common.HexToHash("0xebecb39d00000000000000000000000000000000000000000000000000000000"), + common.HexToHash("0x00000000000000000000000064d922894153be9eef7b7218dc565d1d0ce2a092"), + common.HexToHash("0x66616b6520696c6b000000000000000000000000000000000000000000000000"), + common.HexToHash("0x666c697000000000000000000000000000000000000000000000000000000000"), + }, + Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000064ebecb39d66616b6520696c6b000000000000000000000000000000000000000000000000666c69700000000000000000000000000000000000000000000000000000000000000000000000000000000007fa9ef6609ca7921112231f8f195138ebba2977"), + BlockNumber: 88, + TxHash: common.HexToHash("0xc71ef3e9999595913d31e89446cab35319bd4289520e55611a1b42fc2a8463b6"), + TxIndex: 12, + BlockHash: common.HexToHash("0xe5fcc1b65dd901e003e3768c1d4ce58d72f5f3a31e6a5d27d9cbdc7dca4bb405"), + Index: 0, + Removed: false, +} + +var rawCatFileFlipLog, _ = json.Marshal(EthCatFileFlipLog) +var CatFileFlipModel = flip.CatFileFlipModel{ + Ilk: "fake ilk", + What: "flip", + Flip: "0x07Fa9eF6609cA7921112231F8f195138ebbA2977", + TransactionIndex: EthCatFileFlipLog.TxIndex, + Raw: rawCatFileFlipLog, +} + +var EthCatFilePitVowLog = types.Log{ + Address: common.HexToAddress(shared.CatContractAddress), + Topics: []common.Hash{ + common.HexToHash("0xd4e8be8300000000000000000000000000000000000000000000000000000000"), + common.HexToHash("0x00000000000000000000000064d922894153be9eef7b7218dc565d1d0ce2a092"), + common.HexToHash("0x7069740000000000000000000000000000000000000000000000000000000000"), + common.HexToHash("0x0000000000000000000000008e84a1e068d77059cbe263c43ad0cdc130863313"), + }, + Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000044d4e8be8370697400000000000000000000000000000000000000000000000000000000000000000000000000000000008e84a1e068d77059cbe263c43ad0cdc130863313"), + BlockNumber: 87, + TxHash: common.HexToHash("0x6515c7dfe53f0ad83ce1173fa99032c24a07cfd8b5d5a1c1f80486c99dd52800"), + TxIndex: 11, + BlockHash: common.HexToHash("0xae75936bc6b6a3383e8c991686747ef2221984b0ec8a5d4a6350989ec0ddbd67"), + Index: 0, + Removed: false, +} + +var rawCatFilePitVowLog, _ = json.Marshal(EthCatFilePitVowLog) +var CatFilePitVowModel = pit_vow.CatFilePitVowModel{ + What: "pit", + Data: "0x8E84a1e068d77059Cbe263C43AD0cDc130863313", + TransactionIndex: EthCatFilePitVowLog.TxIndex, + Raw: rawCatFilePitVowLog, +} diff --git a/pkg/transformers/test_data/deal.go b/pkg/transformers/test_data/deal.go index af70ef76..c0a8862d 100644 --- a/pkg/transformers/test_data/deal.go +++ b/pkg/transformers/test_data/deal.go @@ -46,4 +46,4 @@ var DealModel = deal.DealModel{ ContractAddress: shared.FlipperContractAddress, TransactionIndex: 74, Raw: dealRawJson, -} \ No newline at end of file +} diff --git a/pkg/transformers/test_data/mocks/cat_file/chop_lump/converter.go b/pkg/transformers/test_data/mocks/cat_file/chop_lump/converter.go new file mode 100644 index 00000000..559cd05b --- /dev/null +++ b/pkg/transformers/test_data/mocks/cat_file/chop_lump/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 chop_lump + +import ( + "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/chop_lump" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +type MockCatFileChopLumpConverter struct { + Err error + PassedLogs []types.Log +} + +func (converter *MockCatFileChopLumpConverter) ToModels(ethLogs []types.Log) ([]chop_lump.CatFileChopLumpModel, error) { + converter.PassedLogs = ethLogs + return []chop_lump.CatFileChopLumpModel{test_data.CatFileChopLumpModel}, converter.Err +} + +func (converter *MockCatFileChopLumpConverter) SetConverterError(e error) { + converter.Err = e +} diff --git a/pkg/transformers/test_data/mocks/cat_file/chop_lump/repository.go b/pkg/transformers/test_data/mocks/cat_file/chop_lump/repository.go new file mode 100644 index 00000000..bebc85ab --- /dev/null +++ b/pkg/transformers/test_data/mocks/cat_file/chop_lump/repository.go @@ -0,0 +1,71 @@ +// 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 chop_lump + +import ( + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/chop_lump" +) + +type MockCatFileChopLumpRepository struct { + createErr error + markHeaderCheckedErr error + markHeaderCheckedPassedHeaderID int64 + missingHeaders []core.Header + missingHeadersErr error + PassedStartingBlockNumber int64 + PassedEndingBlockNumber int64 + PassedHeaderID int64 + PassedModels []chop_lump.CatFileChopLumpModel +} + +func (repository *MockCatFileChopLumpRepository) Create(headerID int64, models []chop_lump.CatFileChopLumpModel) error { + repository.PassedHeaderID = headerID + repository.PassedModels = models + return repository.createErr +} + +func (repository *MockCatFileChopLumpRepository) MarkHeaderChecked(headerID int64) error { + repository.markHeaderCheckedPassedHeaderID = headerID + return repository.markHeaderCheckedErr +} + +func (repository *MockCatFileChopLumpRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + repository.PassedStartingBlockNumber = startingBlockNumber + repository.PassedEndingBlockNumber = endingBlockNumber + return repository.missingHeaders, repository.missingHeadersErr +} + +func (repository *MockCatFileChopLumpRepository) SetMissingHeadersErr(e error) { + repository.missingHeadersErr = e +} + +func (repository *MockCatFileChopLumpRepository) SetMissingHeaders(headers []core.Header) { + repository.missingHeaders = headers +} + +func (repository *MockCatFileChopLumpRepository) SetCreateError(e error) { + repository.createErr = e +} + +func (repository *MockCatFileChopLumpRepository) SetMarkHeaderCheckedErr(e error) { + repository.markHeaderCheckedErr = e +} + +func (repository *MockCatFileChopLumpRepository) AssertMarkHeaderCheckedCalledWith(i int64) { + Expect(repository.markHeaderCheckedPassedHeaderID).To(Equal(i)) +} diff --git a/pkg/transformers/test_data/mocks/cat_file/flip/converter.go b/pkg/transformers/test_data/mocks/cat_file/flip/converter.go new file mode 100644 index 00000000..687ab41e --- /dev/null +++ b/pkg/transformers/test_data/mocks/cat_file/flip/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 flip + +import ( + "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/flip" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +type MockCatFileFlipConverter struct { + err error + PassedLogs []types.Log +} + +func (converter *MockCatFileFlipConverter) ToModels(ethLogs []types.Log) ([]flip.CatFileFlipModel, error) { + converter.PassedLogs = ethLogs + return []flip.CatFileFlipModel{test_data.CatFileFlipModel}, converter.err +} + +func (converter *MockCatFileFlipConverter) SetConverterError(e error) { + converter.err = e +} diff --git a/pkg/transformers/test_data/mocks/cat_file/flip/repository.go b/pkg/transformers/test_data/mocks/cat_file/flip/repository.go new file mode 100644 index 00000000..72fba300 --- /dev/null +++ b/pkg/transformers/test_data/mocks/cat_file/flip/repository.go @@ -0,0 +1,71 @@ +// 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 flip + +import ( + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/flip" +) + +type MockCatFileFlipRepository struct { + createErr error + markHeaderCheckedErr error + markHeaderCheckedPassedHeaderID int64 + missingHeadersErr error + missingHeaders []core.Header + PassedStartingBlockNumber int64 + PassedEndingBlockNumber int64 + PassedHeaderID int64 + PassedModels []flip.CatFileFlipModel +} + +func (repository *MockCatFileFlipRepository) Create(headerID int64, models []flip.CatFileFlipModel) error { + repository.PassedHeaderID = headerID + repository.PassedModels = models + return repository.createErr +} + +func (repository *MockCatFileFlipRepository) MarkHeaderChecked(headerID int64) error { + repository.markHeaderCheckedPassedHeaderID = headerID + return repository.markHeaderCheckedErr +} + +func (repository *MockCatFileFlipRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + repository.PassedStartingBlockNumber = startingBlockNumber + repository.PassedEndingBlockNumber = endingBlockNumber + return repository.missingHeaders, repository.missingHeadersErr +} + +func (repository *MockCatFileFlipRepository) SetMissingHeadersErr(e error) { + repository.missingHeadersErr = e +} + +func (repository *MockCatFileFlipRepository) SetMissingHeaders(headers []core.Header) { + repository.missingHeaders = headers +} + +func (repository *MockCatFileFlipRepository) SetCreateError(e error) { + repository.createErr = e +} + +func (repository *MockCatFileFlipRepository) SetMarkHeaderCheckedErr(e error) { + repository.markHeaderCheckedErr = e +} + +func (repository *MockCatFileFlipRepository) AssertMarkHeaderCheckedCalledWith(i int64) { + Expect(repository.markHeaderCheckedPassedHeaderID).To(Equal(i)) +} diff --git a/pkg/transformers/test_data/mocks/cat_file/pit_vow/converter.go b/pkg/transformers/test_data/mocks/cat_file/pit_vow/converter.go new file mode 100644 index 00000000..7eb6a26c --- /dev/null +++ b/pkg/transformers/test_data/mocks/cat_file/pit_vow/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 pit_vow + +import ( + "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/pit_vow" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +type MockCatFilePitVowConverter struct { + err error + PassedLogs []types.Log +} + +func (converter *MockCatFilePitVowConverter) ToModels(ethLogs []types.Log) ([]pit_vow.CatFilePitVowModel, error) { + converter.PassedLogs = ethLogs + return []pit_vow.CatFilePitVowModel{test_data.CatFilePitVowModel}, converter.err +} + +func (converter *MockCatFilePitVowConverter) SetConverterError(e error) { + converter.err = e +} diff --git a/pkg/transformers/test_data/mocks/cat_file/pit_vow/repository.go b/pkg/transformers/test_data/mocks/cat_file/pit_vow/repository.go new file mode 100644 index 00000000..f9d7e557 --- /dev/null +++ b/pkg/transformers/test_data/mocks/cat_file/pit_vow/repository.go @@ -0,0 +1,71 @@ +// 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 pit_vow + +import ( + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/pit_vow" +) + +type MockCatFilePitVowRepository struct { + createErr error + markHeaderCheckedErr error + markHeaderCheckedPassedHeaderID int64 + missingHeaders []core.Header + missingHeadersErr error + PassedStartingBlockNumber int64 + PassedEndingBlockNumber int64 + PassedHeaderID int64 + PassedModels []pit_vow.CatFilePitVowModel +} + +func (repository *MockCatFilePitVowRepository) Create(headerID int64, models []pit_vow.CatFilePitVowModel) error { + repository.PassedHeaderID = headerID + repository.PassedModels = models + return repository.createErr +} + +func (repository *MockCatFilePitVowRepository) MarkHeaderChecked(headerID int64) error { + repository.markHeaderCheckedPassedHeaderID = headerID + return repository.markHeaderCheckedErr +} + +func (repository *MockCatFilePitVowRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + repository.PassedStartingBlockNumber = startingBlockNumber + repository.PassedEndingBlockNumber = endingBlockNumber + return repository.missingHeaders, repository.missingHeadersErr +} + +func (repository *MockCatFilePitVowRepository) SetMissingHeadersErr(e error) { + repository.missingHeadersErr = e +} + +func (repository *MockCatFilePitVowRepository) SetMissingHeaders(headers []core.Header) { + repository.missingHeaders = headers +} + +func (repository *MockCatFilePitVowRepository) SetCreateError(e error) { + repository.createErr = e +} + +func (repository *MockCatFilePitVowRepository) SetMarkHeaderCheckedErr(e error) { + repository.markHeaderCheckedErr = e +} + +func (repository *MockCatFilePitVowRepository) AssertMarkHeaderCheckedCalledWith(i int64) { + Expect(repository.markHeaderCheckedPassedHeaderID).To(Equal(i)) +} diff --git a/pkg/transformers/transformers.go b/pkg/transformers/transformers.go index f371b8fd..6aa22de7 100644 --- a/pkg/transformers/transformers.go +++ b/pkg/transformers/transformers.go @@ -16,6 +16,10 @@ package transformers import ( "github.com/vulcanize/vulcanizedb/pkg/transformers/bite" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/chop_lump" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/flip" + "github.com/vulcanize/vulcanizedb/pkg/transformers/cat_file/pit_vow" "github.com/vulcanize/vulcanizedb/pkg/transformers/deal" "github.com/vulcanize/vulcanizedb/pkg/transformers/dent" "github.com/vulcanize/vulcanizedb/pkg/transformers/drip_drip" @@ -38,6 +42,10 @@ import ( var ( BiteTransformerInitializer = bite.BiteTransformerInitializer{Config: bite.BiteConfig}.NewBiteTransformer + catFileConfig = cat_file.CatFileConfig + CatFileChopLumpTransformerInitializer = chop_lump.CatFileChopLumpTransformerInitializer{Config: catFileConfig}.NewCatFileChopLumpTransformer + CatFileFlipTransformerInitializer = flip.CatFileFlipTransformerInitializer{Config: catFileConfig}.NewCatFileFlipTransformer + CatFilePitVowTransformerInitializer = pit_vow.CatFilePitVowTransformerInitializer{Config: catFileConfig}.NewCatFilePitVowTransformer DealTransformerInitializer = deal.DealTransformerInitializer{Config: deal.Config}.NewDealTransformer DentTransformerInitializer = dent.DentTransformerInitializer{Config: dent.DentConfig}.NewDentTransformer DripDripTransformerInitializer = drip_drip.DripDripTransformerInitializer{Config: drip_drip.DripDripConfig}.NewDripDripTransformer @@ -60,6 +68,9 @@ var ( func TransformerInitializers() []shared.TransformerInitializer { return []shared.TransformerInitializer{ BiteTransformerInitializer, + CatFileChopLumpTransformerInitializer, + CatFileFlipTransformerInitializer, + CatFilePitVowTransformerInitializer, DealTransformerInitializer, DentTransformerInitializer, DripDripTransformerInitializer, diff --git a/test_config/test_config.go b/test_config/test_config.go index c01c1686..12717fe9 100644 --- a/test_config/test_config.go +++ b/test_config/test_config.go @@ -78,6 +78,9 @@ func CleanTestDB(db *postgres.DB) { db.MustExec("DELETE FROM log_filters") db.MustExec("DELETE FROM logs") db.MustExec("DELETE FROM maker.bite") + db.MustExec("DELETE FROM maker.cat_file_chop_lump") + db.MustExec("DELETE FROM maker.cat_file_flip") + db.MustExec("DELETE FROM maker.cat_file_pit_vow") db.MustExec("DELETE FROM maker.drip_file_ilk") db.MustExec("DELETE FROM maker.drip_file_repo") db.MustExec("DELETE FROM maker.drip_file_vow")