diff --git a/pkg/transformers/test_data/mocks/vat_fold/converter.go b/pkg/transformers/test_data/mocks/vat_fold/converter.go new file mode 100644 index 00000000..9625cf78 --- /dev/null +++ b/pkg/transformers/test_data/mocks/vat_fold/converter.go @@ -0,0 +1,36 @@ +// 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/ethereum/go-ethereum/core/types" + + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_fold" +) + +type MockVatFoldConverter struct { + converterErr error + PassedLog types.Log +} + +func (converter *MockVatFoldConverter) ToModel(ethLog types.Log) (vat_fold.VatFoldModel, error) { + converter.PassedLog = ethLog + return test_data.VatFoldModel, converter.converterErr +} + +func (converter *MockVatFoldConverter) SetConverterError(e error) { + converter.converterErr = e +} diff --git a/pkg/transformers/test_data/mocks/vat_fold/repository.go b/pkg/transformers/test_data/mocks/vat_fold/repository.go new file mode 100644 index 00000000..79e973d0 --- /dev/null +++ b/pkg/transformers/test_data/mocks/vat_fold/repository.go @@ -0,0 +1,54 @@ +// 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/transformers/vat_fold" +) + +type MockVatFoldRepository struct { + createErr error + missingHeaders []core.Header + missingHeadersErr error + PassedStartingBlockNumber int64 + PassedEndingBlockNumber int64 + PassedHeaderID int64 + PassedModel vat_fold.VatFoldModel +} + +func (repository *MockVatFoldRepository) Create(headerID int64, model vat_fold.VatFoldModel) error { + repository.PassedHeaderID = headerID + repository.PassedModel = model + return repository.createErr +} + +func (repository *MockVatFoldRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) { + repository.PassedStartingBlockNumber = startingBlockNumber + repository.PassedEndingBlockNumber = endingBlockNumber + return repository.missingHeaders, repository.missingHeadersErr +} + +func (repository *MockVatFoldRepository) SetMissingHeadersErr(e error) { + repository.missingHeadersErr = e +} + +func (repository *MockVatFoldRepository) SetMissingHeaders(headers []core.Header) { + repository.missingHeaders = headers +} + +func (repository *MockVatFoldRepository) SetCreateError(e error) { + repository.createErr = e +} diff --git a/pkg/transformers/vat_fold/config.go b/pkg/transformers/vat_fold/config.go new file mode 100644 index 00000000..56e8983d --- /dev/null +++ b/pkg/transformers/vat_fold/config.go @@ -0,0 +1,27 @@ +// Copyright 2018 Vulcanize +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vat_fold + +import ( + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +var VatFoldConfig = shared.TransformerConfig{ + ContractAddresses: []string{shared.VatContractAddress}, + ContractAbi: shared.VatABI, + Topics: []string{shared.VatFoldSignature}, + StartingBlockNumber: 0, + EndingBlockNumber: 10000000, +} diff --git a/pkg/transformers/vat_fold/transformer.go b/pkg/transformers/vat_fold/transformer.go new file mode 100644 index 00000000..78237fb0 --- /dev/null +++ b/pkg/transformers/vat_fold/transformer.go @@ -0,0 +1,74 @@ +// 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 ( + "log" + + "github.com/ethereum/go-ethereum/common" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" +) + +type VatFoldTransformerInitializer struct { + Config shared.TransformerConfig +} + +func (initializer VatFoldTransformerInitializer) NewVatFoldTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { + converter := VatFoldConverter{} + fetcher := shared.NewFetcher(blockChain) + repository := NewVatFoldRepository(db) + return VatFoldTransformer{ + Config: initializer.Config, + Converter: converter, + Fetcher: fetcher, + Repository: repository, + } +} + +type VatFoldTransformer struct { + Config shared.TransformerConfig + Converter Converter + Fetcher shared.LogFetcher + Repository Repository +} + +func (transformer VatFoldTransformer) Execute() error { + missingHeaders, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber) + if err != nil { + return err + } + log.Printf("Fetching vat fold event logs for %d headers \n", len(missingHeaders)) + for _, header := range missingHeaders { + topics := [][]common.Hash{{common.HexToHash(shared.VatFoldSignature)}} + matchingLogs, err := transformer.Fetcher.FetchLogs(VatFoldConfig.ContractAddresses, topics, header.BlockNumber) + if err != nil { + return err + } + for _, log := range matchingLogs { + model, err := transformer.Converter.ToModel(log) + if err != nil { + return err + } + err = transformer.Repository.Create(header.Id, model) + if err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/transformers/vat_fold/transformer_test.go b/pkg/transformers/vat_fold/transformer_test.go new file mode 100644 index 00000000..57370d2b --- /dev/null +++ b/pkg/transformers/vat_fold/transformer_test.go @@ -0,0 +1,179 @@ +// 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 ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "github.com/vulcanize/vulcanizedb/pkg/transformers/shared" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" + "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" + vat_fold_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/vat_fold" + "github.com/vulcanize/vulcanizedb/pkg/transformers/vat_fold" +) + +type setupOptions struct { + setMissingHeadersError bool + setFetcherError bool + setConverterError bool + setCreateError bool + fetchedLogs []types.Log + missingHeaders []core.Header +} + +func setup(options setupOptions) ( + vat_fold.VatFoldTransformer, + *mocks.MockLogFetcher, + *vat_fold_mocks.MockVatFoldConverter, + *vat_fold_mocks.MockVatFoldRepository, +) { + fetcher := &mocks.MockLogFetcher{} + if options.setFetcherError { + fetcher.SetFetcherError(fakes.FakeError) + } + if len(options.fetchedLogs) > 0 { + fetcher.SetFetchedLogs(options.fetchedLogs) + } + + converter := &vat_fold_mocks.MockVatFoldConverter{} + if options.setConverterError { + converter.SetConverterError(fakes.FakeError) + } + + repository := &vat_fold_mocks.MockVatFoldRepository{} + if options.setMissingHeadersError { + repository.SetMissingHeadersErr(fakes.FakeError) + } + if options.setCreateError { + repository.SetCreateError(fakes.FakeError) + } + if len(options.missingHeaders) > 0 { + repository.SetMissingHeaders(options.missingHeaders) + } + + transformer := vat_fold.VatFoldTransformer{ + Config: vat_fold.VatFoldConfig, + Fetcher: fetcher, + Converter: converter, + Repository: repository, + } + + return transformer, fetcher, converter, repository +} + +var _ = Describe("Vat fold transformer", func() { + It("gets missing headers for block numbers specified in config", func() { + transformer, _, _, repository := setup(setupOptions{}) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedStartingBlockNumber).To(Equal(vat_fold.VatFoldConfig.StartingBlockNumber)) + Expect(repository.PassedEndingBlockNumber).To(Equal(vat_fold.VatFoldConfig.EndingBlockNumber)) + }) + + It("returns error if repository returns error for missing headers", func() { + transformer, _, _, _ := setup(setupOptions{ + setMissingHeadersError: true, + }) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("fetches logs for missing headers", func() { + transformer, fetcher, _, _ := setup(setupOptions{ + missingHeaders: []core.Header{{BlockNumber: 1}, {BlockNumber: 2}}, + }) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(fetcher.FetchedBlocks).To(Equal([]int64{1, 2})) + Expect(fetcher.FetchedContractAddresses).To(Equal([][]string{vat_fold.VatFoldConfig.ContractAddresses, vat_fold.VatFoldConfig.ContractAddresses})) + Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.VatFoldSignature)}})) + }) + + It("returns error if fetcher returns error", func() { + transformer, _, _, _ := setup(setupOptions{ + setFetcherError: true, + missingHeaders: []core.Header{{BlockNumber: 1}}, + }) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("converts matching logs", func() { + transformer, _, converter, _ := setup(setupOptions{ + fetchedLogs: []types.Log{test_data.EthVatFoldLog}, + missingHeaders: []core.Header{{BlockNumber: 1}}, + }) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(converter.PassedLog).To(Equal(test_data.EthVatFoldLog)) + }) + + It("returns error if converter returns error", func() { + transformer, _, _, _ := setup(setupOptions{ + setConverterError: true, + fetchedLogs: []types.Log{test_data.EthVatFoldLog}, + missingHeaders: []core.Header{{BlockNumber: 1}}, + }) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) + + It("persists vat fold model", func() { + fakeHeader := core.Header{BlockNumber: 1, Id: 2} + transformer, _, _, repository := setup(setupOptions{ + fetchedLogs: []types.Log{test_data.EthVatFoldLog}, + missingHeaders: []core.Header{fakeHeader}, + }) + + err := transformer.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(repository.PassedHeaderID).To(Equal(fakeHeader.Id)) + Expect(repository.PassedModel).To(Equal(test_data.VatFoldModel)) + }) + + It("returns error if repository returns error for create", func() { + transformer, _, _, _ := setup(setupOptions{ + fetchedLogs: []types.Log{test_data.EthVatFoldLog}, + missingHeaders: []core.Header{{BlockNumber: 1, Id: 2}}, + setCreateError: true, + }) + + err := transformer.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(fakes.FakeError)) + }) +})