From ef0e3f9e117ee2e95bb2895049c0dd96d158404b Mon Sep 17 00:00:00 2001 From: David Terry Date: Wed, 3 Oct 2018 18:22:05 +0200 Subject: [PATCH] Vat.fold: add repository & tests --- pkg/transformers/test_data/vat_fold.go | 13 +- pkg/transformers/vat_fold/converter.go | 7 +- pkg/transformers/vat_fold/repository.go | 60 ++++++++ pkg/transformers/vat_fold/repository_test.go | 145 +++++++++++++++++++ 4 files changed, 216 insertions(+), 9 deletions(-) create mode 100644 pkg/transformers/vat_fold/repository.go create mode 100644 pkg/transformers/vat_fold/repository_test.go diff --git a/pkg/transformers/test_data/vat_fold.go b/pkg/transformers/test_data/vat_fold.go index 8a5e9b25..e033da17 100644 --- a/pkg/transformers/test_data/vat_fold.go +++ b/pkg/transformers/test_data/vat_fold.go @@ -15,7 +15,6 @@ package test_data import ( - "bytes" "encoding/json" "github.com/ethereum/go-ethereum/common" @@ -35,19 +34,19 @@ var EthVatFoldLog = types.Log{ common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"), }, Data: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000064e6a6a64d45544800000000000000000000000000000000000000000000000000000000000000000000000000000000003728e9777b2a0a611ee0f89e00e01044ce4736d10000000000000000000000000000000000000000000000000000000000000000"), - BlockNumber: 72, - TxHash: common.HexToHash("0xe8f39fbb7fea3621f543868f19b1114e305aff6a063a30d32835ff1012526f91"), + BlockNumber: 8940380, + TxHash: common.HexToHash("0xfb37b7a88aa8ad14538d1e244a55939fa07c1828e5ca8168bf4edd56f5fc4d57"), TxIndex: 8, - BlockHash: common.HexToHash("0xe3dd2e05bd8b92833e20ed83e2171bbc06a9ec823232eca1730a807bd8f5edc0"), + BlockHash: common.HexToHash("0xf43ab2fd3cf0a7e08fcc16ec17bbc7f67417a37a4cd978d1d7ca32130c7f64be"), Index: 5, Removed: false, } var rawVatFoldLog, _ = json.Marshal(EthVatFoldLog) var VatFoldModel = vat_fold.VatFoldModel{ - Ilk: string(bytes.Trim(EthVatFoldLog.Topics[1].Bytes(), "\x00")), - Urn: string(bytes.Trim(EthVatFoldLog.Topics[2].Bytes(), "\x00")), - Rate: string(bytes.Trim(EthVatFoldLog.Topics[3].Bytes(), "\x00")), + Ilk: "REP", + Urn: "0x3728e9777B2a0a611ee0F89e00E01044ce4736d1", + Rate: "2", TransactionIndex: EthVatFoldLog.TxIndex, Raw: rawVatFoldLog, } diff --git a/pkg/transformers/vat_fold/converter.go b/pkg/transformers/vat_fold/converter.go index 6270c647..c8bf0e1d 100644 --- a/pkg/transformers/vat_fold/converter.go +++ b/pkg/transformers/vat_fold/converter.go @@ -18,8 +18,11 @@ import ( "bytes" "encoding/json" "errors" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" ) type Converter interface { @@ -35,8 +38,8 @@ func (VatFoldConverter) ToModel(ethLog types.Log) (VatFoldModel, error) { } ilk := string(bytes.Trim(ethLog.Topics[1].Bytes(), "\x00")) - urn := string(bytes.Trim(ethLog.Topics[2].Bytes(), "\x00")) - rate := string(bytes.Trim(ethLog.Topics[3].Bytes(), "\x00")) + urn := common.HexToAddress(ethLog.Topics[2].String()).String() + rate := big.NewInt(0).SetBytes(ethLog.Topics[3].Bytes()).String() raw, err := json.Marshal(ethLog) return VatFoldModel{ diff --git a/pkg/transformers/vat_fold/repository.go b/pkg/transformers/vat_fold/repository.go new file mode 100644 index 00000000..19fac0a1 --- /dev/null +++ b/pkg/transformers/vat_fold/repository.go @@ -0,0 +1,60 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_fold + +import ( + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" +) + +type Repository interface { + Create(headerID int64, model VatFoldModel) error + MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) +} + +type VatFoldRepository struct { + db *postgres.DB +} + +func NewVatFoldRepository(db *postgres.DB) VatFoldRepository { + return VatFoldRepository{ + db: db, + } +} + +func (repository VatFoldRepository) Create(headerID int64, model VatFoldModel) error { + _, err := repository.db.Exec(`INSERT INTO maker.vat_fold (header_id, ilk, urn, rate, raw_log, tx_idx) + VALUES($1, $2, $3, $4::NUMERIC, $5, $6)`, + headerID, model.Ilk, model.Urn, model.Rate, model.Raw, model.TransactionIndex) + return err +} + +func (repository VatFoldRepository) 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 maker.vat_fold on headers.id = header_id + WHERE header_id ISNULL + 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/vat_fold/repository_test.go b/pkg/transformers/vat_fold/repository_test.go new file mode 100644 index 00000000..d703bc1e --- /dev/null +++ b/pkg/transformers/vat_fold/repository_test.go @@ -0,0 +1,145 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_fold_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/repositories" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_fold" + "github.com/vulcanize/vulcanizedb/test_config" +) + +var _ = Describe("", func() { + Describe("Create", func() { + It("adds a vat event", 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()) + vatFoldRepository := vat_fold.NewVatFoldRepository(db) + + err = vatFoldRepository.Create(headerID, test_data.VatFoldModel) + + Expect(err).NotTo(HaveOccurred()) + var dbVatFold vat_fold.VatFoldModel + err = db.Get(&dbVatFold, `SELECT ilk, urn, rate, tx_idx, raw_log FROM maker.vat_fold WHERE header_id = $1`, headerID) + Expect(err).NotTo(HaveOccurred()) + Expect(dbVatFold.Ilk).To(Equal(test_data.VatFoldModel.Ilk)) + Expect(dbVatFold.Urn).To(Equal(test_data.VatFoldModel.Urn)) + Expect(dbVatFold.Rate).To(Equal(test_data.VatFoldModel.Rate)) + Expect(dbVatFold.TransactionIndex).To(Equal(test_data.VatFoldModel.TransactionIndex)) + Expect(dbVatFold.Raw).To(MatchJSON(test_data.VatFoldModel.Raw)) + }) + + It("does not duplicate vat events", 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()) + vatfoldRepository := vat_fold.NewVatFoldRepository(db) + err = vatfoldRepository.Create(headerID, test_data.VatFoldModel) + Expect(err).NotTo(HaveOccurred()) + + err = vatfoldRepository.Create(headerID, test_data.VatFoldModel) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint")) + }) + + It("removes vat if corresponding header is deleted", 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()) + vatfoldRepository := vat_fold.NewVatFoldRepository(db) + err = vatfoldRepository.Create(headerID, test_data.VatFoldModel) + Expect(err).NotTo(HaveOccurred()) + + _, err = db.Exec(`DELETE FROM headers WHERE id = $1`, headerID) + + Expect(err).NotTo(HaveOccurred()) + var dbVatFold vat_fold.VatFoldModel + err = db.Get(&dbVatFold, `SELECT ilk, tx_idx, raw_log FROM maker.vat_fold WHERE header_id = $1`, headerID) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(sql.ErrNoRows)) + }) + }) + + Describe("MissingHeaders", func() { + It("returns headers with no associated vat event", func() { + db := test_config.NewTestDB(core.Node{}) + test_config.CleanTestDB(db) + headerRepository := repositories.NewHeaderRepository(db) + startingBlockNumber := int64(1) + vatfoldBlockNumber := int64(2) + endingBlockNumber := int64(3) + blockNumbers := []int64{startingBlockNumber, vatfoldBlockNumber, 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()) + } + vatfoldRepository := vat_fold.NewVatFoldRepository(db) + err := vatfoldRepository.Create(headerIDs[1], test_data.VatFoldModel) + Expect(err).NotTo(HaveOccurred()) + + headers, err := vatfoldRepository.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 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()) + } + vatFoldRepository := vat_fold.NewVatFoldRepository(db) + vatFoldRepositoryTwo := vat_fold.NewVatFoldRepository(dbTwo) + err := vatFoldRepository.Create(headerIDs[0], test_data.VatFoldModel) + Expect(err).NotTo(HaveOccurred()) + + nodeOneMissingHeaders, err := vatFoldRepository.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeOneMissingHeaders)).To(Equal(len(blockNumbers) - 1)) + + nodeTwoMissingHeaders, err := vatFoldRepositoryTwo.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1]) + Expect(err).NotTo(HaveOccurred()) + Expect(len(nodeTwoMissingHeaders)).To(Equal(len(blockNumbers))) + }) + }) +})