diff --git a/db/migrations/1536942529_create_flop_kick.down.sql b/db/migrations/1536942529_create_flop_kick.down.sql new file mode 100644 index 00000000..cf042f3e --- /dev/null +++ b/db/migrations/1536942529_create_flop_kick.down.sql @@ -0,0 +1,3 @@ +DROP TABLE maker.flop_kick; +ALTER TABLE public.checked_headers + DROP COLUMN flop_kick_checked; \ No newline at end of file diff --git a/db/migrations/1536942529_create_flop_kick.up.sql b/db/migrations/1536942529_create_flop_kick.up.sql new file mode 100644 index 00000000..50e0c36e --- /dev/null +++ b/db/migrations/1536942529_create_flop_kick.up.sql @@ -0,0 +1,15 @@ +CREATE TABLE maker.flop_kick ( + id SERIAL PRIMARY KEY, + header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, + bid_id NUMERIC NOT NULL, + lot NUMERIC NOT NULL, + bid NUMERIC NOT NULL, + gal VARCHAR, + "end" TIMESTAMP WITH TIME ZONE, + tx_idx INTEGER NOT NULL, + raw_log JSONB, + UNIQUE (header_id, tx_idx) +); + +ALTER TABLE public.checked_headers + ADD COLUMN flop_kick_checked BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/db/schema.sql b/db/schema.sql index 030145e7..8ceee603 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -340,6 +340,43 @@ CREATE SEQUENCE maker.flip_kick_id_seq ALTER SEQUENCE maker.flip_kick_id_seq OWNED BY maker.flip_kick.id; +-- +-- Name: flop_kick; Type: TABLE; Schema: maker; Owner: - +-- + +CREATE TABLE maker.flop_kick ( + id integer NOT NULL, + header_id integer NOT NULL, + bid_id numeric NOT NULL, + lot numeric NOT NULL, + bid numeric NOT NULL, + gal character varying, + "end" timestamp with time zone, + tx_idx integer NOT NULL, + raw_log jsonb +); + + +-- +-- Name: flop_kick_id_seq; Type: SEQUENCE; Schema: maker; Owner: - +-- + +CREATE SEQUENCE maker.flop_kick_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: flop_kick_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - +-- + +ALTER SEQUENCE maker.flop_kick_id_seq OWNED BY maker.flop_kick.id; + + -- -- Name: frob; Type: TABLE; Schema: maker; Owner: - -- @@ -670,6 +707,7 @@ CREATE TABLE public.checked_headers ( id integer NOT NULL, header_id integer NOT NULL, price_feeds_checked boolean DEFAULT false NOT NULL, + flop_kick_checked boolean DEFAULT false NOT NULL, deal_checked boolean DEFAULT false NOT NULL, dent_checked boolean DEFAULT false NOT NULL, flip_kick_checked boolean DEFAULT false NOT NULL, @@ -1050,6 +1088,13 @@ ALTER TABLE ONLY maker.drip_file_vow ALTER COLUMN id SET DEFAULT nextval('maker. ALTER TABLE ONLY maker.flip_kick ALTER COLUMN id SET DEFAULT nextval('maker.flip_kick_id_seq'::regclass); +-- +-- Name: flop_kick id; Type: DEFAULT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.flop_kick ALTER COLUMN id SET DEFAULT nextval('maker.flop_kick_id_seq'::regclass); + + -- -- Name: frob id; Type: DEFAULT; Schema: maker; Owner: - -- @@ -1305,6 +1350,22 @@ ALTER TABLE ONLY maker.flip_kick ADD CONSTRAINT flip_kick_pkey PRIMARY KEY (id); +-- +-- Name: flop_kick flop_kick_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.flop_kick + ADD CONSTRAINT flop_kick_header_id_tx_idx_key UNIQUE (header_id, tx_idx); + + +-- +-- Name: flop_kick flop_kick_pkey; Type: CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.flop_kick + ADD CONSTRAINT flop_kick_pkey PRIMARY KEY (id); + + -- -- Name: frob frob_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - -- @@ -1643,6 +1704,14 @@ ALTER TABLE ONLY maker.flip_kick ADD CONSTRAINT flip_kick_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; +-- +-- Name: flop_kick flop_kick_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - +-- + +ALTER TABLE ONLY maker.flop_kick + ADD CONSTRAINT flop_kick_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; + + -- -- Name: frob frob_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - -- diff --git a/pkg/geth/blockchain.go b/pkg/geth/blockchain.go index 351485b8..5f50e213 100644 --- a/pkg/geth/blockchain.go +++ b/pkg/geth/blockchain.go @@ -9,9 +9,9 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" "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 20b0904d..56a461c9 100644 --- a/pkg/transformers/bite/converter.go +++ b/pkg/transformers/bite/converter.go @@ -54,14 +54,14 @@ func (BiteConverter) ToEntity(contractAddress string, contractAbi string, ethLog } func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) { - id := entity.Id.String() + id := entity.Id ilk := entity.Ilk[:] lad := entity.Lad[:] - ink := entity.Ink.String() - art := entity.Art.String() - iArt := entity.IArt.String() - tab := entity.Tab.String() - flip := entity.Flip.String() + ink := entity.Ink + art := entity.Art + iArt := entity.IArt + tab := entity.Tab + flip := entity.Flip txIdx := entity.TransactionIndex rawLogJson, err := json.Marshal(entity.Raw) rawLogString := string(rawLogJson) @@ -70,14 +70,14 @@ func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) { } return BiteModel{ - Id: shared.ConvertNilToEmptyString(id), + Id: shared.BigIntToString(id), Ilk: ilk, Lad: lad, - Ink: shared.ConvertNilToEmptyString(ink), - Art: shared.ConvertNilToEmptyString(art), - IArt: shared.ConvertNilToEmptyString(iArt), - Tab: shared.ConvertNilToEmptyString(tab), - Flip: shared.ConvertNilToEmptyString(flip), + Ink: shared.BigIntToString(ink), + Art: shared.BigIntToString(art), + IArt: shared.BigIntToString(iArt), + Tab: shared.BigIntToString(tab), + Flip: shared.BigIntToString(flip), TransactionIndex: txIdx, Raw: rawLogString, }, nil diff --git a/pkg/transformers/flip_kick/converter.go b/pkg/transformers/flip_kick/converter.go index a2aed28b..9995eb83 100644 --- a/pkg/transformers/flip_kick/converter.go +++ b/pkg/transformers/flip_kick/converter.go @@ -64,13 +64,13 @@ func (FlipKickConverter) ToModels(flipKicks []FlipKickEntity) (results []FlipKic } id := flipKick.Id.String() - lot := shared.ConvertNilToEmptyString(flipKick.Lot.String()) - bid := shared.ConvertNilToEmptyString(flipKick.Bid.String()) + lot := shared.BigIntToString(flipKick.Lot) + bid := shared.BigIntToString(flipKick.Bid) gal := flipKick.Gal.String() - endValue := shared.ConvertNilToZeroTimeValue(flipKick.End) + endValue := shared.BigIntToInt64(flipKick.End) end := time.Unix(endValue, 0) urn := common.BytesToAddress(flipKick.Urn[:common.AddressLength]).String() - tab := shared.ConvertNilToEmptyString(flipKick.Tab.String()) + tab := shared.BigIntToString(flipKick.Tab) rawLogJson, err := json.Marshal(flipKick.Raw) if err != nil { return nil, err diff --git a/pkg/transformers/flop_kick/config.go b/pkg/transformers/flop_kick/config.go new file mode 100644 index 00000000..10dd7930 --- /dev/null +++ b/pkg/transformers/flop_kick/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 flop_kick + +import "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + +var Config = shared.TransformerConfig{ + ContractAddress: shared.FlopperContractAddress, + ContractAbi: shared.FlopperABI, + Topics: []string{shared.FlopKickSignature}, + StartingBlockNumber: 0, + EndingBlockNumber: 10000000, +} diff --git a/pkg/transformers/flop_kick/converter.go b/pkg/transformers/flop_kick/converter.go new file mode 100644 index 00000000..ad75d96d --- /dev/null +++ b/pkg/transformers/flop_kick/converter.go @@ -0,0 +1,81 @@ +// 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 flop_kick + +import ( + "encoding/json" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/vulcanize/vulcanizedb/pkg/geth" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +type Converter interface { + ToEntities(contractAddress, contractAbi string, ethLogs []types.Log) ([]Entity, error) + ToModels(entities []Entity) ([]Model, error) +} + +type FlopKickConverter struct{} + +func (FlopKickConverter) ToEntities(contractAddress, contractAbi string, ethLogs []types.Log) ([]Entity, error) { + var results []Entity + for _, ethLog := range ethLogs { + entity := Entity{} + address := common.HexToAddress(contractAddress) + abi, err := geth.ParseAbi(contractAbi) + if err != nil { + return nil, err + } + + contract := bind.NewBoundContract(address, abi, nil, nil, nil) + + err = contract.UnpackLog(&entity, "Kick", ethLog) + if err != nil { + return nil, err + } + entity.Raw = ethLog + entity.TransactionIndex = ethLog.TxIndex + results = append(results, entity) + } + return results, nil +} + +func (FlopKickConverter) ToModels(entities []Entity) ([]Model, error) { + var results []Model + for _, entity := range entities { + endValue := shared.BigIntToInt64(entity.End) + rawLogJson, err := json.Marshal(entity.Raw) + if err != nil { + return nil, err + } + + model := Model{ + BidId: shared.BigIntToString(entity.Id), + Lot: shared.BigIntToString(entity.Lot), + Bid: shared.BigIntToString(entity.Bid), + Gal: entity.Gal.String(), + End: time.Unix(endValue, 0), + TransactionIndex: entity.TransactionIndex, + Raw: rawLogJson, + } + results = append(results, model) + } + + return results, nil +} diff --git a/pkg/transformers/flop_kick/converter_test.go b/pkg/transformers/flop_kick/converter_test.go new file mode 100644 index 00000000..70bc1814 --- /dev/null +++ b/pkg/transformers/flop_kick/converter_test.go @@ -0,0 +1,82 @@ +// 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 flop_kick_test + +import ( + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "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() { + Describe("ToEntities", func() { + It("converts a log to a FlopKick entity", func() { + converter := flop_kick.FlopKickConverter{} + entities, err := converter.ToEntities(shared.FlopperContractAddress, shared.FlopperABI, []types.Log{test_data.FlopKickLog}) + + Expect(err).NotTo(HaveOccurred()) + entity := entities[0] + Expect(entity.Id).To(Equal(test_data.FlopKickEntity.Id)) + Expect(entity.Lot).To(Equal(test_data.FlopKickEntity.Lot)) + Expect(entity.Bid).To(Equal(test_data.FlopKickEntity.Bid)) + Expect(entity.Gal).To(Equal(test_data.FlopKickEntity.Gal)) + Expect(entity.End).To(Equal(test_data.FlopKickEntity.End)) + Expect(entity.TransactionIndex).To(Equal(test_data.FlopKickEntity.TransactionIndex)) + Expect(entity.Raw).To(Equal(test_data.FlopKickEntity.Raw)) + }) + + It("returns an error if converting the log to an entity fails", func() { + converter := flop_kick.FlopKickConverter{} + entities, err := converter.ToEntities(shared.FlopperContractAddress, "error abi", []types.Log{test_data.FlopKickLog}) + + Expect(err).To(HaveOccurred()) + Expect(entities).To(BeNil()) + }) + }) + + Describe("ToModels", func() { + var emptyAddressHex = "0x0000000000000000000000000000000000000000" + var emptyString = "" + var emptyTime = time.Unix(0, 0) + var emptyEntities = []flop_kick.Entity{flop_kick.Entity{}} + + It("converts an Entity to a Model", func() { + converter := flop_kick.FlopKickConverter{} + models, err := converter.ToModels([]flop_kick.Entity{test_data.FlopKickEntity}) + + Expect(err).NotTo(HaveOccurred()) + Expect(models[0]).To(Equal(test_data.FlopKickModel)) + }) + + It("handles nil values", func() { + converter := flop_kick.FlopKickConverter{} + + models, err := converter.ToModels(emptyEntities) + model := models[0] + Expect(err).NotTo(HaveOccurred()) + Expect(model.BidId).To(Equal(emptyString)) + Expect(model.Lot).To(Equal(emptyString)) + Expect(model.Bid).To(Equal(emptyString)) + Expect(model.Gal).To(Equal(emptyAddressHex)) + Expect(model.End).To(Equal(emptyTime)) + }) + }) +}) diff --git a/pkg/transformers/flop_kick/entity.go b/pkg/transformers/flop_kick/entity.go new file mode 100644 index 00000000..1ca4af9d --- /dev/null +++ b/pkg/transformers/flop_kick/entity.go @@ -0,0 +1,32 @@ +// 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 flop_kick + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type Entity struct { + Id *big.Int + Lot *big.Int + Bid *big.Int + Gal common.Address + End *big.Int + TransactionIndex uint + Raw types.Log +} diff --git a/pkg/transformers/flop_kick/flop_kick_suite_test.go b/pkg/transformers/flop_kick/flop_kick_suite_test.go new file mode 100644 index 00000000..7f75d4f7 --- /dev/null +++ b/pkg/transformers/flop_kick/flop_kick_suite_test.go @@ -0,0 +1,13 @@ +package flop_kick_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestFlopKick(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "FlopKick Suite") +} diff --git a/pkg/transformers/flop_kick/integration_test.go b/pkg/transformers/flop_kick/integration_test.go new file mode 100644 index 00000000..f49b58c1 --- /dev/null +++ b/pkg/transformers/flop_kick/integration_test.go @@ -0,0 +1,50 @@ +// 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 flop_kick_test + +import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/geth" + "github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +var _ = Describe("Integration tests", func() { + It("unpacks an flop kick event log", func() { + address := common.HexToAddress(shared.FlopperContractAddress) + abi, err := geth.ParseAbi(shared.FlopperABI) + Expect(err).NotTo(HaveOccurred()) + + contract := bind.NewBoundContract(address, abi, nil, nil, nil) + entity := &flop_kick.Entity{} + + var eventLog = test_data.FlopKickLog + + err = contract.UnpackLog(entity, "Kick", eventLog) + Expect(err).NotTo(HaveOccurred()) + + expectedEntity := test_data.FlopKickEntity + Expect(entity.Id).To(Equal(expectedEntity.Id)) + Expect(entity.Lot).To(Equal(expectedEntity.Lot)) + Expect(entity.Bid).To(Equal(expectedEntity.Bid)) + Expect(entity.Gal).To(Equal(expectedEntity.Gal)) + Expect(entity.End).To(Equal(expectedEntity.End)) + }) +}) diff --git a/pkg/transformers/flop_kick/model.go b/pkg/transformers/flop_kick/model.go new file mode 100644 index 00000000..7902875e --- /dev/null +++ b/pkg/transformers/flop_kick/model.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 flop_kick + +import "time" + +type Model struct { + BidId string `db:"bid_id"` + Lot string + Bid string + Gal string + End time.Time + TransactionIndex uint `db:"tx_idx"` + Raw []byte `db:"raw_log"` +} diff --git a/pkg/transformers/flop_kick/repository.go b/pkg/transformers/flop_kick/repository.go new file mode 100644 index 00000000..c4926e8c --- /dev/null +++ b/pkg/transformers/flop_kick/repository.go @@ -0,0 +1,89 @@ +// 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 flop_kick + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" +) + +type Repository interface { + Create(headerId int64, flopKick []Model) error + MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) + MarkHeaderChecked(headerId int64) error +} + +type FlopKickRepository struct { + DB *postgres.DB +} + +func NewFlopKickRepository(db *postgres.DB) FlopKickRepository { + return FlopKickRepository{DB: db} +} + +func (r FlopKickRepository) Create(headerId int64, flopKicks []Model) error { + tx, err := r.DB.Begin() + if err != nil { + return err + } + for _, flopKick := range flopKicks { + _, err = tx.Exec( + `INSERT into maker.flop_kick (header_id, bid_id, lot, bid, gal, "end", tx_idx, raw_log) + VALUES($1, $2, $3, $4, $5, $6, $7, $8)`, + headerId, flopKick.BidId, flopKick.Lot, flopKick.Bid, flopKick.Gal, flopKick.End, flopKick.TransactionIndex, flopKick.Raw, + ) + if err != nil { + tx.Rollback() + return err + } + } + + _, err = tx.Exec(`INSERT INTO public.checked_headers (header_id, flop_kick_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET flop_kick_checked = $2`, headerId, true) + if err != nil { + tx.Rollback() + return err + } + + return tx.Commit() +} + +func (r FlopKickRepository) MarkHeaderChecked(headerId int64) error { + _, err := r.DB.Exec(`INSERT INTO public.checked_headers (header_id, flop_kick_checked) + VALUES ($1, $2) + ON CONFLICT (header_id) DO + UPDATE SET flop_kick_checked = $2`, headerId, true) + return err +} + +func (r FlopKickRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + var result []core.Header + err := r.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 flop_kick_checked IS FALSE) + AND headers.block_number >= $1 + AND headers.block_number <= $2 + AND headers.eth_node_fingerprint = $3`, + startingBlockNumber, + endingBlockNumber, + r.DB.Node.ID, + ) + + return result, err +} diff --git a/pkg/transformers/flop_kick/repository_test.go b/pkg/transformers/flop_kick/repository_test.go new file mode 100644 index 00000000..15ba0d91 --- /dev/null +++ b/pkg/transformers/flop_kick/repository_test.go @@ -0,0 +1,200 @@ +// 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 flop_kick_test + +import ( + "math/rand" + + . "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/flop_kick" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("FlopRepository", func() { + var db *postgres.DB + var repository flop_kick.FlopKickRepository + var headerRepository repositories.HeaderRepository + var headerId int64 + var err error + var dbResult test_data.FlopKickDBResult + + BeforeEach(func() { + node := test_config.NewTestNode() + db = test_config.NewTestDB(node) + test_config.CleanTestDB(db) + repository = flop_kick.NewFlopKickRepository(db) + headerRepository = repositories.NewHeaderRepository(db) + headerId, err = headerRepository.CreateOrUpdateHeader(core.Header{}) + Expect(err).NotTo(HaveOccurred()) + dbResult = test_data.FlopKickDBResult{} + }) + + Describe("Create", func() { + It("creates FlopKick records", func() { + err := repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel}) + Expect(err).NotTo(HaveOccurred()) + + err = db.QueryRowx(`SELECT * FROM maker.flop_kick WHERE header_id = $1`, headerId).StructScan(&dbResult) + Expect(err).NotTo(HaveOccurred()) + Expect(dbResult.HeaderId).To(Equal(headerId)) + Expect(dbResult.BidId).To(Equal(test_data.FlopKickModel.BidId)) + Expect(dbResult.Lot).To(Equal(test_data.FlopKickModel.Lot)) + Expect(dbResult.Bid).To(Equal(test_data.FlopKickModel.Bid)) + Expect(dbResult.Gal).To(Equal(test_data.FlopKickModel.Gal)) + Expect(dbResult.End.Equal(test_data.FlopKickModel.End)).To(BeTrue()) + Expect(dbResult.TransactionIndex).To(Equal(test_data.FlopKickModel.TransactionIndex)) + Expect(dbResult.Raw).To(MatchJSON(test_data.FlopKickModel.Raw)) + }) + + It("marks headerId as checked for flop kick logs", func() { + err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel}) + Expect(err).NotTo(HaveOccurred()) + + var headerChecked bool + err = db.Get(&headerChecked, `SELECT flop_kick_checked FROM public.checked_headers WHERE header_id = $1`, headerId) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + + It("returns an error if inserting the flop_kick record fails", func() { + err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel}) + Expect(err).NotTo(HaveOccurred()) + + err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint")) + }) + + It("deletes the flop_kick records if its corresponding header record is deleted", func() { + err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel}) + Expect(err).NotTo(HaveOccurred()) + + var flopKickCount int + err = db.QueryRow(`SELECT count(*) FROM maker.flop_kick`).Scan(&flopKickCount) + Expect(err).NotTo(HaveOccurred()) + Expect(flopKickCount).To(Equal(1)) + + _, err = db.Exec(`DELETE FROM headers where id = $1`, headerId) + Expect(err).NotTo(HaveOccurred()) + + err = db.QueryRow(`SELECT count(*) FROM maker.flop_kick`).Scan(&flopKickCount) + Expect(err).NotTo(HaveOccurred()) + Expect(flopKickCount).To(Equal(0)) + }) + }) + + Describe("MarkedHeadersChecked", func() { + It("creates a row for a new headerId", func() { + err := repository.MarkHeaderChecked(headerId) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT flop_kick_checked FROM public.checked_headers WHERE header_id = $1`, headerId) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + + It("updates row when headerId already exists", func() { + _, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerId) + err = repository.MarkHeaderChecked(headerId) + + Expect(err).NotTo(HaveOccurred()) + var headerChecked bool + err = db.Get(&headerChecked, `SELECT flop_kick_checked FROM public.checked_headers WHERE header_id = $1`, headerId) + Expect(err).NotTo(HaveOccurred()) + Expect(headerChecked).To(BeTrue()) + }) + }) + + Describe("MissingHeaders", func() { + var flopKickBlockNumber = rand.Int63() + var startingBlockNumber = flopKickBlockNumber - 1 + var endingBlockNumber = flopKickBlockNumber + 1 + var outOfRangeBlockNumber = flopKickBlockNumber + 2 + + It("returns headers haven't been checked", func() { + var headerIds []int64 + + for _, number := range []int64{startingBlockNumber, flopKickBlockNumber, endingBlockNumber, outOfRangeBlockNumber} { + headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number}) + Expect(err).NotTo(HaveOccurred()) + headerIds = append(headerIds, headerId) + } + + err = repository.MarkHeaderChecked(headerIds[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := repository.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 flop_kicks have been checked", func() { + var headerIds []int64 + for _, number := range []int64{startingBlockNumber, flopKickBlockNumber, endingBlockNumber, outOfRangeBlockNumber} { + headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number}) + Expect(err).NotTo(HaveOccurred()) + headerIds = append(headerIds, headerId) + } + + _, err := db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIds[1]) + Expect(err).NotTo(HaveOccurred()) + + headers, err := repository.MissingHeaders(startingBlockNumber, endingBlockNumber) + + Expect(err).NotTo(HaveOccurred()) + Expect(len(headers)).To(Equal(3)) + Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(flopKickBlockNumber))) + Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(flopKickBlockNumber))) + Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(flopKickBlockNumber))) + }) + + It("only returns missing headers for the current node", func() { + var headerIds []int64 + node2 := core.Node{} + db2 := test_config.NewTestDB(node2) + headerRepository2 := repositories.NewHeaderRepository(db2) + flopKickRepository2 := flop_kick.NewFlopKickRepository(db2) + + for _, number := range []int64{startingBlockNumber, flopKickBlockNumber, endingBlockNumber} { + headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number}) + Expect(err).NotTo(HaveOccurred()) + headerIds = append(headerIds, headerId) + + headerRepository2.CreateOrUpdateHeader(core.Header{BlockNumber: number}) + Expect(err).NotTo(HaveOccurred()) + } + + err = repository.MarkHeaderChecked(headerIds[1]) + Expect(err).NotTo(HaveOccurred()) + + node1MissingHeaders, err := repository.MissingHeaders(startingBlockNumber, endingBlockNumber) + Expect(err).NotTo(HaveOccurred()) + Expect(len(node1MissingHeaders)).To(Equal(2)) + + node2MissingHeaders, err := flopKickRepository2.MissingHeaders(startingBlockNumber, endingBlockNumber) + Expect(err).NotTo(HaveOccurred()) + Expect(len(node2MissingHeaders)).To(Equal(3)) + }) + }) +}) diff --git a/pkg/transformers/flop_kick/transformer.go b/pkg/transformers/flop_kick/transformer.go new file mode 100644 index 00000000..67ff93be --- /dev/null +++ b/pkg/transformers/flop_kick/transformer.go @@ -0,0 +1,81 @@ +// 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 flop_kick + +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/shared" +) + +type Transformer struct { + Config shared.TransformerConfig + Converter Converter + Fetcher shared.LogFetcher + Repository Repository +} + +type FlopKickTransformerInitializer struct { + Config shared.TransformerConfig +} + +func (i FlopKickTransformerInitializer) NewFlopKickTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { + return Transformer{ + Config: i.Config, + Converter: FlopKickConverter{}, + Fetcher: shared.NewFetcher(blockChain), + Repository: NewFlopKickRepository(db), + } +} + +func (t Transformer) Execute() error { + config := t.Config + headers, err := t.Repository.MissingHeaders(config.StartingBlockNumber, config.EndingBlockNumber) + if err != nil { + return err + } + + for _, header := range headers { + topics := [][]common.Hash{{common.HexToHash(shared.FlopKickSignature)}} + matchingLogs, err := t.Fetcher.FetchLogs(config.ContractAddress, topics, header.BlockNumber) + if err != nil { + return err + } + if len(matchingLogs) < 1 { + err := t.Repository.MarkHeaderChecked(header.Id) + if err != nil { + return err + } + } + + entities, err := t.Converter.ToEntities(config.ContractAddress, config.ContractAbi, matchingLogs) + if err != nil { + return err + } + + models, err := t.Converter.ToModels(entities) + if err != nil { + return err + } + + err = t.Repository.Create(header.Id, models) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/transformers/flop_kick/transformer_test.go b/pkg/transformers/flop_kick/transformer_test.go new file mode 100644 index 00000000..304cba1a --- /dev/null +++ b/pkg/transformers/flop_kick/transformer_test.go @@ -0,0 +1,246 @@ +// 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 flop_kick_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/flop_kick" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" + flop_kick_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/flop_kick" +) + +var _ = Describe("FlopKick Transformer", func() { + var fetcher mocks.MockLogFetcher + var converter flop_kick_mocks.MockConverter + var repository flop_kick_mocks.MockRepository + var config = flop_kick.Config + var headerOne core.Header + var headerTwo core.Header + + BeforeEach(func() { + fetcher = mocks.MockLogFetcher{} + converter = flop_kick_mocks.MockConverter{} + repository = flop_kick_mocks.MockRepository{} + headerOne = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()} + headerTwo = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()} + }) + + It("gets missing headers for specified block numbers", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + + err := transformer.Execute() + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedStartingBlockNumber).To(Equal(flop_kick.Config.StartingBlockNumber)) + Expect(repository.PassedEndingBlockNumber).To(Equal(flop_kick.Config.EndingBlockNumber)) + }) + + It("returns error if getting missing headers fails", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeadersError(fakes.FakeError) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("fetches logs for each missing header", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{headerOne, headerTwo}) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(fetcher.FetchedBlocks).To(Equal([]int64{headerOne.BlockNumber, headerTwo.BlockNumber})) + Expect(fetcher.FetchedContractAddress).To(Equal(flop_kick.Config.ContractAddress)) + Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.FlopKickSignature)}})) + }) + + It("returns error if fetcher returns error", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + fetcher.SetFetcherError(fakes.FakeError) + repository.SetMissingHeaders([]core.Header{{}}) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("marks header as checked even if no logs were returned", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{headerOne, headerTwo}) + fetcher.SetFetchedLogs([]types.Log{}) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.CheckedHeaderIds).To(ContainElement(headerOne.Id)) + Expect(repository.CheckedHeaderIds).To(ContainElement(headerTwo.Id)) + }) + + It("returns error if marking header checked returns err", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{headerOne, headerTwo}) + repository.SetCheckedHeaderError(fakes.FakeError) + fetcher.SetFetchedLogs([]types.Log{}) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("converts matching logs to entity", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{{BlockNumber: GinkgoRandomSeed()}}) + fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog}) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(converter.PassedContractAddress).To(Equal(flop_kick.Config.ContractAddress)) + Expect(converter.PassedContractABI).To(Equal(flop_kick.Config.ContractAbi)) + Expect(converter.PassedLogs).To(Equal([]types.Log{test_data.FlopKickLog})) + }) + + It("returns an error if converting logs to entity fails", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{{BlockNumber: GinkgoRandomSeed()}}) + fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog}) + converter.SetToEntityConverterError(fakes.FakeError) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("converts flop_kick entity to model", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{{}}) + fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog}) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(converter.PassedEntities).To(Equal([]flop_kick.Entity{test_data.FlopKickEntity})) + }) + + It("returns an error if there's a failure in converting to model", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{{}}) + fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog}) + converter.SetToModelConverterError(fakes.FakeError) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("persists the flop_kick model", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{headerOne, headerTwo}) + fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog}) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.CreatedHeaderIds).To(ContainElement(headerOne.Id)) + Expect(repository.CreatedHeaderIds).To(ContainElement(headerTwo.Id)) + Expect(repository.CreatedModels).To(ContainElement(test_data.FlopKickModel)) + }) + + It("returns error if repository returns error for create", func() { + transformer := flop_kick.Transformer{ + Config: config, + Converter: &converter, + Fetcher: &fetcher, + Repository: &repository, + } + repository.SetMissingHeaders([]core.Header{{}}) + repository.SetCreateError(fakes.FakeError) + fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog}) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) +}) diff --git a/pkg/transformers/old-DOCUMENTATION.md b/pkg/transformers/old-DOCUMENTATION.md deleted file mode 100644 index fc6411ae..00000000 --- a/pkg/transformers/old-DOCUMENTATION.md +++ /dev/null @@ -1,79 +0,0 @@ -The main goal of creating a transformer is to fetch specific log events from Ethereum, convert/decode them into usable data and then persist them to VulcanizeDB. For Maker there are two main types of log events that we're tracking: custom events that are defined in the contract solidity code, and LogNote events which utilize the [DSNote library](https://github.com/dapphub/ds-note). The transformer process for each of these different log types is the same, except for the converting process, as denoted below. - -## Creating a Transformer for custom events (i.e. FlopKick) -To illustrate how to create a custom log event transformer we'll use the Kick event defined in [flop.sol](https://github.com/makerdao/dss/blob/master/src/flop.sol) as an example. - -1. Get an example FlopKick log event either from mainnet (if the - contract has already been deployed), from the Kovan testnet, or by -deploying the contract to a local chain and emitting the event manually. -We will use the example log event to test drive converting the log to a -FlopKick database model. - -1. Fetch the appropriate logs from the chain. - - Most transformers use `shared.LogFetcher` - - Price Feeds - -1. Convert the raw log into a database model. - - For Custom Events, such as FlopKick - 1. Create a converter to convert the raw log into a Go structure. - - We've been using [go-ethereum's abigen tool](https://github.com/ethereum/go-ethereum/tree/master/cmd/abigen) to get the contract's ABI, and a Go struct that represents the event log. We will unpack the raw logs into this struct. - - To use abigen: `abigen --sol flip.sol --pkg flip --out {/path/to/output_file}` - - sol: this is the path to the solidity contract - - pkg: a package name for the generated Go code - - out: the file path for the generated Go code (optional) - - the output for `flop.sol` will include the FlopperAbi and the FlopperKick struct: - ```go - type FlopperKick struct { - Id *big.Int - Lot *big.Int - Bid *big.Int - Gal common.Address - End *big.Int - Raw types.Log - } - ``` - - Using go-ethereum's `contract.UnpackLog` method we can unpack the raw log into the FlopperKick struct (which we're referring to as the `entity`). - - See the `ToEntity` method in `pkg/transformers/flop_kick/converter`. - - The unpack method will not add the `Raw` or `TransactionIndex` values to the entity struct - both of these values are accessible from the entity. - 1. Then convert the entity into a database model. See the `ToModel` method in `pkg/transformers/flop_kick/converter`. - - - For LogNote Events, such as tend. - - Since LogNote events are a generic structure, they depend on the - method signature of the method that is calling them. For example, -when the `tend` method is called on the -[flip.sol contract](https://github.com/makerdao/dss/blob/master/src/flip.sol#L117), the method signature looks like this: `tend(uint id, uint lot, uint bid)` - - the LogNote event will take the first - - - - -1. Persist the log record to VulcanizeDB. - - Each event log has it's own table in the database, as well as it's - own column in the `checked_headers` table. - - The `checked_headers` table alllows us to keep track of which - headers have been queried for a given log type. - - To create a new migration file: `migrate create -ext sql -dir ./db/migrations/ create_flop_kick` - - See `db/migrations/1536942529_create_flop_kick.up.sql`. - - The specific log event tables are all created in the `maker` - schema. - - There is a one-many association between `headers` and the log - event tables. This is so that if a header is removed due to a -reorg, the associated log event records are also removed. - - We have been following a repository pattern to interact with the - database for each table, see the `Create` method in `pkg/transformers/flop_kick/repository.go`. -1. Get all MissingHeaders. //TODO// - - The repository is also responsible for querying for all header - records that have not yet been. -1. MarkHeaderChecked//TODO// - -1. Wire each component up in the transformer. - - - Each transformer's `Execute` method iterates through all of the - MissingHeaders - - Currently each transformer's `Execute` method is responsible for - fetching the missing headers, meaning the headers that haven't yet -been checked for a given log type - - - -#### For LogNote events diff --git a/pkg/transformers/shared/constants.go b/pkg/transformers/shared/constants.go index a8ee6d4f..f8bedd72 100644 --- a/pkg/transformers/shared/constants.go +++ b/pkg/transformers/shared/constants.go @@ -20,6 +20,7 @@ var ( 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\"}]" 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\"}]" MedianizerABI = `[{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"poke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"poke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"compute","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wat","type":"address"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wat","type":"address"}],"name":"unset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"indexes","outputs":[{"name":"","type":"bytes12"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"next","outputs":[{"name":"","type":"bytes12"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"read","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"peek","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes12"}],"name":"values","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"min_","type":"uint96"}],"name":"setMin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"void","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"pos","type":"bytes12"},{"name":"wat","type":"address"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"pos","type":"bytes12"}],"name":"unset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"next_","type":"bytes12"}],"name":"setNext","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"min","outputs":[{"name":"","type":"uint96"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"val","type":"bytes32"}],"name":"LogValue","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"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]]` PitABI = `[{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x36569e77"},{"constant":true,"inputs":[],"name":"live","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x957aa58c"},{"constant":true,"inputs":[],"name":"drip","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x9f678cca"},{"constant":true,"inputs":[],"name":"Line","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbabe8a3f"},{"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":"spot","type":"uint256"},{"name":"line","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"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":"dink","type":"int256"},{"indexed":false,"name":"dart","type":"int256"},{"indexed":false,"name":"iArt","type":"uint256"}],"name":"Frob","type":"event","signature":"0xb2afa28318bcc689926b52835d844de174ef8de97e982a85c0199d584920791b"},{"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":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x29ae8114"},{"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":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"frob","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5a984ded"}]` VatABI = `[{"constant":true,"inputs":[],"name":"debt","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x0dca59c1"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes32"}],"name":"urns","outputs":[{"name":"ink","type":"uint256"},{"name":"art","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26e27482"},{"constant":true,"inputs":[],"name":"vice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x2d61a355"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"sin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xa60f1d3e"},{"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":"","type":"bytes32"}],"name":"gem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc0912683"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"take","type":"uint256"},{"name":"rate","type":"uint256"},{"name":"Ink","type":"uint256"},{"name":"Art","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"dai","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf53e4e69"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":true,"name":"too","type":"bytes32"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"Note","type":"event","signature":"0x8c2dbbc2b33ffaa77c104b777e574a8a4ff79829dfee8b66f4dc63e3f8067152"},{"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":"init","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3b663195"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"guy","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"slip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x42066cbb"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"flux","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6e41821"},{"constant":false,"inputs":[{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"move","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x78f19470"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"w","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"tune","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5dd6471a"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"w","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"grab","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3690ae4c"},{"constant":false,"inputs":[{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"heal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x990a5f63"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"rate","type":"int256"}],"name":"fold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe6a6a64d"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"take","type":"int256"}],"name":"toll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x09b7a0b5"}]` @@ -28,6 +29,7 @@ var ( CatContractAddress = "0x2f34f22a00ee4b7a8f8bbc4eaee1658774c624e0" DripContractAddress = "0x891c04639a5edcae088e546fa125b5d7fb6a2b9d" FlipperContractAddress = "0x32d496ad866d110060866b7125981c73642cc509" // ETH FLIP Contract + FlopperContractAddress = "0x6191c9b0086c2ebf92300cc507009b53996fbffa" // MCD FLOP Contract PepContractAddress = "0xB1997239Cfc3d15578A3a09730f7f84A90BB4975" PipContractAddress = "0x9FfFE440258B79c5d6604001674A4722FfC0f7Bc" PitContractAddress = "0xe7cf3198787c9a4daac73371a38f29aaeeced87e" @@ -43,6 +45,7 @@ var ( dripFileRepoMethod = GetSolidityMethodSignature(DripABI, "file") dripFileVowMethod = "file(bytes32,bytes32)" flipKickMethod = GetSolidityMethodSignature(FlipperABI, "Kick") + flopKickMethod = GetSolidityMethodSignature(FlopperABI, "Kick") frobMethod = GetSolidityMethodSignature(PitABI, "Frob") logValueMethod = GetSolidityMethodSignature(MedianizerABI, "LogValue") pitFileDebtCeilingMethod = "file(bytes32,uint256)" @@ -59,6 +62,7 @@ var ( DripFileRepoSignature = GetLogNoteSignature(dripFileRepoMethod) DripFileVowSignature = GetLogNoteSignature(dripFileVowMethod) FlipKickSignature = GetEventSignature(flipKickMethod) + FlopKickSignature = GetEventSignature(flopKickMethod) FrobSignature = GetEventSignature(frobMethod) LogValueSignature = GetEventSignature(logValueMethod) PitFileDebtCeilingSignature = GetLogNoteSignature(pitFileDebtCeilingMethod) diff --git a/pkg/transformers/shared/event_signature_generator_test.go b/pkg/transformers/shared/event_signature_generator_test.go index 85ce065d..f45a3c01 100644 --- a/pkg/transformers/shared/event_signature_generator_test.go +++ b/pkg/transformers/shared/event_signature_generator_test.go @@ -135,6 +135,13 @@ var _ = Describe("Event signature generator", func() { Expect(expected).To(Equal(actual)) }) + It("gets the flop Kick event signature", func() { + expected := "Kick(uint256,uint256,uint256,address,uint48)" + actual := shared.GetSolidityMethodSignature(shared.FlopperABI, "Kick") + + Expect(expected).To(Equal(actual)) + }) + It("gets the pit frob event signature", func() { expected := "Frob(bytes32,bytes32,uint256,uint256,int256,int256,uint256)" actual := shared.GetSolidityMethodSignature(shared.PitABI, "Frob") diff --git a/pkg/transformers/shared/utilities.go b/pkg/transformers/shared/utilities.go index 7130a0b7..83afd982 100644 --- a/pkg/transformers/shared/utilities.go +++ b/pkg/transformers/shared/utilities.go @@ -16,7 +16,7 @@ package shared import "math/big" -func ConvertNilToZeroTimeValue(value *big.Int) int64 { +func BigIntToInt64(value *big.Int) int64 { if value == nil { return int64(0) } else { @@ -24,10 +24,11 @@ func ConvertNilToZeroTimeValue(value *big.Int) int64 { } } -func ConvertNilToEmptyString(value string) string { - if value == "" { +func BigIntToString(value *big.Int) string { + result := value.String() + if result == "" { return "" } else { - return value + return result } } diff --git a/pkg/transformers/test_data/flop_kick.go b/pkg/transformers/test_data/flop_kick.go new file mode 100644 index 00000000..747afde0 --- /dev/null +++ b/pkg/transformers/test_data/flop_kick.go @@ -0,0 +1,78 @@ +// 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/flop_kick" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "math/big" + "strconv" + "time" +) + +var ( + FlopKickLog = types.Log{ + Address: common.HexToAddress(shared.FlopperContractAddress), + Topics: []common.Hash{ + common.HexToHash("0xefa52d9342a199cb30efd2692463f2c2bef63cd7186b50382d4fb94ad207880e"), + common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000005"), + }, + Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000020000000000000000000000007d7bee5fcfd8028cf7b00876c5b1421c800561a6000000000000000000000000000000000000000000000000000000000002a300"), + BlockNumber: 19, + TxHash: common.HexToHash("0xd8fd67b37a6aa64a3cef4937204765183b180d8dc92eecd0d233f445526d31b5"), + TxIndex: flopTxIndex, + BlockHash: common.HexToHash("0x7891fe725691abc5b94113dc2bfa8c3509e1e1d0e0bb74289e3b996c30d00c8c"), + Index: 32, + Removed: false, + } + + flopTxIndex = uint(33) + flopBidId = int64(5) + flopLot = int64(15) + flopBid = int64(2) + flopGal = "0x7d7bEe5fCfD8028cf7b00876C5b1421c800561A6" + rawFlopLogJson, _ = json.Marshal(FlopKickLog) + flopEnd = int64(172800) + + FlopKickEntity = flop_kick.Entity{ + Id: big.NewInt(flopBidId), + Lot: big.NewInt(flopLot), + Bid: big.NewInt(flopBid), + Gal: common.HexToAddress(flopGal), + End: big.NewInt(flopEnd), + TransactionIndex: flopTxIndex, + Raw: FlopKickLog, + } + + FlopKickModel = flop_kick.Model{ + BidId: strconv.FormatInt(flopBidId, 10), + Lot: strconv.FormatInt(flopLot, 10), + Bid: strconv.FormatInt(flopBid, 10), + Gal: flopGal, + End: time.Unix(flopEnd, 0), + TransactionIndex: flopTxIndex, + Raw: rawFlopLogJson, + } +) + +type FlopKickDBResult struct { + Id int64 + HeaderId int64 `db:"header_id"` + flop_kick.Model +} diff --git a/pkg/transformers/test_data/mocks/flop_kick/converter.go b/pkg/transformers/test_data/mocks/flop_kick/converter.go new file mode 100644 index 00000000..2abfaf5e --- /dev/null +++ b/pkg/transformers/test_data/mocks/flop_kick/converter.go @@ -0,0 +1,51 @@ +// 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 flop_kick + +import ( + "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" +) + +type MockConverter struct { + PassedContractAddress string + PassedContractABI string + PassedLogs []types.Log + PassedEntities []flop_kick.Entity + entityConverterError error + modelConverterError error +} + +func (c *MockConverter) ToEntities(contractAddress, contractAbi string, ethLogs []types.Log) ([]flop_kick.Entity, error) { + c.PassedContractAddress = contractAddress + c.PassedContractABI = contractAbi + c.PassedLogs = ethLogs + + return []flop_kick.Entity{test_data.FlopKickEntity}, c.entityConverterError +} + +func (c *MockConverter) ToModels(entities []flop_kick.Entity) ([]flop_kick.Model, error) { + c.PassedEntities = entities + return []flop_kick.Model{test_data.FlopKickModel}, c.modelConverterError +} + +func (c *MockConverter) SetToEntityConverterError(err error) { + c.entityConverterError = err +} + +func (c *MockConverter) SetToModelConverterError(err error) { + c.modelConverterError = err +} diff --git a/pkg/transformers/test_data/mocks/flop_kick/repository.go b/pkg/transformers/test_data/mocks/flop_kick/repository.go new file mode 100644 index 00000000..a46e9876 --- /dev/null +++ b/pkg/transformers/test_data/mocks/flop_kick/repository.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 flop_kick + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick" +) + +type MockRepository struct { + PassedStartingBlockNumber int64 + PassedEndingBlockNumber int64 + CreatedHeaderIds []int64 + CreatedModels []flop_kick.Model + CheckedHeaderIds []int64 + missingHeaders []core.Header + missingHeadersError error + createError error + checkedHeaderError error +} + +func (r *MockRepository) Create(headerId int64, flopKicks []flop_kick.Model) error { + r.CreatedHeaderIds = append(r.CreatedHeaderIds, headerId) + r.CreatedModels = flopKicks + + return r.createError +} + +func (r *MockRepository) MarkHeaderChecked(headerId int64) error { + r.CheckedHeaderIds = append(r.CheckedHeaderIds, headerId) + + return r.checkedHeaderError +} + +func (r *MockRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + r.PassedStartingBlockNumber = startingBlockNumber + r.PassedEndingBlockNumber = endingBlockNumber + + return r.missingHeaders, r.missingHeadersError +} + +func (r *MockRepository) SetMissingHeaders(headers []core.Header) { + r.missingHeaders = headers +} + +func (r *MockRepository) SetMissingHeadersError(err error) { + r.missingHeadersError = err +} + +func (r *MockRepository) SetCreateError(err error) { + r.createError = err +} + +func (r *MockRepository) SetCheckedHeaderError(err error) { + r.checkedHeaderError = err +} diff --git a/pkg/transformers/transformers.go b/pkg/transformers/transformers.go index 90b58f00..f371b8fd 100644 --- a/pkg/transformers/transformers.go +++ b/pkg/transformers/transformers.go @@ -24,6 +24,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/transformers/drip_file/repo" "github.com/vulcanize/vulcanizedb/pkg/transformers/drip_file/vow" "github.com/vulcanize/vulcanizedb/pkg/transformers/flip_kick" + "github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick" "github.com/vulcanize/vulcanizedb/pkg/transformers/frob" "github.com/vulcanize/vulcanizedb/pkg/transformers/pit_file" "github.com/vulcanize/vulcanizedb/pkg/transformers/pit_file/debt_ceiling" @@ -45,6 +46,7 @@ var ( DripFileRepoTransformerInitializer = repo.DripFileRepoTransformerInitializer{Config: dripFileConfig}.NewDripFileRepoTransformer DripFileVowTransfromerInitializer = vow.DripFileVowTransformerInitializer{Config: dripFileConfig}.NewDripFileVowTransformer FlipKickTransformerInitializer = flip_kick.FlipKickTransformerInitializer{Config: flip_kick.FlipKickConfig}.NewFlipKickTransformer + FlopKickTransformerInitializer = flop_kick.FlopKickTransformerInitializer{Config: flop_kick.Config}.NewFlopKickTransformer FrobTransformerInitializer = frob.FrobTransformerInitializer{Config: frob.FrobConfig}.NewFrobTransformer pitFileConfig = pit_file.PitFileConfig PitFileDebtCeilingTransformerInitializer = debt_ceiling.PitFileDebtCeilingTransformerInitializer{Config: pitFileConfig}.NewPitFileDebtCeilingTransformer @@ -62,8 +64,10 @@ func TransformerInitializers() []shared.TransformerInitializer { DentTransformerInitializer, DripDripTransformerInitializer, DripFileIlkTransformerInitializer, + DripFileVowTransfromerInitializer, DripFileRepoTransformerInitializer, FlipKickTransformerInitializer, + FlopKickTransformerInitializer, FrobTransformerInitializer, PitFileDebtCeilingTransformerInitializer, PitFileIlkTransformerInitializer,