Add vat_move transformer (#47)

* Add vat_move transformer base

* Add vat_move migrations

* Add test data for vat_move

* Add vat_move transformer to initialisers

* Add numeric cast to psql insert of Rad

* Add new db schema

* Dependency update

* Expand abbreviation in repository

* Add test suite for vat_move

* Add header checking to transformer and mock repository

* Remove trailing zero in test data

* Fix minor mishaps

* Go fmt nitpicking

* Refactoring in tests

* Add tests covering checked headers stuff (and fix revealed bugs)

* go fmt fixes

* Implement batching behaviour of transformer

* Small fixes after review

* Go fmt
This commit is contained in:
Edvard Hübinette
2018-10-12 16:13:13 +02:00
committed by GitHub
parent 8fe92edd26
commit be58dd4ac8
23 changed files with 1163 additions and 19 deletions
+2
View File
@@ -58,6 +58,7 @@ var (
vatHealMethod = GetSolidityMethodSignature(VatABI, "heal")
vatGrabMethod = GetSolidityMethodSignature(VatABI, "grab")
vatInitMethod = GetSolidityMethodSignature(VatABI, "init")
vatMoveMethod = GetSolidityMethodSignature(VatABI, "move")
vatFoldMethod = GetSolidityMethodSignature(VatABI, "fold")
vatTollMethod = GetSolidityMethodSignature(VatABI, "toll")
vatTuneMethod = GetSolidityMethodSignature(VatABI, "tune")
@@ -83,6 +84,7 @@ var (
VatHealSignature = GetLogNoteSignature(vatHealMethod)
VatGrabSignature = GetLogNoteSignature(vatGrabMethod)
VatInitSignature = GetLogNoteSignature(vatInitMethod)
VatMoveSignature = GetLogNoteSignature(vatMoveMethod)
VatFoldSignature = GetLogNoteSignature(vatFoldMethod)
VatTollSignature = GetLogNoteSignature(vatTollMethod)
VatTuneSignature = GetLogNoteSignature(vatTuneMethod)
@@ -146,6 +146,13 @@ var _ = Describe("Event signature generator", func() {
Expect(expected).To(Equal(actual))
})
It("gets the vat move method signature", func() {
expected := "move(bytes32,bytes32,int256)"
actual := shared.GetSolidityMethodSignature(shared.VatABI, "move")
Expect(expected).To(Equal(actual))
})
})
Describe("it handles events", func() {
+1 -1
View File
@@ -58,4 +58,4 @@ func hexStringsToAddresses(hexStrings []string) []common.Address {
}
return addresses
}
}
@@ -0,0 +1,35 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move
import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move"
)
type MockVatMoveConverter struct {
converterError error
PassedLogs []types.Log
}
func (converter *MockVatMoveConverter) ToModels(ethLogs []types.Log) ([]vat_move.VatMoveModel, error) {
converter.PassedLogs = ethLogs
return []vat_move.VatMoveModel{test_data.VatMoveModel}, converter.converterError
}
func (converter *MockVatMoveConverter) SetConverterError(e error) {
converter.converterError = e
}
@@ -0,0 +1,65 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move"
)
type MockVatMoveRepository struct {
createError error
missingHeaders []core.Header
missingHeadersError error
PassedStartingBlockNumber int64
PassedEndingBlockNumber int64
PassedHeaderID int64
PassedModels []vat_move.VatMoveModel
CheckedHeaderIDs []int64
CheckedHeaderError error
}
func (repository *MockVatMoveRepository) Create(headerID int64, models []vat_move.VatMoveModel) error {
repository.PassedHeaderID = headerID
repository.PassedModels = models
return repository.createError
}
func (repository *MockVatMoveRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) {
repository.PassedStartingBlockNumber = startingBlockNumber
repository.PassedEndingBlockNumber = endingBlockNumber
return repository.missingHeaders, repository.missingHeadersError
}
func (repository *MockVatMoveRepository) SetMissingHeadersError(e error) {
repository.missingHeadersError = e
}
func (repository *MockVatMoveRepository) SetMissingHeaders(headers []core.Header) {
repository.missingHeaders = headers
}
func (repository *MockVatMoveRepository) SetCreateError(e error) {
repository.createError = e
}
func (repository *MockVatMoveRepository) MarkHeaderChecked(headerId int64) error {
repository.CheckedHeaderIDs = append(repository.CheckedHeaderIDs, headerId)
return repository.CheckedHeaderError
}
func (repository *MockVatMoveRepository) SetCheckedHeaderError(e error) {
repository.CheckedHeaderError = e
}
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package test_data
import (
"encoding/json"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
)
var EthVatMoveLog = types.Log{
Address: common.HexToAddress(shared.VatContractAddress),
Topics: []common.Hash{
common.HexToHash("0x78f1947000000000000000000000000000000000000000000000000000000000"),
common.HexToHash("0xa730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000"),
common.HexToHash("0xb730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000"),
common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000002a"),
},
Data: hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006478f19470a730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000b730d1ff8b6bc74a26d54c20a9dda539909bab0e000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a"),
BlockNumber: 10,
TxHash: common.HexToHash("0xe8f39fbb7fea3621f543868f19b1114e305aff6a063a30d32835ff1012526f91"),
TxIndex: 7,
BlockHash: common.HexToHash("0xe3dd2e05bd8b92833e20ed83e2171bbc06a9ec823232eca1730a807bd8f5edc0"),
Index: 0,
Removed: false,
}
var rawVatMoveLog, _ = json.Marshal(EthVatMoveLog)
var VatMoveModel = vat_move.VatMoveModel{
Src: "0xA730d1FF8B6Bc74a26d54c20a9dda539909BaB0e",
Dst: "0xB730D1fF8b6BC74a26D54c20a9ddA539909BAb0e",
Rad: "42",
TransactionIndex: EthVatMoveLog.TxIndex,
Raw: rawVatMoveLog,
}
+3
View File
@@ -41,6 +41,7 @@ import (
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_grab"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_heal"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_init"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_toll"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_tune"
)
@@ -69,6 +70,7 @@ var (
TendTransformerInitializer = tend.TendTransformerInitializer{Config: tend.TendConfig}.NewTendTransformer
VatGrabTransformerInitializer = vat_grab.VatGrabTransformerInitializer{Config: vat_grab.VatGrabConfig}.NewVatGrabTransformer
VatInitTransformerInitializer = vat_init.VatInitTransformerInitializer{Config: vat_init.VatInitConfig}.NewVatInitTransformer
VatMoveTransformerInitializer = vat_move.VatMoveTransformerInitializer{Config: vat_move.VatMoveConfig}.NewVatMoveTransformer
VatHealTransformerInitializer = vat_heal.VatHealTransformerInitializer{Config: vat_heal.VatHealConfig}.NewVatHealTransformer
VatFoldTransformerInitializer = vat_fold.VatFoldTransformerInitializer{Config: vat_fold.VatFoldConfig}.NewVatFoldTransformer
VatTollTransformerInitializer = vat_toll.VatTollTransformerInitializer{Config: vat_toll.VatTollConfig}.NewVatTollTransformer
@@ -97,6 +99,7 @@ func TransformerInitializers() []shared.TransformerInitializer {
TendTransformerInitializer,
VatGrabTransformerInitializer,
VatInitTransformerInitializer,
VatMoveTransformerInitializer,
VatHealTransformerInitializer,
VatFoldTransformerInitializer,
VatTollTransformerInitializer,
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move
import (
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
)
var VatMoveConfig = shared.TransformerConfig{
ContractAddresses: []string{shared.VatContractAddress},
ContractAbi: shared.VatABI,
Topics: []string{shared.VatMoveSignature},
StartingBlockNumber: 0,
EndingBlockNumber: 10000000,
}
+66
View File
@@ -0,0 +1,66 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move
import (
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type Converter interface {
ToModels(ethLog []types.Log) ([]VatMoveModel, error)
}
type VatMoveConverter struct{}
func (VatMoveConverter) ToModels(ethLogs []types.Log) ([]VatMoveModel, error) {
var models []VatMoveModel
for _, ethLog := range ethLogs {
err := verifyLog(ethLog)
if err != nil {
return []VatMoveModel{}, err
}
src := common.BytesToAddress(ethLog.Topics[1].Bytes()[:common.AddressLength])
dst := common.BytesToAddress(ethLog.Topics[2].Bytes()[:common.AddressLength])
rad := ethLog.Topics[3].Big()
raw, err := json.Marshal(ethLog)
if err != nil {
return []VatMoveModel{}, err
}
models = append(models, VatMoveModel{
Src: src.String(),
Dst: dst.String(),
Rad: rad.String(),
TransactionIndex: ethLog.TxIndex,
Raw: raw,
})
}
return models, nil
}
func verifyLog(ethLog types.Log) error {
if len(ethLog.Data) <= 0 {
return errors.New("log data is empty")
}
if len(ethLog.Topics) < 4 {
return errors.New("log missing topics")
}
return nil
}
@@ -0,0 +1,44 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move_test
import (
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move"
)
var _ = Describe("Vat move converter", func() {
It("returns err if logs are missing topics", func() {
converter := vat_move.VatMoveConverter{}
badLog := types.Log{}
_, err := converter.ToModels([]types.Log{badLog})
Expect(err).To(HaveOccurred())
})
It("converts a log to a model", func() {
converter := vat_move.VatMoveConverter{}
models, err := converter.ToModels([]types.Log{test_data.EthVatMoveLog})
Expect(err).NotTo(HaveOccurred())
Expect(models[0]).To(Equal(test_data.VatMoveModel))
})
})
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move
type VatMoveModel struct {
Src string
Dst string
Rad string
TransactionIndex uint `db:"tx_idx"`
Raw []byte `db:"raw_log"`
}
+96
View File
@@ -0,0 +1,96 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
)
type Repository interface {
Create(headerID int64, models []VatMoveModel) error
MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error)
MarkHeaderChecked(headerID int64) error
}
type VatMoveRepository struct {
db *postgres.DB
}
func NewVatMoveRepository(db *postgres.DB) VatMoveRepository {
return VatMoveRepository{
db: db,
}
}
func (repository VatMoveRepository) Create(headerID int64, models []VatMoveModel) error {
tx, err := repository.db.Begin()
if err != nil {
return err
}
for _, vatMove := range models {
_, err = tx.Exec(
`INSERT INTO maker.vat_move (header_id, src, dst, rad, tx_idx, raw_log)
VALUES ($1, $2, $3, $4::NUMERIC, $5, $6)`,
headerID, vatMove.Src, vatMove.Dst, vatMove.Rad, vatMove.TransactionIndex, vatMove.Raw,
)
if err != nil {
tx.Rollback()
return err
}
}
_, err = tx.Exec(
`INSERT INTO public.checked_headers (header_id, vat_move_checked)
VALUES ($1, $2)
ON CONFLICT (header_id) DO
UPDATE SET vat_move_checked = $2`,
headerID, true)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
func (repository VatMoveRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) {
var result []core.Header
err := repository.db.Select(
&result,
`SELECT headers.id, headers.block_number FROM headers
LEFT JOIN checked_headers on headers.id = header_id
WHERE (header_id ISNULL OR vat_move_checked IS FALSE)
AND headers.block_number >= $1
AND headers.block_number <= $2
AND headers.eth_node_fingerprint = $3`,
startingBlockNumber,
endingBlockNumber,
repository.db.Node.ID,
)
return result, err
}
func (repository VatMoveRepository) MarkHeaderChecked(headerID int64) error {
_, err := repository.db.Exec(`INSERT INTO public.checked_headers (header_id, vat_move_checked)
VALUES ($1, $2)
ON CONFLICT (header_id) DO
UPDATE SET vat_move_checked = $2`, headerID, true)
return err
}
@@ -0,0 +1,192 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move_test
import (
"database/sql"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move"
"github.com/vulcanize/vulcanizedb/test_config"
"math/rand"
)
var _ = Describe("Vat Move", func() {
var db *postgres.DB
var headerRepository repositories.HeaderRepository
var vatMoveRepository vat_move.VatMoveRepository
BeforeEach(func() {
db = test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
headerRepository = repositories.NewHeaderRepository(db)
vatMoveRepository = vat_move.NewVatMoveRepository(db)
})
Describe("Create", func() {
var headerID int64
var err error
BeforeEach(func() {
headerID, err = headerRepository.CreateOrUpdateHeader(core.Header{})
Expect(err).NotTo(HaveOccurred())
err = vatMoveRepository.Create(headerID, []vat_move.VatMoveModel{test_data.VatMoveModel})
Expect(err).NotTo(HaveOccurred())
})
It("adds a vat move event", func() {
var dbVatMove vat_move.VatMoveModel
err = db.Get(&dbVatMove, `SELECT src, dst, rad, tx_idx, raw_log FROM maker.vat_move WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(dbVatMove.Src).To(Equal(test_data.VatMoveModel.Src))
Expect(dbVatMove.Dst).To(Equal(test_data.VatMoveModel.Dst))
Expect(dbVatMove.Rad).To(Equal(test_data.VatMoveModel.Rad))
Expect(dbVatMove.TransactionIndex).To(Equal(test_data.VatMoveModel.TransactionIndex))
Expect(dbVatMove.Raw).To(MatchJSON(test_data.VatMoveModel.Raw))
})
It("marks header id as checked for vat move logs", func() {
var headerChecked bool
err = db.Get(&headerChecked, `SELECT vat_move_checked FROM public.checked_headers WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(headerChecked).To(BeTrue())
})
It("returns an error if insertion fails", func() {
err = vatMoveRepository.Create(headerID, []vat_move.VatMoveModel{test_data.VatMoveModel})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint"))
})
It("removes vat move event if corresponding header is deleted", func() {
_, err = db.Exec(`DELETE FROM headers WHERE id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
var dbVatMove vat_move.VatMoveModel
err = db.Get(&dbVatMove, `SELECT src, dst, rad, tx_idx, raw_log FROM maker.vat_move WHERE header_id = $1`, headerID)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(sql.ErrNoRows))
})
})
Describe("MissingHeaders", func() {
var eventBlockNumber = rand.Int63()
var startingBlockNumber = eventBlockNumber - 1
var endingBlockNumber = eventBlockNumber + 1
var outOfRangeBlockNumber = eventBlockNumber + 2
It("returns headers haven't been checked", func() {
var headerIds []int64
for _, number := range []int64{startingBlockNumber, eventBlockNumber, endingBlockNumber, outOfRangeBlockNumber} {
headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number})
Expect(err).NotTo(HaveOccurred())
headerIds = append(headerIds, headerId)
}
err := vatMoveRepository.MarkHeaderChecked(headerIds[1])
Expect(err).NotTo(HaveOccurred())
headers, err := vatMoveRepository.MissingHeaders(startingBlockNumber, endingBlockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(len(headers)).To(Equal(2))
Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber)))
Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber)))
})
It("only treats headers as checked if vat_move has been checked", func() {
var headerIds []int64
for _, number := range []int64{startingBlockNumber, eventBlockNumber, endingBlockNumber, outOfRangeBlockNumber} {
headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number})
Expect(err).NotTo(HaveOccurred())
headerIds = append(headerIds, headerId)
}
// Just creates row, doesn't set this header as checked
_, err := db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIds[1])
Expect(err).NotTo(HaveOccurred())
headers, err := vatMoveRepository.MissingHeaders(startingBlockNumber, endingBlockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(len(headers)).To(Equal(3))
Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventBlockNumber)))
Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventBlockNumber)))
Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventBlockNumber)))
})
It("only returns headers associated with the current node", func() {
blockNumbers := []int64{1, 2, 3}
dbTwo := test_config.NewTestDB(core.Node{ID: "second"})
headerRepositoryTwo := repositories.NewHeaderRepository(dbTwo)
var headerIDs []int64
for _, n := range blockNumbers {
headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n})
Expect(err).NotTo(HaveOccurred())
headerIDs = append(headerIDs, headerID)
_, err = headerRepositoryTwo.CreateOrUpdateHeader(core.Header{BlockNumber: n})
Expect(err).NotTo(HaveOccurred())
}
vatMoveRepositoryTwo := vat_move.NewVatMoveRepository(dbTwo)
err := vatMoveRepository.Create(headerIDs[0], []vat_move.VatMoveModel{test_data.VatMoveModel})
Expect(err).NotTo(HaveOccurred())
nodeOneMissingHeaders, err := vatMoveRepository.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1])
Expect(err).NotTo(HaveOccurred())
Expect(len(nodeOneMissingHeaders)).To(Equal(len(blockNumbers) - 1))
nodeTwoMissingHeaders, err := vatMoveRepositoryTwo.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1])
Expect(err).NotTo(HaveOccurred())
Expect(len(nodeTwoMissingHeaders)).To(Equal(len(blockNumbers)))
})
})
Describe("MarkHeaderChecked", func() {
var headerID int64
var err error
BeforeEach(func() {
headerID, err = headerRepository.CreateOrUpdateHeader(core.Header{})
Expect(err).NotTo(HaveOccurred())
})
It("creates a new row for a new headerID", func() {
err = vatMoveRepository.MarkHeaderChecked(headerID)
Expect(err).NotTo(HaveOccurred())
var headerChecked bool
err = db.Get(&headerChecked, `SELECT vat_move_checked FROM public.checked_headers WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(headerChecked).To(BeTrue())
})
It("updates row when headerId already exists", func() {
_, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerID)
Expect(err).NotTo(HaveOccurred())
err = vatMoveRepository.MarkHeaderChecked(headerID)
Expect(err).NotTo(HaveOccurred())
var headerChecked bool
err = db.Get(&headerChecked, `SELECT vat_move_checked FROM public.checked_headers WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(headerChecked).To(BeTrue())
})
})
})
+83
View File
@@ -0,0 +1,83 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move
import (
"log"
"github.com/ethereum/go-ethereum/common"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
)
type VatMoveTransformerInitializer struct {
Config shared.TransformerConfig
}
func (initializer VatMoveTransformerInitializer) NewVatMoveTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer {
converter := VatMoveConverter{}
fetcher := shared.NewFetcher(blockChain)
repository := NewVatMoveRepository(db)
return VatMoveTransformer{
Config: initializer.Config,
Converter: converter,
Fetcher: fetcher,
Repository: repository,
}
}
type VatMoveTransformer struct {
Config shared.TransformerConfig
Converter Converter
Fetcher shared.LogFetcher
Repository Repository
}
func (transformer VatMoveTransformer) Execute() error {
missingHeaders, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber)
if err != nil {
return err
}
log.Printf("Fetching vat move event logs for %d headers \n", len(missingHeaders))
for _, header := range missingHeaders {
topics := [][]common.Hash{{common.HexToHash(shared.VatMoveSignature)}}
matchingLogs, err := transformer.Fetcher.FetchLogs(VatMoveConfig.ContractAddresses, topics, header.BlockNumber)
if err != nil {
return err
}
if len(matchingLogs) < 1 {
err := transformer.Repository.MarkHeaderChecked(header.Id)
if err != nil {
return err
}
continue
}
models, err := transformer.Converter.ToModels(matchingLogs)
if err != nil {
return err
}
err = transformer.Repository.Create(header.Id, models)
if err != nil {
return err
}
}
return nil
}
@@ -0,0 +1,212 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move_test
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks"
vat_move_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/vat_move"
"github.com/vulcanize/vulcanizedb/pkg/transformers/vat_move"
)
var _ = Describe("Vat move transformer", func() {
var fetcher mocks.MockLogFetcher
var converter vat_move_mocks.MockVatMoveConverter
var repository vat_move_mocks.MockVatMoveRepository
var config = vat_move.VatMoveConfig
var headerOne core.Header
var headerTwo core.Header
BeforeEach(func() {
fetcher = mocks.MockLogFetcher{}
converter = vat_move_mocks.MockVatMoveConverter{}
repository = vat_move_mocks.MockVatMoveRepository{}
headerOne = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()}
headerTwo = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()}
})
It("gets missing headers for block numbers specified in config", func() {
transformer := vat_move.VatMoveTransformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(repository.PassedStartingBlockNumber).To(Equal(vat_move.VatMoveConfig.StartingBlockNumber))
Expect(repository.PassedEndingBlockNumber).To(Equal(vat_move.VatMoveConfig.EndingBlockNumber))
})
It("returns error if repository returns error for missing headers", func() {
repository.SetMissingHeadersError(fakes.FakeError)
transformer := vat_move.VatMoveTransformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("fetches logs for missing headers", func() {
repository.SetMissingHeaders([]core.Header{headerOne, headerTwo})
transformer := vat_move.VatMoveTransformer{
Config: config,
Fetcher: &fetcher,
Converter: &vat_move_mocks.MockVatMoveConverter{},
Repository: &repository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(fetcher.FetchedBlocks).To(Equal([]int64{headerOne.BlockNumber, headerTwo.BlockNumber}))
Expect(fetcher.FetchedContractAddresses).To(Equal([][]string{
vat_move.VatMoveConfig.ContractAddresses,
vat_move.VatMoveConfig.ContractAddresses,
}))
Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.VatMoveSignature)}}))
})
It("returns error if fetcher returns error", func() {
fetcher.SetFetcherError(fakes.FakeError)
repository.SetMissingHeaders([]core.Header{headerOne})
transformer := vat_move.VatMoveTransformer{
Config: config,
Fetcher: &fetcher,
Converter: &converter,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("converts matching logs", func() {
fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog})
repository.SetMissingHeaders([]core.Header{headerOne})
transformer := vat_move.VatMoveTransformer{
Config: config,
Fetcher: &fetcher,
Converter: &converter,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(converter.PassedLogs).To(Equal([]types.Log{test_data.EthVatMoveLog}))
})
It("returns error if converter returns error", func() {
converter.SetConverterError(fakes.FakeError)
fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog})
repository.SetMissingHeaders([]core.Header{headerOne})
transformer := vat_move.VatMoveTransformer{
Config: config,
Fetcher: &fetcher,
Converter: &converter,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("marks header as checked even if no logs were returned", func() {
repository.SetMissingHeaders([]core.Header{headerOne, headerTwo})
fetcher.SetFetchedLogs([]types.Log{})
transformer := vat_move.VatMoveTransformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(repository.CheckedHeaderIDs).To(ContainElement(headerOne.Id))
Expect(repository.CheckedHeaderIDs).To(ContainElement(headerTwo.Id))
})
It("returns error if marking header checked returns err", func() {
repository.SetMissingHeaders([]core.Header{headerOne, headerTwo})
repository.SetCheckedHeaderError(fakes.FakeError)
fetcher.SetFetchedLogs([]types.Log{})
transformer := vat_move.VatMoveTransformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("persists vat move model", func() {
fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog})
repository.SetMissingHeaders([]core.Header{headerOne})
transformer := vat_move.VatMoveTransformer{
Config: config,
Fetcher: &fetcher,
Converter: &converter,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(repository.PassedHeaderID).To(Equal(headerOne.Id))
Expect(repository.PassedModels).To(Equal([]vat_move.VatMoveModel{test_data.VatMoveModel}))
})
It("returns error if repository returns error for create", func() {
fetcher.SetFetchedLogs([]types.Log{test_data.EthVatMoveLog})
repository.SetMissingHeaders([]core.Header{headerOne})
repository.SetCreateError(fakes.FakeError)
transformer := vat_move.VatMoveTransformer{
Config: config,
Fetcher: &fetcher,
Converter: &converter,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
})
@@ -0,0 +1,33 @@
// Copyright 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vat_move_test
import (
"io/ioutil"
"log"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestVatMove(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "VatMove Suite")
}
var _ = BeforeSuite(func() {
log.SetOutput(ioutil.Discard)
})