From 11b5efbfe38f5566469262afc932adfd55b0f33c Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Tue, 8 Oct 2019 11:48:15 -0500 Subject: [PATCH 1/4] fix for issue #146; mark header checked for contract if it doesnt have any logs at that header but other contracts do; test --- ...ct_watcher_header_sync_transformer_test.go | 40 ++++++++++++++++++- .../header/repository/header_repository.go | 10 ----- .../header/transformer/transformer.go | 17 +++++++- .../shared/helpers/test_helpers/database.go | 3 +- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/integration_test/contract_watcher_header_sync_transformer_test.go b/integration_test/contract_watcher_header_sync_transformer_test.go index 4181b08e..08030134 100644 --- a/integration_test/contract_watcher_header_sync_transformer_test.go +++ b/integration_test/contract_watcher_header_sync_transformer_test.go @@ -40,8 +40,8 @@ var _ = Describe("contractWatcher headerSync transformer", func() { var blockChain core.BlockChain var headerRepository repositories.HeaderRepository var headerID int64 - var ensAddr = strings.ToLower(constants.EnsContractAddress) - var tusdAddr = strings.ToLower(constants.TusdContractAddress) + var ensAddr = strings.ToLower(constants.EnsContractAddress) // 0x314159265dd8dbb310642f98f50c066173c1259b + var tusdAddr = strings.ToLower(constants.TusdContractAddress) // 0x8dd5fbce2f6a956c3022ba3663759011dd51e73e BeforeEach(func() { db, blockChain = test_helpers.SetupDBandBC() @@ -377,6 +377,42 @@ var _ = Describe("contractWatcher headerSync transformer", func() { Expect(transferLog.Value).To(Equal("2800000000000000000000")) }) + It("Marks header checked for a contract that has no logs at that header", func() { + t := transformer.NewTransformer(test_helpers.ENSandTusdConfig, blockChain, db) + err = t.Init() + Expect(err).ToNot(HaveOccurred()) + err = t.Execute() + Expect(err).ToNot(HaveOccurred()) + Expect(t.Start).To(Equal(int64(6885702))) + + newOwnerLog := test_helpers.HeaderSyncNewOwnerLog{} + err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.newowner_event", ensAddr)).StructScan(&newOwnerLog) + Expect(err).ToNot(HaveOccurred()) + transferLog := test_helpers.HeaderSyncTransferLog{} + err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.transfer_event", tusdAddr)).StructScan(&transferLog) + Expect(err).ToNot(HaveOccurred()) + Expect(transferLog.HeaderID).ToNot(Equal(newOwnerLog.HeaderID)) + + type checkedHeader struct { + ID int64 `db:"id"` + HeaderID int64 `db:"header_id"` + NewOwner int64 `db:"newowner_0x314159265dd8dbb310642f98f50c066173c1259b"` + Transfer int64 `db:"transfer_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e"` + } + + transferCheckedHeader := new(checkedHeader) + err = db.QueryRowx("SELECT * FROM public.checked_headers WHERE header_id = $1", transferLog.HeaderID).StructScan(transferCheckedHeader) + Expect(err).ToNot(HaveOccurred()) + Expect(transferCheckedHeader.Transfer).To(Equal(int64(1))) + Expect(transferCheckedHeader.NewOwner).To(Equal(int64(1))) + + newOwnerCheckedHeader := new(checkedHeader) + err = db.QueryRowx("SELECT * FROM public.checked_headers WHERE header_id = $1", newOwnerLog.HeaderID).StructScan(newOwnerCheckedHeader) + Expect(err).ToNot(HaveOccurred()) + Expect(newOwnerCheckedHeader.NewOwner).To(Equal(int64(1))) + Expect(newOwnerCheckedHeader.Transfer).To(Equal(int64(1))) + }) + It("Keeps track of contract-related hashes and addresses while transforming event data if they need to be used for later method polling", func() { var testConf config.ContractConfig testConf = test_helpers.ENSandTusdConfig diff --git a/pkg/contract_watcher/header/repository/header_repository.go b/pkg/contract_watcher/header/repository/header_repository.go index 4ad817f0..11be42a9 100644 --- a/pkg/contract_watcher/header/repository/header_repository.go +++ b/pkg/contract_watcher/header/repository/header_repository.go @@ -20,7 +20,6 @@ import ( "fmt" "github.com/hashicorp/golang-lru" - "github.com/jmoiron/sqlx" "github.com/sirupsen/logrus" "github.com/vulcanize/vulcanizedb/pkg/core" @@ -268,12 +267,3 @@ func continuousHeaders(headers []core.Header) []core.Header { func (r *headerRepository) CheckCache(key string) (interface{}, bool) { return r.columns.Get(key) } - -// Used to mark a header checked as part of some external transaction so as to group into one commit -func (r *headerRepository) MarkHeaderCheckedInTransaction(headerID int64, tx *sqlx.Tx, eventID string) error { - _, err := tx.Exec(`INSERT INTO public.checked_headers (header_id, `+eventID+`) - VALUES ($1, $2) - ON CONFLICT (header_id) DO - UPDATE SET `+eventID+` = checked_headers.`+eventID+` + 1`, headerID, 1) - return err -} diff --git a/pkg/contract_watcher/header/transformer/transformer.go b/pkg/contract_watcher/header/transformer/transformer.go index 34b3ddc1..6c436412 100644 --- a/pkg/contract_watcher/header/transformer/transformer.go +++ b/pkg/contract_watcher/header/transformer/transformer.go @@ -249,6 +249,11 @@ func (tr *Transformer) Execute() error { } // Sort logs by the contract they belong to + // Start by adding every contract addr to the map + // So that if we don't have any logs for it, it is caught and the header is still marked checked for its events + for _, addr := range tr.contractAddresses { + sortedLogs[addr] = nil + } for _, log := range allLogs { addr := strings.ToLower(log.Address.Hex()) sortedLogs[addr] = append(sortedLogs[addr], log) @@ -256,12 +261,20 @@ func (tr *Transformer) Execute() error { // Process logs for each contract for conAddr, logs := range sortedLogs { - if logs == nil { + con := tr.Contracts[conAddr] + if len(logs) < 1 { + eventIds := make([]string, 0) + for eventName := range con.Events { + eventIds = append(eventIds, strings.ToLower(eventName+"_"+con.Address)) + } + markCheckedErr := tr.HeaderRepository.MarkHeaderCheckedForAll(header.Id, eventIds) + if markCheckedErr != nil { + return fmt.Errorf("error marking header checked: %s", markCheckedErr.Error()) + } logrus.Tracef("no logs found for contract %s at block %d, continuing", conAddr, header.BlockNumber) continue } // Configure converter with this contract - con := tr.Contracts[conAddr] tr.Converter.Update(con) // Convert logs into batches of log mappings (eventName => []types.Logs diff --git a/pkg/contract_watcher/shared/helpers/test_helpers/database.go b/pkg/contract_watcher/shared/helpers/test_helpers/database.go index 352ad79b..1988060e 100644 --- a/pkg/contract_watcher/shared/helpers/test_helpers/database.go +++ b/pkg/contract_watcher/shared/helpers/test_helpers/database.go @@ -294,8 +294,7 @@ func TearDown(db *postgres.DB) { _, err = tx.Exec(`CREATE TABLE checked_headers ( id SERIAL PRIMARY KEY, - header_id INTEGER UNIQUE NOT NULL REFERENCES headers (id) ON DELETE CASCADE, - check_count INTEGER NOT NULL DEFAULT 1);`) + header_id INTEGER UNIQUE NOT NULL REFERENCES headers (id) ON DELETE CASCADE);`) Expect(err).NotTo(HaveOccurred()) _, err = tx.Exec(`DROP SCHEMA IF EXISTS full_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e CASCADE`) From 65808998b37e734a1e68935c1a7b4cf116d80bc2 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Tue, 15 Oct 2019 13:12:17 -0500 Subject: [PATCH 2/4] goimports -w; golinting, remove some unused code --- .../contract_watcher_full_transformer_test.go | 2 +- .../full/converter/converter.go | 11 +++---- .../full/retriever/block_retriever.go | 15 ++++++---- .../full/retriever/block_retriever_test.go | 3 +- .../full/retriever/retriever_suite_test.go | 2 +- .../full/transformer/transformer.go | 6 +++- .../transformer/transformer_suite_test.go | 2 +- .../header/converter/converter.go | 14 +++++---- .../header/converter/converter_test.go | 4 +-- .../header/fetcher/fetcher.go | 6 ++-- .../header/repository/header_repository.go | 22 +++++++------- .../repository/header_repository_test.go | 2 +- .../repository/repository_suite_test.go | 2 +- .../header/retriever/block_retriever.go | 9 +++--- .../header/retriever/retriever_suite_test.go | 2 +- .../header/transformer/transformer.go | 24 ++++++++------- .../transformer/transformer_suite_test.go | 2 +- .../header/transformer/transformer_test.go | 1 + .../shared/constants/constants.go | 27 +++-------------- .../shared/constants/interface.go | 10 ++++--- .../shared/contract/contract.go | 14 +++++---- .../shared/fetcher/fetcher.go | 14 ++++----- .../shared/getter/interface_getter.go | 21 ++++++-------- .../shared/helpers/helpers.go | 3 ++ pkg/contract_watcher/shared/parser/parser.go | 17 ++++++----- pkg/contract_watcher/shared/poller/poller.go | 20 ++++++++----- .../shared/repository/event_repository.go | 21 ++++++++------ .../shared/repository/method_repository.go | 12 +++++--- .../repository/repository_suite_test.go | 2 +- .../shared/retriever/address_retriever.go | 9 +++--- .../shared/retriever/retriever_suite_test.go | 2 +- pkg/contract_watcher/shared/types/event.go | 9 ++++-- pkg/contract_watcher/shared/types/method.go | 6 ++-- pkg/contract_watcher/shared/types/mode.go | 29 +++---------------- 34 files changed, 175 insertions(+), 170 deletions(-) diff --git a/integration_test/contract_watcher_full_transformer_test.go b/integration_test/contract_watcher_full_transformer_test.go index 4e0cce01..0a606c43 100644 --- a/integration_test/contract_watcher_full_transformer_test.go +++ b/integration_test/contract_watcher_full_transformer_test.go @@ -18,7 +18,6 @@ package integration import ( "fmt" - "github.com/vulcanize/vulcanizedb/pkg/config" "math/rand" "strings" "time" @@ -27,6 +26,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/pkg/config" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/transformer" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers" diff --git a/pkg/contract_watcher/full/converter/converter.go b/pkg/contract_watcher/full/converter/converter.go index 09bae256..5d47199b 100644 --- a/pkg/contract_watcher/full/converter/converter.go +++ b/pkg/contract_watcher/full/converter/converter.go @@ -17,7 +17,6 @@ package converter import ( - "errors" "fmt" "math/big" "strconv" @@ -32,22 +31,24 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/core" ) -// Converter is used to convert watched event logs to +// ConverterInterface is used to convert watched event logs to // custom logs containing event input name => value maps type ConverterInterface interface { Convert(watchedEvent core.WatchedEvent, event types.Event) (*types.Log, error) Update(info *contract.Contract) } +// Converter is the underlying struct for the ConverterInterface type Converter struct { ContractInfo *contract.Contract } +// Update configures the converter for a specific contract func (c *Converter) Update(info *contract.Contract) { c.ContractInfo = info } -// Convert the given watched event log into a types.Log for the given event +// Convert converts the given watched event log into a types.Log for the given event func (c *Converter) Convert(watchedEvent core.WatchedEvent, event types.Event) (*types.Log, error) { boundContract := bind.NewBoundContract(common.HexToAddress(c.ContractInfo.Address), c.ContractInfo.ParsedAbi, nil, nil, nil) values := make(map[string]interface{}) @@ -88,14 +89,14 @@ func (c *Converter) Convert(watchedEvent core.WatchedEvent, event types.Event) ( b := input.(byte) strValues[fieldName] = string(b) default: - return nil, errors.New(fmt.Sprintf("error: unhandled abi type %T", input)) + return nil, fmt.Errorf("error: unhandled abi type %T", input) } } // Only hold onto logs that pass our address filter, if any if c.ContractInfo.PassesEventFilter(strValues) { eventLog := &types.Log{ - Id: watchedEvent.LogID, + ID: watchedEvent.LogID, Values: strValues, Block: watchedEvent.BlockNumber, Tx: watchedEvent.TxHash, diff --git a/pkg/contract_watcher/full/retriever/block_retriever.go b/pkg/contract_watcher/full/retriever/block_retriever.go index 24c41003..0003e4c3 100644 --- a/pkg/contract_watcher/full/retriever/block_retriever.go +++ b/pkg/contract_watcher/full/retriever/block_retriever.go @@ -18,11 +18,12 @@ package retriever import ( "database/sql" + "github.com/vulcanize/vulcanizedb/libraries/shared/repository" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" ) -// Block retriever is used to retrieve the first block for a given contract and the most recent block +// BlockRetriever is used to retrieve the first block for a given contract and the most recent block // It requires a vDB synced database with blocks, transactions, receipts, and logs type BlockRetriever interface { RetrieveFirstBlock(contractAddr string) (int64, error) @@ -33,13 +34,15 @@ type blockRetriever struct { db *postgres.DB } -func NewBlockRetriever(db *postgres.DB) (r *blockRetriever) { +// NewBlockRetriever returns a new BlockRetriever +func NewBlockRetriever(db *postgres.DB) BlockRetriever { return &blockRetriever{ db: db, } } -// Try both methods of finding the first block, with the receipt method taking precedence +// RetrieveFirstBlock fetches the block number for the earliest block in the db +// Tries both methods of finding the first block, with the receipt method taking precedence func (r *blockRetriever) RetrieveFirstBlock(contractAddr string) (int64, error) { i, err := r.retrieveFirstBlockFromReceipts(contractAddr) if err != nil { @@ -55,7 +58,7 @@ func (r *blockRetriever) RetrieveFirstBlock(contractAddr string) (int64, error) // For some contracts the contract creation transaction receipt doesn't have the contract address so this doesn't work (e.g. Sai) func (r *blockRetriever) retrieveFirstBlockFromReceipts(contractAddr string) (int64, error) { var firstBlock int64 - addressId, getAddressErr := repository.GetOrCreateAddress(r.db, contractAddr) + addressID, getAddressErr := repository.GetOrCreateAddress(r.db, contractAddr) if getAddressErr != nil { return firstBlock, getAddressErr } @@ -66,7 +69,7 @@ func (r *blockRetriever) retrieveFirstBlockFromReceipts(contractAddr string) (in WHERE contract_address_id = $1 ORDER BY block_id ASC LIMIT 1)`, - addressId, + addressID, ) return firstBlock, err @@ -84,7 +87,7 @@ func (r *blockRetriever) retrieveFirstBlockFromLogs(contractAddr string) (int64, return int64(firstBlock), err } -// Method to retrieve the most recent block in vDB +// RetrieveMostRecentBlock retrieves the most recent block number in vDB func (r *blockRetriever) RetrieveMostRecentBlock() (int64, error) { var lastBlock int64 err := r.db.Get( diff --git a/pkg/contract_watcher/full/retriever/block_retriever_test.go b/pkg/contract_watcher/full/retriever/block_retriever_test.go index 88b171f8..adf55bf9 100644 --- a/pkg/contract_watcher/full/retriever/block_retriever_test.go +++ b/pkg/contract_watcher/full/retriever/block_retriever_test.go @@ -17,10 +17,11 @@ package retriever_test import ( + "strings" + "github.com/ethereum/go-ethereum/core/types" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "strings" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/retriever" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants" diff --git a/pkg/contract_watcher/full/retriever/retriever_suite_test.go b/pkg/contract_watcher/full/retriever/retriever_suite_test.go index 005d2817..2f97ce0a 100644 --- a/pkg/contract_watcher/full/retriever/retriever_suite_test.go +++ b/pkg/contract_watcher/full/retriever/retriever_suite_test.go @@ -17,12 +17,12 @@ package retriever_test import ( - "github.com/sirupsen/logrus" "io/ioutil" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" ) func TestRetriever(t *testing.T) { diff --git a/pkg/contract_watcher/full/transformer/transformer.go b/pkg/contract_watcher/full/transformer/transformer.go index dbc1dced..005c1fc7 100644 --- a/pkg/contract_watcher/full/transformer/transformer.go +++ b/pkg/contract_watcher/full/transformer/transformer.go @@ -35,6 +35,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" ) +// Transformer is the top level struct for transforming watched contract data // Requires a fully synced vDB and a running eth node (or infura) type Transformer struct { // Database interfaces @@ -60,7 +61,7 @@ type Transformer struct { LastBlock int64 } -// Transformer takes in config for blockchain, database, and network id +// NewTransformer takes in contract config, blockchain, and database, and returns a new Transformer func NewTransformer(con config.ContractConfig, BC core.BlockChain, DB *postgres.DB) *Transformer { return &Transformer{ Poller: poller.NewPoller(BC, DB, types.FullSync), @@ -75,6 +76,7 @@ func NewTransformer(con config.ContractConfig, BC core.BlockChain, DB *postgres. } } +// Init initializes the transformer // Use after creating and setting transformer // Loops over all of the addr => filter sets // Uses parser to pull event info from abi @@ -167,6 +169,7 @@ func (tr *Transformer) Init() error { return nil } +// Execute runs the transformation processes // Iterates through stored, initialized contract objects // Iterates through contract's event filters, grabbing watched event logs // Uses converter to convert logs into custom log type @@ -227,6 +230,7 @@ func (tr *Transformer) Execute() error { return nil } +// GetConfig returns the transformers config; satisfies the transformer interface func (tr *Transformer) GetConfig() config.ContractConfig { return tr.Config } diff --git a/pkg/contract_watcher/full/transformer/transformer_suite_test.go b/pkg/contract_watcher/full/transformer/transformer_suite_test.go index 42d0ffb0..aac31e85 100644 --- a/pkg/contract_watcher/full/transformer/transformer_suite_test.go +++ b/pkg/contract_watcher/full/transformer/transformer_suite_test.go @@ -17,12 +17,12 @@ package transformer_test import ( - "github.com/sirupsen/logrus" "io/ioutil" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" ) func TestTransformer(t *testing.T) { diff --git a/pkg/contract_watcher/header/converter/converter.go b/pkg/contract_watcher/header/converter/converter.go index afc04212..5e263ed2 100644 --- a/pkg/contract_watcher/header/converter/converter.go +++ b/pkg/contract_watcher/header/converter/converter.go @@ -18,7 +18,6 @@ package converter import ( "encoding/json" - "errors" "fmt" "math/big" "strconv" @@ -32,16 +31,19 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/types" ) +// ConverterInterface is the interface for converting geth logs to our custom log type type ConverterInterface interface { Convert(logs []gethTypes.Log, event types.Event, headerID int64) ([]types.Log, error) ConvertBatch(logs []gethTypes.Log, events map[string]types.Event, headerID int64) (map[string][]types.Log, error) Update(info *contract.Contract) } +// Converter is the underlying struct for the ConverterInterface type Converter struct { ContractInfo *contract.Contract } +// Update is used to configure the converter with a specific contract func (c *Converter) Update(info *contract.Contract) { c.ContractInfo = info } @@ -98,7 +100,7 @@ func (c *Converter) Convert(logs []gethTypes.Log, event types.Event, headerID in strValues[fieldName] = converted.String() seenHashes = append(seenHashes, converted) default: - return nil, errors.New(fmt.Sprintf("error: unhandled abi type %T", input)) + return nil, fmt.Errorf("error: unhandled abi type %T", input) } } @@ -114,7 +116,7 @@ func (c *Converter) Convert(logs []gethTypes.Log, event types.Event, headerID in Values: strValues, Raw: raw, TransactionIndex: log.TxIndex, - Id: headerID, + ID: headerID, }) // Cache emitted values if their caching is turned on @@ -130,7 +132,7 @@ func (c *Converter) Convert(logs []gethTypes.Log, event types.Event, headerID in return returnLogs, nil } -// Convert the given watched event logs into types.Logs; returns a map of event names to a slice of their converted logs +// ConvertBatch converts the given watched event logs into types.Logs; returns a map of event names to a slice of their converted logs func (c *Converter) ConvertBatch(logs []gethTypes.Log, events map[string]types.Event, headerID int64) (map[string][]types.Log, error) { boundContract := bind.NewBoundContract(common.HexToAddress(c.ContractInfo.Address), c.ContractInfo.ParsedAbi, nil, nil, nil) eventsToLogs := make(map[string][]types.Log) @@ -182,7 +184,7 @@ func (c *Converter) ConvertBatch(logs []gethTypes.Log, events map[string]types.E strValues[fieldName] = converted.String() seenHashes = append(seenHashes, converted) default: - return nil, errors.New(fmt.Sprintf("error: unhandled abi type %T", input)) + return nil, fmt.Errorf("error: unhandled abi type %T", input) } } @@ -198,7 +200,7 @@ func (c *Converter) ConvertBatch(logs []gethTypes.Log, events map[string]types.E Values: strValues, Raw: raw, TransactionIndex: log.TxIndex, - Id: headerID, + ID: headerID, }) // Cache emitted values that pass the argument filter if their caching is turned on diff --git a/pkg/contract_watcher/header/converter/converter_test.go b/pkg/contract_watcher/header/converter/converter_test.go index 72cfa59a..9c7d6f68 100644 --- a/pkg/contract_watcher/header/converter/converter_test.go +++ b/pkg/contract_watcher/header/converter/converter_test.go @@ -72,11 +72,11 @@ var _ = Describe("Converter", func() { Expect(logs[0].Values["to"]).To(Equal(sender1.String())) Expect(logs[0].Values["from"]).To(Equal(sender2.String())) Expect(logs[0].Values["value"]).To(Equal(value.String())) - Expect(logs[0].Id).To(Equal(int64(232))) + Expect(logs[0].ID).To(Equal(int64(232))) Expect(logs[1].Values["to"]).To(Equal(sender2.String())) Expect(logs[1].Values["from"]).To(Equal(sender1.String())) Expect(logs[1].Values["value"]).To(Equal(value.String())) - Expect(logs[1].Id).To(Equal(int64(232))) + Expect(logs[1].ID).To(Equal(int64(232))) }) It("Keeps track of addresses it sees if they will be used for method polling", func() { diff --git a/pkg/contract_watcher/header/fetcher/fetcher.go b/pkg/contract_watcher/header/fetcher/fetcher.go index bfecf807..afa9aade 100644 --- a/pkg/contract_watcher/header/fetcher/fetcher.go +++ b/pkg/contract_watcher/header/fetcher/fetcher.go @@ -24,6 +24,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/core" ) +// Fetcher is the fetching interface type Fetcher interface { FetchLogs(contractAddresses []string, topics []common.Hash, missingHeader core.Header) ([]types.Log, error) } @@ -32,13 +33,14 @@ type fetcher struct { blockChain core.BlockChain } -func NewFetcher(blockchain core.BlockChain) *fetcher { +// NewFetcher returns a new Fetcher +func NewFetcher(blockchain core.BlockChain) Fetcher { return &fetcher{ blockChain: blockchain, } } -// Checks all topic0s, on all addresses, fetching matching logs for the given header +// FetchLogs checks all topic0s, on all addresses, fetching matching logs for the given header func (fetcher *fetcher) FetchLogs(contractAddresses []string, topic0s []common.Hash, header core.Header) ([]types.Log, error) { addresses := hexStringsToAddresses(contractAddresses) blockHash := common.HexToHash(header.Hash) diff --git a/pkg/contract_watcher/header/repository/header_repository.go b/pkg/contract_watcher/header/repository/header_repository.go index 11be42a9..4a3fd8d7 100644 --- a/pkg/contract_watcher/header/repository/header_repository.go +++ b/pkg/contract_watcher/header/repository/header_repository.go @@ -28,6 +28,7 @@ import ( const columnCacheSize = 1000 +// HeaderRepository interfaces with the header and checked_headers tables type HeaderRepository interface { AddCheckColumn(id string) error AddCheckColumns(ids []string) error @@ -45,7 +46,8 @@ type headerRepository struct { columns *lru.Cache // Cache created columns to minimize db connections } -func NewHeaderRepository(db *postgres.DB) *headerRepository { +// NewHeaderRepository returns a new HeaderRepository +func NewHeaderRepository(db *postgres.DB) HeaderRepository { ccs, _ := lru.New(columnCacheSize) return &headerRepository{ db: db, @@ -53,7 +55,7 @@ func NewHeaderRepository(db *postgres.DB) *headerRepository { } } -// Adds a checked_header column for the provided column id +// AddCheckColumn adds a checked_header column for the provided column id func (r *headerRepository) AddCheckColumn(id string) error { // Check cache to see if column already exists before querying pg _, ok := r.columns.Get(id) @@ -74,7 +76,7 @@ func (r *headerRepository) AddCheckColumn(id string) error { return nil } -// Adds a checked_header column for all of the provided column ids +// AddCheckColumns adds a checked_header column for all of the provided column ids func (r *headerRepository) AddCheckColumns(ids []string) error { var err error baseQuery := "ALTER TABLE public.checked_headers" @@ -98,7 +100,7 @@ func (r *headerRepository) AddCheckColumns(ids []string) error { return err } -// Marks the header checked for the provided column id +// MarkHeaderChecked marks the header checked for the provided column id func (r *headerRepository) MarkHeaderChecked(headerID int64, id string) error { _, err := r.db.Exec(`INSERT INTO public.checked_headers (header_id, `+id+`) VALUES ($1, $2) @@ -107,7 +109,7 @@ func (r *headerRepository) MarkHeaderChecked(headerID int64, id string) error { return err } -// Marks the header checked for all of the provided column ids +// MarkHeaderCheckedForAll marks the header checked for all of the provided column ids func (r *headerRepository) MarkHeaderCheckedForAll(headerID int64, ids []string) error { pgStr := "INSERT INTO public.checked_headers (header_id, " for _, id := range ids { @@ -126,7 +128,7 @@ func (r *headerRepository) MarkHeaderCheckedForAll(headerID int64, ids []string) return err } -// Marks all of the provided headers checked for each of the provided column ids +// MarkHeadersCheckedForAll marks all of the provided headers checked for each of the provided column ids func (r *headerRepository) MarkHeadersCheckedForAll(headers []core.Header, ids []string) error { tx, err := r.db.Beginx() if err != nil { @@ -159,7 +161,7 @@ func (r *headerRepository) MarkHeadersCheckedForAll(headers []core.Header, ids [ return err } -// Returns missing headers for the provided checked_headers column id +// MissingHeaders returns missing headers for the provided checked_headers column id func (r *headerRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64, id string) ([]core.Header, error) { var result []core.Header var query string @@ -185,7 +187,7 @@ func (r *headerRepository) MissingHeaders(startingBlockNumber, endingBlockNumber return continuousHeaders(result), err } -// Returns missing headers for all of the provided checked_headers column ids +// MissingHeadersForAll returns missing headers for all of the provided checked_headers column ids func (r *headerRepository) MissingHeadersForAll(startingBlockNumber, endingBlockNumber int64, ids []string) ([]core.Header, error) { var result []core.Header var query string @@ -213,7 +215,7 @@ func (r *headerRepository) MissingHeadersForAll(startingBlockNumber, endingBlock return continuousHeaders(result), err } -// Returns headers that have been checked for all of the provided event ids but not for the provided method ids +// MissingMethodsCheckedEventsIntersection returns headers that have been checked for all of the provided event ids but not for the provided method ids func (r *headerRepository) MissingMethodsCheckedEventsIntersection(startingBlockNumber, endingBlockNumber int64, methodIds, eventIds []string) ([]core.Header, error) { var result []core.Header var query string @@ -263,7 +265,7 @@ func continuousHeaders(headers []core.Header) []core.Header { return headers } -// Check the repositories column id cache for a value +// CheckCache checks the repositories column id cache for a value func (r *headerRepository) CheckCache(key string) (interface{}, bool) { return r.columns.Get(key) } diff --git a/pkg/contract_watcher/header/repository/header_repository_test.go b/pkg/contract_watcher/header/repository/header_repository_test.go index 327afb59..9084e40f 100644 --- a/pkg/contract_watcher/header/repository/header_repository_test.go +++ b/pkg/contract_watcher/header/repository/header_repository_test.go @@ -18,7 +18,6 @@ package repository_test import ( "fmt" - "github.com/vulcanize/vulcanizedb/pkg/core" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -26,6 +25,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/repository" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks" + "github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" ) diff --git a/pkg/contract_watcher/header/repository/repository_suite_test.go b/pkg/contract_watcher/header/repository/repository_suite_test.go index 6e8de467..87726ebd 100644 --- a/pkg/contract_watcher/header/repository/repository_suite_test.go +++ b/pkg/contract_watcher/header/repository/repository_suite_test.go @@ -17,12 +17,12 @@ package repository_test import ( - "github.com/sirupsen/logrus" "io/ioutil" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" ) func TestRepository(t *testing.T) { diff --git a/pkg/contract_watcher/header/retriever/block_retriever.go b/pkg/contract_watcher/header/retriever/block_retriever.go index 47691013..8362d483 100644 --- a/pkg/contract_watcher/header/retriever/block_retriever.go +++ b/pkg/contract_watcher/header/retriever/block_retriever.go @@ -20,7 +20,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" ) -// Block retriever is used to retrieve the first block for a given contract and the most recent block +// BlockRetriever is used to retrieve the first block for a given contract and the most recent block // It requires a vDB synced database with blocks, transactions, receipts, and logs type BlockRetriever interface { RetrieveFirstBlock() (int64, error) @@ -31,13 +31,14 @@ type blockRetriever struct { db *postgres.DB } -func NewBlockRetriever(db *postgres.DB) (r *blockRetriever) { +// NewBlockRetriever returns a new BlockRetriever +func NewBlockRetriever(db *postgres.DB) BlockRetriever { return &blockRetriever{ db: db, } } -// Retrieve block number of earliest header in repo +// RetrieveFirstBlock retrieves block number of earliest header in repo func (r *blockRetriever) RetrieveFirstBlock() (int64, error) { var firstBlock int err := r.db.Get( @@ -48,7 +49,7 @@ func (r *blockRetriever) RetrieveFirstBlock() (int64, error) { return int64(firstBlock), err } -// Retrieve block number of latest header in repo +// RetrieveMostRecentBlock retrieves block number of latest header in repo func (r *blockRetriever) RetrieveMostRecentBlock() (int64, error) { var lastBlock int err := r.db.Get( diff --git a/pkg/contract_watcher/header/retriever/retriever_suite_test.go b/pkg/contract_watcher/header/retriever/retriever_suite_test.go index 72bc1657..f6d4967e 100644 --- a/pkg/contract_watcher/header/retriever/retriever_suite_test.go +++ b/pkg/contract_watcher/header/retriever/retriever_suite_test.go @@ -17,12 +17,12 @@ package retriever_test import ( - "github.com/sirupsen/logrus" "io/ioutil" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" ) func TestRetriever(t *testing.T) { diff --git a/pkg/contract_watcher/header/transformer/transformer.go b/pkg/contract_watcher/header/transformer/transformer.go index 6c436412..afac986f 100644 --- a/pkg/contract_watcher/header/transformer/transformer.go +++ b/pkg/contract_watcher/header/transformer/transformer.go @@ -40,6 +40,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" ) +// Transformer is the top level struct for transforming watched contract data // Requires a header synced vDB (headers) and a running eth node (or infura) type Transformer struct { // Database interfaces @@ -76,7 +77,7 @@ type Transformer struct { // 3. Init // 4. Execute -// Transformer takes in config for blockchain, database, and network id +// NewTransformer takes in a contract config, blockchain, and database, and returns a new Transformer func NewTransformer(con config.ContractConfig, bc core.BlockChain, db *postgres.DB) *Transformer { return &Transformer{ @@ -92,6 +93,7 @@ func NewTransformer(con config.ContractConfig, bc core.BlockChain, db *postgres. } } +// Init initialized the Transformer // Use after creating and setting transformer // Loops over all of the addr => filter sets // Uses parser to pull event info from abi @@ -175,14 +177,14 @@ func (tr *Transformer) Init() error { // Create checked_headers columns for each event id and append to list of all event ids tr.sortedEventIds[con.Address] = make([]string, 0, len(con.Events)) for _, event := range con.Events { - eventId := strings.ToLower(event.Name + "_" + con.Address) - addColumnErr := tr.HeaderRepository.AddCheckColumn(eventId) + eventID := strings.ToLower(event.Name + "_" + con.Address) + addColumnErr := tr.HeaderRepository.AddCheckColumn(eventID) if addColumnErr != nil { return fmt.Errorf("error adding check column: %s", addColumnErr.Error()) } // Keep track of this event id; sorted and unsorted - tr.sortedEventIds[con.Address] = append(tr.sortedEventIds[con.Address], eventId) - tr.eventIds = append(tr.eventIds, eventId) + tr.sortedEventIds[con.Address] = append(tr.sortedEventIds[con.Address], eventID) + tr.eventIds = append(tr.eventIds, eventID) // Append this event sig to the filters tr.eventFilters = append(tr.eventFilters, event.Sig()) } @@ -190,12 +192,12 @@ func (tr *Transformer) Init() error { // Create checked_headers columns for each method id and append list of all method ids tr.sortedMethodIds[con.Address] = make([]string, 0, len(con.Methods)) for _, m := range con.Methods { - methodId := strings.ToLower(m.Name + "_" + con.Address) - addColumnErr := tr.HeaderRepository.AddCheckColumn(methodId) + methodID := strings.ToLower(m.Name + "_" + con.Address) + addColumnErr := tr.HeaderRepository.AddCheckColumn(methodID) if addColumnErr != nil { return fmt.Errorf("error adding check column: %s", addColumnErr.Error()) } - tr.sortedMethodIds[con.Address] = append(tr.sortedMethodIds[con.Address], methodId) + tr.sortedMethodIds[con.Address] = append(tr.sortedMethodIds[con.Address], methodID) } // Update start to the lowest block @@ -207,6 +209,7 @@ func (tr *Transformer) Init() error { return nil } +// Execute runs the transformation processes func (tr *Transformer) Execute() error { if len(tr.Contracts) == 0 { return errors.New("error: transformer has no initialized contracts") @@ -286,8 +289,8 @@ func (tr *Transformer) Execute() error { for eventName, logs := range convertedLogs { // If logs for this event are empty, mark them checked at this header and continue if len(logs) < 1 { - eventId := strings.ToLower(eventName + "_" + con.Address) - markCheckedErr := tr.HeaderRepository.MarkHeaderChecked(header.Id, eventId) + eventID := strings.ToLower(eventName + "_" + con.Address) + markCheckedErr := tr.HeaderRepository.MarkHeaderChecked(header.Id, eventID) if markCheckedErr != nil { return fmt.Errorf("error marking header checked: %s", markCheckedErr.Error()) } @@ -341,6 +344,7 @@ func (tr *Transformer) methodPolling(header core.Header, sortedMethodIds map[str return nil } +// GetConfig returns the transformers config; satisfies the transformer interface func (tr *Transformer) GetConfig() config.ContractConfig { return tr.Config } diff --git a/pkg/contract_watcher/header/transformer/transformer_suite_test.go b/pkg/contract_watcher/header/transformer/transformer_suite_test.go index 7d799896..56bcc01c 100644 --- a/pkg/contract_watcher/header/transformer/transformer_suite_test.go +++ b/pkg/contract_watcher/header/transformer/transformer_suite_test.go @@ -17,12 +17,12 @@ package transformer_test import ( - "github.com/sirupsen/logrus" "io/ioutil" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" ) func TestTransformer(t *testing.T) { diff --git a/pkg/contract_watcher/header/transformer/transformer_test.go b/pkg/contract_watcher/header/transformer/transformer_test.go index be1e2db2..bd448000 100644 --- a/pkg/contract_watcher/header/transformer/transformer_test.go +++ b/pkg/contract_watcher/header/transformer/transformer_test.go @@ -18,6 +18,7 @@ package transformer_test import ( "database/sql" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" diff --git a/pkg/contract_watcher/shared/constants/constants.go b/pkg/contract_watcher/shared/constants/constants.go index 86c6cd9a..b47d22ae 100644 --- a/pkg/contract_watcher/shared/constants/constants.go +++ b/pkg/contract_watcher/shared/constants/constants.go @@ -34,6 +34,7 @@ const ( NewOwnerEvent Event = 4 ) +// String returns the string name for an event func (e Event) String() string { strings := [...]string{ "Transfer", @@ -50,6 +51,7 @@ func (e Event) String() string { return strings[e] } +// Signature returns the keccak256 signature for an event func (e Event) Signature() string { strings := [...]string{ helpers.GenerateSignature("Transfer(address,address,uint256)"), @@ -74,10 +76,6 @@ var MarketPlaceContractAddress = "0x8e5660b4Ab70168b5a6fEeA0e0315cb49c8Cd539" var MolochContractAddress = "0x1fd169A4f5c59ACf79d0Fd5d91D1201EF1Bce9f1" var PublicResolverAddress = "0x1da022710dF5002339274AaDEe8D58218e9D6AB5" -// Contract Owner -var DaiContractOwner = "0x0000000000000000000000000000000000000000" -var TusdContractOwner = "0x9978d2d229a69b3aef93420d132ab22b44e3578f" - // Contract Abis var DaiAbiString = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"stop","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"mint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name_","type":"bytes32"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stopped","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","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":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"mint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"push","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"move","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"start","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":"guy","type":"address"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"wad","type":"uint256"}],"name":"pull","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"symbol_","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Burn","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"},{"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":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"}]` @@ -89,8 +87,8 @@ var MarketPlaceAbiString = `[{"constant":false,"inputs":[{"name":"_ownerCutPerMi var MolochAbiString = `[{"constant":true,"inputs":[],"name":"processingReward","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"memberAddress","type":"address"},{"name":"proposalIndex","type":"uint256"}],"name":"getMemberProposalVote","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentPeriod","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"members","outputs":[{"name":"delegateKey","type":"address"},{"name":"shares","type":"uint256"},{"name":"exists","type":"bool"},{"name":"highestIndexYesVote","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSharesRequested","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newDelegateKey","type":"address"}],"name":"updateDelegateKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalShares","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposalQueue","outputs":[{"name":"proposer","type":"address"},{"name":"applicant","type":"address"},{"name":"sharesRequested","type":"uint256"},{"name":"startingPeriod","type":"uint256"},{"name":"yesVotes","type":"uint256"},{"name":"noVotes","type":"uint256"},{"name":"processed","type":"bool"},{"name":"didPass","type":"bool"},{"name":"aborted","type":"bool"},{"name":"tokenTribute","type":"uint256"},{"name":"details","type":"string"},{"name":"maxTotalSharesAtYesVote","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"memberAddressByDelegateKey","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gracePeriodLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"abortWindow","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getProposalQueueLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"summoningTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"votingPeriodLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sharesToBurn","type":"uint256"}],"name":"ragequit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"proposalDeposit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"startingPeriod","type":"uint256"}],"name":"hasVotingPeriodExpired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"applicant","type":"address"},{"name":"tokenTribute","type":"uint256"},{"name":"sharesRequested","type":"uint256"},{"name":"details","type":"string"}],"name":"submitProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"proposalIndex","type":"uint256"},{"name":"uintVote","type":"uint8"}],"name":"submitVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"highestIndexYesVote","type":"uint256"}],"name":"canRagequit","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"guildBank","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dilutionBound","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"periodDuration","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"approvedToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"proposalIndex","type":"uint256"}],"name":"abort","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"proposalIndex","type":"uint256"}],"name":"processProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"summoner","type":"address"},{"name":"_approvedToken","type":"address"},{"name":"_periodDuration","type":"uint256"},{"name":"_votingPeriodLength","type":"uint256"},{"name":"_gracePeriodLength","type":"uint256"},{"name":"_abortWindow","type":"uint256"},{"name":"_proposalDeposit","type":"uint256"},{"name":"_dilutionBound","type":"uint256"},{"name":"_processingReward","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalIndex","type":"uint256"},{"indexed":true,"name":"delegateKey","type":"address"},{"indexed":true,"name":"memberAddress","type":"address"},{"indexed":true,"name":"applicant","type":"address"},{"indexed":false,"name":"tokenTribute","type":"uint256"},{"indexed":false,"name":"sharesRequested","type":"uint256"}],"name":"SubmitProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalIndex","type":"uint256"},{"indexed":true,"name":"delegateKey","type":"address"},{"indexed":true,"name":"memberAddress","type":"address"},{"indexed":false,"name":"uintVote","type":"uint8"}],"name":"SubmitVote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalIndex","type":"uint256"},{"indexed":true,"name":"applicant","type":"address"},{"indexed":true,"name":"memberAddress","type":"address"},{"indexed":false,"name":"tokenTribute","type":"uint256"},{"indexed":false,"name":"sharesRequested","type":"uint256"},{"indexed":false,"name":"didPass","type":"bool"}],"name":"ProcessProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"memberAddress","type":"address"},{"indexed":false,"name":"sharesToBurn","type":"uint256"}],"name":"Ragequit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalIndex","type":"uint256"},{"indexed":false,"name":"applicantAddress","type":"address"}],"name":"Abort","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"memberAddress","type":"address"},{"indexed":false,"name":"newDelegateKey","type":"address"}],"name":"UpdateDelegateKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"summoner","type":"address"},{"indexed":false,"name":"shares","type":"uint256"}],"name":"SummonComplete","type":"event"}]` -// Look-up table for ABI strings -var Abis = map[common.Address]string{ +// ABIs is a look-up table for ABI strings +var ABIs = map[common.Address]string{ common.HexToAddress("0x314159265dD8dbb310642f98f50C066173C1259b"): ENSAbiString, common.HexToAddress("0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E"): TusdAbiString, common.HexToAddress("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"): DaiAbiString, @@ -98,23 +96,6 @@ var Abis = map[common.Address]string{ // Filters // To add additional filter parameters, filter by other Topics e.g. for a Transfer event filter Topics[1] to filter for a specific 'from' address -var DaiERC20Filters = []filters.LogFilter{ - { - Name: TransferEvent.String(), - FromBlock: 4752008, - ToBlock: -1, - Address: DaiContractAddress, - Topics: core.Topics{TransferEvent.Signature()}, - }, - { - Name: ApprovalEvent.String(), - FromBlock: 4752008, - ToBlock: -1, - Address: DaiContractAddress, - Topics: core.Topics{ApprovalEvent.Signature()}, - }, -} - var TusdGenericFilters = []filters.LogFilter{ { Name: BurnEvent.String(), diff --git a/pkg/contract_watcher/shared/constants/interface.go b/pkg/contract_watcher/shared/constants/interface.go index 5e383f28..7063bb99 100644 --- a/pkg/contract_watcher/shared/constants/interface.go +++ b/pkg/contract_watcher/shared/constants/interface.go @@ -20,11 +20,10 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" ) -// Basic abi needed to check which interfaces are adhered to +// SupportsInterfaceABI is the basic abi needed to check which interfaces are adhered to var SupportsInterfaceABI = `[{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}]` // Individual event interfaces for constructing ABI from -var SupportsInterface = `{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}` var AddrChangeInterface = `{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"}` var ContentChangeInterface = `{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}` var NameChangeInterface = `{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"name","type":"string"}],"name":"NameChanged","type":"event"}` @@ -34,11 +33,10 @@ var TextChangeInterface = `{"anonymous":false,"inputs":[{"indexed":true,"name":" var MultihashChangeInterface = `{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes"}],"name":"MultihashChanged","type":"event"}` var ContenthashChangeInterface = `{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes"}],"name":"ContenthashChanged","type":"event"}` -var StartingBlock = int64(3648359) - // Resolver interface signatures type Interface int +// Interface enums const ( MetaSig Interface = iota AddrChangeSig @@ -51,6 +49,7 @@ const ( ContentHashChangeSig ) +// Hex returns the hex signature for an interface func (e Interface) Hex() string { strings := [...]string{ "0x01ffc9a7", @@ -71,6 +70,7 @@ func (e Interface) Hex() string { return strings[e] } +// Bytes returns the bytes signature for an interface func (e Interface) Bytes() [4]uint8 { if e < MetaSig || e > ContentHashChangeSig { return [4]byte{} @@ -86,6 +86,7 @@ func (e Interface) Bytes() [4]uint8 { return byArray } +// EventSig returns the event signature for an interface func (e Interface) EventSig() string { strings := [...]string{ "", @@ -106,6 +107,7 @@ func (e Interface) EventSig() string { return strings[e] } +// MethodSig returns the method signature for an interface func (e Interface) MethodSig() string { strings := [...]string{ "supportsInterface(bytes4)", diff --git a/pkg/contract_watcher/shared/contract/contract.go b/pkg/contract_watcher/shared/contract/contract.go index 6b3eb0ff..1bb5ce3d 100644 --- a/pkg/contract_watcher/shared/contract/contract.go +++ b/pkg/contract_watcher/shared/contract/contract.go @@ -48,6 +48,7 @@ type Contract struct { Piping bool // Whether or not to pipe method results forward as arguments to subsequent methods } +// Init initializes a contract object // If we will be calling methods that use addr, hash, or byte arrays // as arguments then we initialize maps to hold these types of values func (c Contract) Init() *Contract { @@ -66,7 +67,7 @@ func (c Contract) Init() *Contract { return &c } -// Use contract info to generate event filters - full sync contract watcher only +// GenerateFilters uses contract info to generate event filters - full sync contract watcher only func (c *Contract) GenerateFilters() error { c.Filters = map[string]filters.LogFilter{} @@ -87,7 +88,7 @@ func (c *Contract) GenerateFilters() error { return nil } -// Returns true if address is in list of arguments to +// WantedEventArg returns true if address is in list of arguments to // filter events for or if no filtering is specified func (c *Contract) WantedEventArg(arg string) bool { if c.FilterArgs == nil { @@ -101,7 +102,7 @@ func (c *Contract) WantedEventArg(arg string) bool { return false } -// Returns true if address is in list of arguments to +// WantedMethodArg returns true if address is in list of arguments to // poll methods with or if no filtering is specified func (c *Contract) WantedMethodArg(arg interface{}) bool { if c.MethodArgs == nil { @@ -121,7 +122,7 @@ func (c *Contract) WantedMethodArg(arg interface{}) bool { return false } -// Returns true if any mapping value matches filtered for address or if no filter exists +// PassesEventFilter returns true if any mapping value matches filtered for address or if no filter exists // Used to check if an event log name-value mapping should be filtered or not func (c *Contract) PassesEventFilter(args map[string]string) bool { for _, arg := range args { @@ -133,7 +134,7 @@ func (c *Contract) PassesEventFilter(args map[string]string) bool { return false } -// Add event emitted address to our list if it passes filter and method polling is on +// AddEmittedAddr adds event emitted addresses to our list if it passes filter and method polling is on func (c *Contract) AddEmittedAddr(addresses ...interface{}) { for _, addr := range addresses { if c.WantedMethodArg(addr) && c.Methods != nil { @@ -142,7 +143,7 @@ func (c *Contract) AddEmittedAddr(addresses ...interface{}) { } } -// Add event emitted hash to our list if it passes filter and method polling is on +// AddEmittedHash adds event emitted hashes to our list if it passes filter and method polling is on func (c *Contract) AddEmittedHash(hashes ...interface{}) { for _, hash := range hashes { if c.WantedMethodArg(hash) && c.Methods != nil { @@ -151,6 +152,7 @@ func (c *Contract) AddEmittedHash(hashes ...interface{}) { } } +// StringifyArg resolves a method argument type to string type func StringifyArg(arg interface{}) (str string) { switch arg.(type) { case string: diff --git a/pkg/contract_watcher/shared/fetcher/fetcher.go b/pkg/contract_watcher/shared/fetcher/fetcher.go index 7507628f..db4c6561 100644 --- a/pkg/contract_watcher/shared/fetcher/fetcher.go +++ b/pkg/contract_watcher/shared/fetcher/fetcher.go @@ -29,7 +29,7 @@ import ( // Fetcher serves as the lower level data fetcher that calls the underlying // blockchain's FetchConctractData method for a given return type -// Interface definition for a Fetcher +// FetcherInterface is the interface definition for a fetcher type FetcherInterface interface { FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error) FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error) @@ -56,14 +56,14 @@ type fetcherError struct { fetchMethod string } -// Fetcher error method +// Error method func (fe *fetcherError) Error() string { return fmt.Sprintf("Error fetching %s: %s", fe.fetchMethod, fe.err) } // Generic Fetcher methods used by Getters to call contract methods -// Method used to fetch big.Int value from contract +// FetchBigInt is the method used to fetch big.Int value from contract func (f Fetcher) FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error) { var result = new(big.Int) err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) @@ -75,7 +75,7 @@ func (f Fetcher) FetchBigInt(method, contractAbi, contractAddress string, blockN return *result, nil } -// Method used to fetch bool value from contract +// FetchBool is the method used to fetch bool value from contract func (f Fetcher) FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error) { var result = new(bool) err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) @@ -87,7 +87,7 @@ func (f Fetcher) FetchBool(method, contractAbi, contractAddress string, blockNum return *result, nil } -// Method used to fetch address value from contract +// FetchAddress is the method used to fetch address value from contract func (f Fetcher) FetchAddress(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Address, error) { var result = new(common.Address) err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) @@ -99,7 +99,7 @@ func (f Fetcher) FetchAddress(method, contractAbi, contractAddress string, block return *result, nil } -// Method used to fetch string value from contract +// FetchString is the method used to fetch string value from contract func (f Fetcher) FetchString(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (string, error) { var result = new(string) err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) @@ -111,7 +111,7 @@ func (f Fetcher) FetchString(method, contractAbi, contractAddress string, blockN return *result, nil } -// Method used to fetch hash value from contract +// FetchHash is the method used to fetch hash value from contract func (f Fetcher) FetchHash(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Hash, error) { var result = new(common.Hash) err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) diff --git a/pkg/contract_watcher/shared/getter/interface_getter.go b/pkg/contract_watcher/shared/getter/interface_getter.go index 5cf39865..bc2502b4 100644 --- a/pkg/contract_watcher/shared/getter/interface_getter.go +++ b/pkg/contract_watcher/shared/getter/interface_getter.go @@ -17,12 +17,12 @@ package getter import ( - "fmt" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/fetcher" "github.com/vulcanize/vulcanizedb/pkg/core" ) +// InterfaceGetter is used to derive the interface of a contract type InterfaceGetter interface { GetABI(resolverAddr string, blockNumber int64) string GetBlockChain() core.BlockChain @@ -32,7 +32,8 @@ type interfaceGetter struct { fetcher.Fetcher } -func NewInterfaceGetter(blockChain core.BlockChain) *interfaceGetter { +// NewInterfaceGetter returns a new InterfaceGetter +func NewInterfaceGetter(blockChain core.BlockChain) InterfaceGetter { return &interfaceGetter{ Fetcher: fetcher.Fetcher{ BlockChain: blockChain, @@ -40,19 +41,15 @@ func NewInterfaceGetter(blockChain core.BlockChain) *interfaceGetter { } } -// Used to construct a custom ABI based on the results from calling supportsInterface -func (g *interfaceGetter) GetABI(resolverAddr string, blockNumber int64) (string, error) { +// GetABI is used to construct a custom ABI based on the results from calling supportsInterface +func (g *interfaceGetter) GetABI(resolverAddr string, blockNumber int64) string { a := constants.SupportsInterfaceABI args := make([]interface{}, 1) args[0] = constants.MetaSig.Bytes() supports, err := g.getSupportsInterface(a, resolverAddr, blockNumber, args) - if err != nil { - return "", fmt.Errorf("call to getSupportsInterface failed: %v", err) + if err != nil || !supports { + return "" } - if !supports { - return "", fmt.Errorf("contract does not support interface") - } - abiStr := `[` args[0] = constants.AddrChangeSig.Bytes() supports, err = g.getSupportsInterface(a, resolverAddr, blockNumber, args) @@ -96,7 +93,7 @@ func (g *interfaceGetter) GetABI(resolverAddr string, blockNumber int64) (string } abiStr = abiStr[:len(abiStr)-1] + `]` - return abiStr, nil + return abiStr } // Use this method to check whether or not a contract supports a given method/event interface @@ -104,7 +101,7 @@ func (g *interfaceGetter) getSupportsInterface(contractAbi, contractAddress stri return g.Fetcher.FetchBool("supportsInterface", contractAbi, contractAddress, blockNumber, methodArgs) } -// Method to retrieve the Getter's blockchain +// GetBlockChain is a method to retrieve the Getter's blockchain func (g *interfaceGetter) GetBlockChain() core.BlockChain { return g.Fetcher.BlockChain } diff --git a/pkg/contract_watcher/shared/helpers/helpers.go b/pkg/contract_watcher/shared/helpers/helpers.go index 9fb8dfae..6312210f 100644 --- a/pkg/contract_watcher/shared/helpers/helpers.go +++ b/pkg/contract_watcher/shared/helpers/helpers.go @@ -27,6 +27,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/core" ) +// ConvertToLog converts a watched event to a log func ConvertToLog(watchedEvent core.WatchedEvent) types.Log { allTopics := []string{watchedEvent.Topic0, watchedEvent.Topic1, watchedEvent.Topic2, watchedEvent.Topic3} var nonNilTopics []string @@ -56,12 +57,14 @@ func createTopics(topics ...string) []common.Hash { return topicsArray } +// BigFromString creates a big.Int from a string func BigFromString(n string) *big.Int { b := new(big.Int) b.SetString(n, 10) return b } +// GenerateSignature returns the keccak256 hash hex of a string func GenerateSignature(s string) string { eventSignature := []byte(s) hash := crypto.Keccak256Hash(eventSignature) diff --git a/pkg/contract_watcher/shared/parser/parser.go b/pkg/contract_watcher/shared/parser/parser.go index a8247b52..c55c82dd 100644 --- a/pkg/contract_watcher/shared/parser/parser.go +++ b/pkg/contract_watcher/shared/parser/parser.go @@ -45,7 +45,8 @@ type parser struct { parsedAbi abi.ABI } -func NewParser(network string) *parser { +// NewParser returns a new Parser +func NewParser(network string) Parser { url := eth.GenURL(network) return &parser{ @@ -53,15 +54,17 @@ func NewParser(network string) *parser { } } +// Abi returns the parser's configured abi string func (p *parser) Abi() string { return p.abi } +// ParsedAbi returns the parser's parsed abi func (p *parser) ParsedAbi() abi.ABI { return p.parsedAbi } -// Retrieves and parses the abi string +// Parse retrieves and parses the abi string // for the given contract address func (p *parser) Parse(contractAddr string) error { // If the abi is one our locally stored abis, fetch @@ -84,7 +87,7 @@ func (p *parser) Parse(contractAddr string) error { return err } -// Loads and parses an abi from a given abi string +// ParseAbiStr loads and parses an abi from a given abi string func (p *parser) ParseAbiStr(abiStr string) error { var err error p.abi = abiStr @@ -94,14 +97,14 @@ func (p *parser) ParseAbiStr(abiStr string) error { } func (p *parser) lookUp(contractAddr string) (string, error) { - if v, ok := constants.Abis[common.HexToAddress(contractAddr)]; ok { + if v, ok := constants.ABIs[common.HexToAddress(contractAddr)]; ok { return v, nil } return "", errors.New("ABI not present in lookup table") } -// Returns only specified methods, if they meet the criteria +// GetSelectMethods returns only specified methods, if they meet the criteria // Returns as array with methods in same order they were specified // Nil or empty wanted array => no events are returned func (p *parser) GetSelectMethods(wanted []string) []types.Method { @@ -121,7 +124,7 @@ func (p *parser) GetSelectMethods(wanted []string) []types.Method { return methods } -// Returns wanted methods +// GetMethods returns wanted methods // Empty wanted array => all methods are returned // Nil wanted array => no methods are returned func (p *parser) GetMethods(wanted []string) []types.Method { @@ -139,7 +142,7 @@ func (p *parser) GetMethods(wanted []string) []types.Method { return methods } -// Returns wanted events as map of types.Events +// GetEvents returns wanted events as map of types.Events // Empty wanted array => all events are returned // Nil wanted array => no events are returned func (p *parser) GetEvents(wanted []string) map[string]types.Event { diff --git a/pkg/contract_watcher/shared/poller/poller.go b/pkg/contract_watcher/shared/poller/poller.go index b3f1c51b..38717377 100644 --- a/pkg/contract_watcher/shared/poller/poller.go +++ b/pkg/contract_watcher/shared/poller/poller.go @@ -33,6 +33,7 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" ) +// Poller is the interface for polling public contract methods type Poller interface { PollContract(con contract.Contract, lastBlock int64) error PollContractAt(con contract.Contract, blockNumber int64) error @@ -45,13 +46,15 @@ type poller struct { contract contract.Contract } -func NewPoller(blockChain core.BlockChain, db *postgres.DB, mode types.Mode) *poller { +// NewPoller returns a new Poller +func NewPoller(blockChain core.BlockChain, db *postgres.DB, mode types.Mode) Poller { return &poller{ MethodRepository: repository.NewMethodRepository(db, mode), bc: blockChain, } } +// PollContract polls a contract's public methods from the contracts starting block to specified last block func (p *poller) PollContract(con contract.Contract, lastBlock int64) error { for i := con.StartingBlock; i <= lastBlock; i++ { if err := p.PollContractAt(con, i); err != nil { @@ -62,6 +65,7 @@ func (p *poller) PollContract(con contract.Contract, lastBlock int64) error { return nil } +// PollContractAt polls a contract's public getter methods at the specified block height func (p *poller) PollContractAt(con contract.Contract, blockNumber int64) error { p.contract = con for _, m := range con.Methods { @@ -98,7 +102,7 @@ func (p *poller) pollNoArgAt(m types.Method, bn int64) error { var out interface{} err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, nil, &out, bn) if err != nil { - return errors.New(fmt.Sprintf("poller error calling 0 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err)) + return fmt.Errorf("poller error calling 0 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err) } strOut, err := stringify(out) if err != nil { @@ -112,7 +116,7 @@ func (p *poller) pollNoArgAt(m types.Method, bn int64) error { // Persist result immediately err = p.PersistResults([]types.Result{result}, m, p.contract.Address, p.contract.Name) if err != nil { - return errors.New(fmt.Sprintf("poller error persisting 0 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err)) + return fmt.Errorf("poller error persisting 0 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err) } return nil @@ -148,7 +152,7 @@ func (p *poller) pollSingleArgAt(m types.Method, bn int64) error { var out interface{} err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, in, &out, bn) if err != nil { - return errors.New(fmt.Sprintf("poller error calling 1 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err)) + return fmt.Errorf("poller error calling 1 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err) } strOut, err := stringify(out) if err != nil { @@ -164,7 +168,7 @@ func (p *poller) pollSingleArgAt(m types.Method, bn int64) error { // Persist result set as batch err := p.PersistResults(results, m, p.contract.Address, p.contract.Name) if err != nil { - return errors.New(fmt.Sprintf("poller error persisting 1 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err)) + return fmt.Errorf("poller error persisting 1 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err) } return nil @@ -212,7 +216,7 @@ func (p *poller) pollDoubleArgAt(m types.Method, bn int64) error { var out interface{} err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, in, &out, bn) if err != nil { - return errors.New(fmt.Sprintf("poller error calling 2 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err)) + return fmt.Errorf("poller error calling 2 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err) } strOut, err := stringify(out) if err != nil { @@ -228,13 +232,13 @@ func (p *poller) pollDoubleArgAt(m types.Method, bn int64) error { err := p.PersistResults(results, m, p.contract.Address, p.contract.Name) if err != nil { - return errors.New(fmt.Sprintf("poller error persisting 2 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err)) + return fmt.Errorf("poller error persisting 2 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", bn, m.Name, p.contract.Address, err) } return nil } -// This is just a wrapper around the poller blockchain's FetchContractData method +// FetchContractData is just a wrapper around the poller blockchain's FetchContractData method func (p *poller) FetchContractData(contractAbi, contractAddress, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error { return p.bc.FetchContractData(contractAbi, contractAddress, method, methodArgs, result, blockNumber) } diff --git a/pkg/contract_watcher/shared/repository/event_repository.go b/pkg/contract_watcher/shared/repository/event_repository.go index 759c3889..d6d3c601 100644 --- a/pkg/contract_watcher/shared/repository/event_repository.go +++ b/pkg/contract_watcher/shared/repository/event_repository.go @@ -35,7 +35,7 @@ const ( eventCacheSize = 1000 ) -// Event repository is used to persist event data into custom tables +// EventRepository is used to persist event data into custom tables type EventRepository interface { PersistLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error CreateEventTable(contractAddr string, event types.Event) (bool, error) @@ -51,7 +51,8 @@ type eventRepository struct { tables *lru.Cache // Cache names of recently used tables to minimize db connections } -func NewEventRepository(db *postgres.DB, mode types.Mode) *eventRepository { +// NewEventRepository returns a new EventRepository +func NewEventRepository(db *postgres.DB, mode types.Mode) EventRepository { ccs, _ := lru.New(contractCacheSize) ecs, _ := lru.New(eventCacheSize) return &eventRepository{ @@ -62,7 +63,7 @@ func NewEventRepository(db *postgres.DB, mode types.Mode) *eventRepository { } } -// Creates a schema for the contract if needed +// PersistLogs creates a schema for the contract if needed // Creates table for the watched contract event if needed // Persists converted event log data into this custom table func (r *eventRepository) PersistLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error { @@ -112,7 +113,7 @@ func (r *eventRepository) persistHeaderSyncLogs(logs []types.Log, eventInfo type // Preallocate slice of needed capacity and proceed to pack variables into it in same order they appear in string data := make([]interface{}, 0, 5+el) data = append(data, - event.Id, + event.ID, contractName, event.Raw, event.LogIndex, @@ -144,8 +145,8 @@ func (r *eventRepository) persistHeaderSyncLogs(logs []types.Log, eventInfo type } // Mark header as checked for this eventId - eventId := strings.ToLower(eventInfo.Name + "_" + contractAddr) - markCheckedErr := repository.MarkContractWatcherHeaderCheckedInTransaction(logs[0].Id, tx, eventId) // This assumes all logs are from same block + eventID := strings.ToLower(eventInfo.Name + "_" + contractAddr) + markCheckedErr := repository.MarkContractWatcherHeaderCheckedInTransaction(logs[0].ID, tx, eventID) // This assumes all logs are from same block if markCheckedErr != nil { rollbackErr := tx.Rollback() if rollbackErr != nil { @@ -171,7 +172,7 @@ func (r *eventRepository) persistFullSyncLogs(logs []types.Log, eventInfo types. data := make([]interface{}, 0, 4+el) data = append(data, - event.Id, + event.ID, contractName, event.Block, event.Tx) @@ -201,7 +202,7 @@ func (r *eventRepository) persistFullSyncLogs(logs []types.Log, eventInfo types. return tx.Commit() } -// Checks for event table and creates it if it does not already exist +// CreateEventTable checks for event table and creates it if it does not already exist // Returns true if it created a new table; returns false if table already existed func (r *eventRepository) CreateEventTable(contractAddr string, event types.Event) (bool, error) { tableID := fmt.Sprintf("%s_%s.%s_event", r.mode.String(), strings.ToLower(contractAddr), strings.ToLower(event.Name)) @@ -270,7 +271,7 @@ func (r *eventRepository) checkForTable(contractAddr string, eventName string) ( return exists, err } -// Checks for contract schema and creates it if it does not already exist +// CreateContractSchema checks for contract schema and creates it if it does not already exist // Returns true if it created a new schema; returns false if schema already existed func (r *eventRepository) CreateContractSchema(contractAddr string) (bool, error) { if contractAddr == "" { @@ -316,10 +317,12 @@ func (r *eventRepository) checkForSchema(contractAddr string) (bool, error) { return exists, err } +// CheckSchemaCache is used to query the schema name cache func (r *eventRepository) CheckSchemaCache(key string) (interface{}, bool) { return r.schemas.Get(key) } +// CheckTableCache is used to query the table name cache func (r *eventRepository) CheckTableCache(key string) (interface{}, bool) { return r.tables.Get(key) } diff --git a/pkg/contract_watcher/shared/repository/method_repository.go b/pkg/contract_watcher/shared/repository/method_repository.go index 346de5a6..c8776b67 100644 --- a/pkg/contract_watcher/shared/repository/method_repository.go +++ b/pkg/contract_watcher/shared/repository/method_repository.go @@ -30,6 +30,7 @@ import ( const methodCacheSize = 1000 +// MethodRepository is used to persist public getter method data type MethodRepository interface { PersistResults(results []types.Result, methodInfo types.Method, contractAddr, contractName string) error CreateMethodTable(contractAddr string, method types.Method) (bool, error) @@ -45,7 +46,8 @@ type methodRepository struct { tables *lru.Cache // Cache names of recently used tables to minimize db connections } -func NewMethodRepository(db *postgres.DB, mode types.Mode) *methodRepository { +// NewMethodRepository returns a new MethodRepository +func NewMethodRepository(db *postgres.DB, mode types.Mode) MethodRepository { ccs, _ := lru.New(contractCacheSize) mcs, _ := lru.New(methodCacheSize) return &methodRepository{ @@ -56,7 +58,7 @@ func NewMethodRepository(db *postgres.DB, mode types.Mode) *methodRepository { } } -// Creates a schema for the contract if needed +// PersistResults creates a schema for the contract if needed // Creates table for the contract method if needed // Persists method polling data into this custom table func (r *methodRepository) PersistResults(results []types.Result, methodInfo types.Method, contractAddr, contractName string) error { @@ -124,7 +126,7 @@ func (r *methodRepository) persistResults(results []types.Result, methodInfo typ return tx.Commit() } -// Checks for event table and creates it if it does not already exist +// CreateMethodTable checks for event table and creates it if it does not already exist func (r *methodRepository) CreateMethodTable(contractAddr string, method types.Method) (bool, error) { tableID := fmt.Sprintf("%s_%s.%s_method", r.mode.String(), strings.ToLower(contractAddr), strings.ToLower(method.Name)) @@ -177,7 +179,7 @@ func (r *methodRepository) checkForTable(contractAddr string, methodName string) return exists, err } -// Checks for contract schema and creates it if it does not already exist +// CreateContractSchema checks for contract schema and creates it if it does not already exist func (r *methodRepository) CreateContractSchema(contractAddr string) (bool, error) { if contractAddr == "" { return false, errors.New("error: no contract address specified") @@ -222,10 +224,12 @@ func (r *methodRepository) checkForSchema(contractAddr string) (bool, error) { return exists, err } +// CheckSchemaCache is used to query the schema name cache func (r *methodRepository) CheckSchemaCache(key string) (interface{}, bool) { return r.schemas.Get(key) } +// CheckTableCache is used to query the table name cache func (r *methodRepository) CheckTableCache(key string) (interface{}, bool) { return r.tables.Get(key) } diff --git a/pkg/contract_watcher/shared/repository/repository_suite_test.go b/pkg/contract_watcher/shared/repository/repository_suite_test.go index 1ffa6ded..707e1917 100644 --- a/pkg/contract_watcher/shared/repository/repository_suite_test.go +++ b/pkg/contract_watcher/shared/repository/repository_suite_test.go @@ -17,12 +17,12 @@ package repository_test import ( - "github.com/sirupsen/logrus" "io/ioutil" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" ) func TestRepository(t *testing.T) { diff --git a/pkg/contract_watcher/shared/retriever/address_retriever.go b/pkg/contract_watcher/shared/retriever/address_retriever.go index 6acdf72b..8afb2b98 100644 --- a/pkg/contract_watcher/shared/retriever/address_retriever.go +++ b/pkg/contract_watcher/shared/retriever/address_retriever.go @@ -18,17 +18,17 @@ package retriever import ( "fmt" - "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/types" "strings" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract" + "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/types" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" ) -// Address retriever is used to retrieve the addresses associated with a contract +// AddressRetriever is used to retrieve the addresses associated with a contract type AddressRetriever interface { RetrieveTokenHolderAddresses(info contract.Contract) (map[common.Address]bool, error) } @@ -38,14 +38,15 @@ type addressRetriever struct { mode types.Mode } -func NewAddressRetriever(db *postgres.DB, mode types.Mode) (r *addressRetriever) { +// NewAddressRetriever returns a new AddressRetriever +func NewAddressRetriever(db *postgres.DB, mode types.Mode) AddressRetriever { return &addressRetriever{ db: db, mode: mode, } } -// Method to retrieve list of token-holding/contract-related addresses by iterating over available events +// RetrieveTokenHolderAddresses is used to retrieve list of token-holding/contract-related addresses by iterating over available events // This generic method should work whether or not the argument/input names of the events meet the expected standard // This could be generalized to iterate over ALL events and pull out any address arguments func (r *addressRetriever) RetrieveTokenHolderAddresses(info contract.Contract) (map[common.Address]bool, error) { diff --git a/pkg/contract_watcher/shared/retriever/retriever_suite_test.go b/pkg/contract_watcher/shared/retriever/retriever_suite_test.go index aff7fab1..6056bbfc 100644 --- a/pkg/contract_watcher/shared/retriever/retriever_suite_test.go +++ b/pkg/contract_watcher/shared/retriever/retriever_suite_test.go @@ -17,12 +17,12 @@ package retriever_test import ( - "github.com/sirupsen/logrus" "io/ioutil" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" ) func TestRetriever(t *testing.T) { diff --git a/pkg/contract_watcher/shared/types/event.go b/pkg/contract_watcher/shared/types/event.go index dfca596d..d5474828 100644 --- a/pkg/contract_watcher/shared/types/event.go +++ b/pkg/contract_watcher/shared/types/event.go @@ -25,20 +25,22 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +// Event is our custom event type type Event struct { Name string Anonymous bool Fields []Field } +// Field is our custom event field type which associates a postgres type with the field type Field struct { abi.Argument // Name, Type, Indexed PgType string // Holds type used when committing data held in this field to postgres } -// Struct to hold instance of an event log data +// Log is used to hold instance of an event log data type Log struct { - Id int64 // VulcanizeIdLog for full sync and header ID for header sync contract watcher + ID int64 // VulcanizeIdLog for full sync and header ID for header sync contract watcher Values map[string]string // Map of event input names to their values // Used for full sync only @@ -51,7 +53,7 @@ type Log struct { Raw []byte // json.Unmarshalled byte array of geth/core/types.Log{} } -// Unpack abi.Event into our custom Event struct +// NewEvent unpacks abi.Event into our custom Event struct func NewEvent(e abi.Event) Event { fields := make([]Field, len(e.Inputs)) for i, input := range e.Inputs { @@ -85,6 +87,7 @@ func NewEvent(e abi.Event) Event { } } +// Sig returns the hash signature for an event func (e Event) Sig() common.Hash { types := make([]string, len(e.Fields)) diff --git a/pkg/contract_watcher/shared/types/method.go b/pkg/contract_watcher/shared/types/method.go index 7b962b17..293b3594 100644 --- a/pkg/contract_watcher/shared/types/method.go +++ b/pkg/contract_watcher/shared/types/method.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +// Method is our custom method struct type Method struct { Name string Const bool @@ -32,7 +33,7 @@ type Method struct { Return []Field } -// Struct to hold instance of result from method call with given inputs and block +// Result is used to hold instance of result from method call with given inputs and block type Result struct { Method Inputs []interface{} // Will only use addresses @@ -41,7 +42,7 @@ type Result struct { Block int64 } -// Unpack abi.Method into our custom Method struct +// NewMethod unpacks abi.Method into our custom Method struct func NewMethod(m abi.Method) Method { inputs := make([]Field, len(m.Inputs)) for i, input := range m.Inputs { @@ -99,6 +100,7 @@ func NewMethod(m abi.Method) Method { } } +// Sig returns the hash signature for the method func (m Method) Sig() common.Hash { types := make([]string, len(m.Args)) i := 0 diff --git a/pkg/contract_watcher/shared/types/mode.go b/pkg/contract_watcher/shared/types/mode.go index 07d58dc6..667e66f2 100644 --- a/pkg/contract_watcher/shared/types/mode.go +++ b/pkg/contract_watcher/shared/types/mode.go @@ -16,19 +16,21 @@ package types -import "fmt" - +// Mode is used to explicitly represent the operating mode of the transformer type Mode int +// Mode enums const ( HeaderSync Mode = iota FullSync ) +// IsValid returns true is the Mode is valid func (mode Mode) IsValid() bool { return mode >= HeaderSync && mode <= FullSync } +// String returns the string representation of the mode func (mode Mode) String() string { switch mode { case HeaderSync: @@ -39,26 +41,3 @@ func (mode Mode) String() string { return "unknown" } } - -func (mode Mode) MarshalText() ([]byte, error) { - switch mode { - case HeaderSync: - return []byte("header"), nil - case FullSync: - return []byte("full"), nil - default: - return nil, fmt.Errorf("contract watcher: unknown mode %d, want HeaderSync or FullSync", mode) - } -} - -func (mode *Mode) UnmarshalText(text []byte) error { - switch string(text) { - case "header": - *mode = HeaderSync - case "full": - *mode = FullSync - default: - return fmt.Errorf(`contract watcher: unknown mode %q, want "header" or "full"`, text) - } - return nil -} From 4fbde836d4b4c3482136f26e0532e2b6372e1a25 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Thu, 24 Oct 2019 13:12:19 -0500 Subject: [PATCH 3/4] log sql.ErrNoRows which I suspect is what is leading to the flaky test --- pkg/contract_watcher/header/transformer/transformer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/contract_watcher/header/transformer/transformer.go b/pkg/contract_watcher/header/transformer/transformer.go index afac986f..b91d98c8 100644 --- a/pkg/contract_watcher/header/transformer/transformer.go +++ b/pkg/contract_watcher/header/transformer/transformer.go @@ -128,6 +128,7 @@ func (tr *Transformer) Init() error { firstBlock, retrieveErr := tr.Retriever.RetrieveFirstBlock() if retrieveErr != nil { if retrieveErr == sql.ErrNoRows { + logrus.Error(retrieveErr) firstBlock = 0 } else { return fmt.Errorf("error retrieving first block: %s", retrieveErr.Error()) From a2d249ca9de98cad7c37ddf7682b990c43f3a53f Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Mon, 28 Oct 2019 08:48:06 -0500 Subject: [PATCH 4/4] review fixes --- libraries/shared/repository/repository.go | 27 -------- .../shared/repository/repository_test.go | 67 ------------------- .../header/transformer/transformer.go | 31 +++------ .../shared/getter/interface_getter.go | 16 +++-- .../shared/repository/event_repository.go | 12 ---- .../repository/event_repository_test.go | 5 -- 6 files changed, 19 insertions(+), 139 deletions(-) delete mode 100644 libraries/shared/repository/repository.go delete mode 100644 libraries/shared/repository/repository_test.go diff --git a/libraries/shared/repository/repository.go b/libraries/shared/repository/repository.go deleted file mode 100644 index 155e27ae..00000000 --- a/libraries/shared/repository/repository.go +++ /dev/null @@ -1,27 +0,0 @@ -// VulcanizeDB -// Copyright © 2019 Vulcanize - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package repository - -import "github.com/jmoiron/sqlx" - -func MarkContractWatcherHeaderCheckedInTransaction(headerID int64, tx *sqlx.Tx, checkedHeadersColumn string) error { - _, err := tx.Exec(`INSERT INTO public.checked_headers (header_id, `+checkedHeadersColumn+`) - VALUES ($1, $2) - ON CONFLICT (header_id) DO - UPDATE SET `+checkedHeadersColumn+` = checked_headers.`+checkedHeadersColumn+` + 1`, headerID, 1) - return err -} diff --git a/libraries/shared/repository/repository_test.go b/libraries/shared/repository/repository_test.go deleted file mode 100644 index 72a613af..00000000 --- a/libraries/shared/repository/repository_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// VulcanizeDB -// Copyright © 2019 Vulcanize - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package repository_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/vulcanize/vulcanizedb/libraries/shared/repository" - "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" - "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" - "github.com/vulcanize/vulcanizedb/pkg/fakes" - "github.com/vulcanize/vulcanizedb/test_config" -) - -var _ = Describe("", func() { - Describe("MarkContractWatcherHeaderCheckedInTransaction", func() { - var ( - checkedHeadersColumn string - db *postgres.DB - ) - - BeforeEach(func() { - db = test_config.NewTestDB(test_config.NewTestNode()) - test_config.CleanTestDB(db) - checkedHeadersColumn = "test_column_checked" - _, migrateErr := db.Exec(`ALTER TABLE public.checked_headers - ADD COLUMN ` + checkedHeadersColumn + ` integer`) - Expect(migrateErr).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - _, cleanupMigrateErr := db.Exec(`ALTER TABLE public.checked_headers DROP COLUMN ` + checkedHeadersColumn) - Expect(cleanupMigrateErr).NotTo(HaveOccurred()) - }) - - It("marks passed header as checked within a passed transaction", func() { - headerRepository := repositories.NewHeaderRepository(db) - headerID, headerErr := headerRepository.CreateOrUpdateHeader(fakes.FakeHeader) - Expect(headerErr).NotTo(HaveOccurred()) - tx, txErr := db.Beginx() - Expect(txErr).NotTo(HaveOccurred()) - - err := repository.MarkContractWatcherHeaderCheckedInTransaction(headerID, tx, checkedHeadersColumn) - Expect(err).NotTo(HaveOccurred()) - commitErr := tx.Commit() - Expect(commitErr).NotTo(HaveOccurred()) - var checkedCount int - fetchErr := db.Get(&checkedCount, `SELECT COUNT(*) FROM public.checked_headers WHERE header_id = $1`, headerID) - Expect(fetchErr).NotTo(HaveOccurred()) - Expect(checkedCount).To(Equal(1)) - }) - }) -}) diff --git a/pkg/contract_watcher/header/transformer/transformer.go b/pkg/contract_watcher/header/transformer/transformer.go index b91d98c8..f3fe0188 100644 --- a/pkg/contract_watcher/header/transformer/transformer.go +++ b/pkg/contract_watcher/header/transformer/transformer.go @@ -128,7 +128,7 @@ func (tr *Transformer) Init() error { firstBlock, retrieveErr := tr.Retriever.RetrieveFirstBlock() if retrieveErr != nil { if retrieveErr == sql.ErrNoRows { - logrus.Error(retrieveErr) + logrus.Error(fmt.Errorf("error retrieving first block: %s", retrieveErr.Error())) firstBlock = 0 } else { return fmt.Errorf("error retrieving first block: %s", retrieveErr.Error()) @@ -252,12 +252,6 @@ func (tr *Transformer) Execute() error { continue } - // Sort logs by the contract they belong to - // Start by adding every contract addr to the map - // So that if we don't have any logs for it, it is caught and the header is still marked checked for its events - for _, addr := range tr.contractAddresses { - sortedLogs[addr] = nil - } for _, log := range allLogs { addr := strings.ToLower(log.Address.Hex()) sortedLogs[addr] = append(sortedLogs[addr], log) @@ -265,20 +259,12 @@ func (tr *Transformer) Execute() error { // Process logs for each contract for conAddr, logs := range sortedLogs { - con := tr.Contracts[conAddr] - if len(logs) < 1 { - eventIds := make([]string, 0) - for eventName := range con.Events { - eventIds = append(eventIds, strings.ToLower(eventName+"_"+con.Address)) - } - markCheckedErr := tr.HeaderRepository.MarkHeaderCheckedForAll(header.Id, eventIds) - if markCheckedErr != nil { - return fmt.Errorf("error marking header checked: %s", markCheckedErr.Error()) - } + if logs == nil { logrus.Tracef("no logs found for contract %s at block %d, continuing", conAddr, header.BlockNumber) continue } // Configure converter with this contract + con := tr.Contracts[conAddr] tr.Converter.Update(con) // Convert logs into batches of log mappings (eventName => []types.Logs @@ -290,16 +276,10 @@ func (tr *Transformer) Execute() error { for eventName, logs := range convertedLogs { // If logs for this event are empty, mark them checked at this header and continue if len(logs) < 1 { - eventID := strings.ToLower(eventName + "_" + con.Address) - markCheckedErr := tr.HeaderRepository.MarkHeaderChecked(header.Id, eventID) - if markCheckedErr != nil { - return fmt.Errorf("error marking header checked: %s", markCheckedErr.Error()) - } logrus.Tracef("no logs found for event %s on contract %s at block %d, continuing", eventName, conAddr, header.BlockNumber) continue } // If logs aren't empty, persist them - // Header is marked checked in the transactions persistErr := tr.EventRepository.PersistLogs(logs, con.Events[eventName], con.Address, con.Name) if persistErr != nil { return fmt.Errorf("error persisting logs: %s", persistErr.Error()) @@ -307,6 +287,11 @@ func (tr *Transformer) Execute() error { } } + markCheckedErr := tr.HeaderRepository.MarkHeaderCheckedForAll(header.Id, tr.eventIds) + if markCheckedErr != nil { + return fmt.Errorf("error marking header checked: %s", markCheckedErr.Error()) + } + // Poll contracts at this block height pollingErr := tr.methodPolling(header, tr.sortedMethodIds) if pollingErr != nil { diff --git a/pkg/contract_watcher/shared/getter/interface_getter.go b/pkg/contract_watcher/shared/getter/interface_getter.go index bc2502b4..273940e9 100644 --- a/pkg/contract_watcher/shared/getter/interface_getter.go +++ b/pkg/contract_watcher/shared/getter/interface_getter.go @@ -17,6 +17,8 @@ package getter import ( + "fmt" + "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/fetcher" "github.com/vulcanize/vulcanizedb/pkg/core" @@ -24,7 +26,7 @@ import ( // InterfaceGetter is used to derive the interface of a contract type InterfaceGetter interface { - GetABI(resolverAddr string, blockNumber int64) string + GetABI(resolverAddr string, blockNumber int64) (string, error) GetBlockChain() core.BlockChain } @@ -42,14 +44,18 @@ func NewInterfaceGetter(blockChain core.BlockChain) InterfaceGetter { } // GetABI is used to construct a custom ABI based on the results from calling supportsInterface -func (g *interfaceGetter) GetABI(resolverAddr string, blockNumber int64) string { +func (g *interfaceGetter) GetABI(resolverAddr string, blockNumber int64) (string, error) { a := constants.SupportsInterfaceABI args := make([]interface{}, 1) args[0] = constants.MetaSig.Bytes() supports, err := g.getSupportsInterface(a, resolverAddr, blockNumber, args) - if err != nil || !supports { - return "" + if err != nil { + return "", fmt.Errorf("call to getSupportsInterface failed: %v", err) } + if !supports { + return "", fmt.Errorf("contract does not support interface") + } + abiStr := `[` args[0] = constants.AddrChangeSig.Bytes() supports, err = g.getSupportsInterface(a, resolverAddr, blockNumber, args) @@ -93,7 +99,7 @@ func (g *interfaceGetter) GetABI(resolverAddr string, blockNumber int64) string } abiStr = abiStr[:len(abiStr)-1] + `]` - return abiStr + return abiStr, nil } // Use this method to check whether or not a contract supports a given method/event interface diff --git a/pkg/contract_watcher/shared/repository/event_repository.go b/pkg/contract_watcher/shared/repository/event_repository.go index d6d3c601..ab5756df 100644 --- a/pkg/contract_watcher/shared/repository/event_repository.go +++ b/pkg/contract_watcher/shared/repository/event_repository.go @@ -24,7 +24,6 @@ import ( "github.com/hashicorp/golang-lru" "github.com/sirupsen/logrus" - "github.com/vulcanize/vulcanizedb/libraries/shared/repository" "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/types" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" ) @@ -144,17 +143,6 @@ func (r *eventRepository) persistHeaderSyncLogs(logs []types.Log, eventInfo type } } - // Mark header as checked for this eventId - eventID := strings.ToLower(eventInfo.Name + "_" + contractAddr) - markCheckedErr := repository.MarkContractWatcherHeaderCheckedInTransaction(logs[0].ID, tx, eventID) // This assumes all logs are from same block - if markCheckedErr != nil { - rollbackErr := tx.Rollback() - if rollbackErr != nil { - logrus.Warnf("error rolling back transaction while marking header checked: %s", rollbackErr.Error()) - } - return fmt.Errorf("error marking header checked: %s", markCheckedErr.Error()) - } - return tx.Commit() } diff --git a/pkg/contract_watcher/shared/repository/event_repository_test.go b/pkg/contract_watcher/shared/repository/event_repository_test.go index 40f1a783..bc61d776 100644 --- a/pkg/contract_watcher/shared/repository/event_repository_test.go +++ b/pkg/contract_watcher/shared/repository/event_repository_test.go @@ -355,11 +355,6 @@ var _ = Describe("Repository", func() { Expect(count).To(Equal(2)) }) - It("Fails if the persisted event does not have a corresponding eventID column in the checked_headers table", func() { - err = dataStore.PersistLogs(logs, event, con.Address, con.Name) - Expect(err).To(HaveOccurred()) - }) - It("Fails with empty log", func() { err = dataStore.PersistLogs([]types.Log{}, event, con.Address, con.Name) Expect(err).To(HaveOccurred())