diff --git a/cmd/contractWatcher.go b/cmd/contractWatcher.go
new file mode 100644
index 00000000..342de614
--- /dev/null
+++ b/cmd/contractWatcher.go
@@ -0,0 +1,123 @@
+// VulcanizeDB
+// Copyright © 2018 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 cmd
+
+import (
+ "fmt"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
+ "time"
+
+ log "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+
+ st "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
+ ft "github.com/vulcanize/vulcanizedb/pkg/omni/full/transformer"
+ lt "github.com/vulcanize/vulcanizedb/pkg/omni/light/transformer"
+ "github.com/vulcanize/vulcanizedb/utils"
+)
+
+// contractWatcherCmd represents the contractWatcher command
+var contractWatcherCmd = &cobra.Command{
+ Use: "contractWatcher",
+ Short: "Watches events at the provided contract address using fully synced vDB",
+ Long: `Uses input contract address and event filters to watch events
+
+Expects an ethereum node to be running
+Expects an archival node synced into vulcanizeDB
+Requires a .toml config file:
+
+ [database]
+ name = "vulcanize_public"
+ hostname = "localhost"
+ port = 5432
+
+ [client]
+ ipcPath = "https://mainnet.infura.io/J5Vd2fRtGsw0zZ0Ov3BL"
+
+ [contract]
+ network = ""
+ addresses = [
+ "contractAddress1",
+ "contractAddress2"
+ ]
+ [contract.contractAddress1]
+ abi = 'ABI for contract 1'
+ startingBlock = 982463
+ [contract.contractAddress2]
+ abi = 'ABI for contract 2'
+ events = [
+ "event1",
+ "event2"
+ ]
+ eventArgs = [
+ "arg1",
+ "arg2"
+ ]
+ methods = [
+ "method1",
+ "method2"
+ ]
+ methodArgs = [
+ "arg1",
+ "arg2"
+ ]
+ startingBlock = 4448566
+ piping = true
+`,
+ Run: func(cmd *cobra.Command, args []string) {
+ contractWatcher()
+ },
+}
+
+var (
+ mode string
+)
+
+func contractWatcher() {
+ ticker := time.NewTicker(5 * time.Second)
+ defer ticker.Stop()
+
+ blockChain := getBlockChain()
+ db := utils.LoadPostgres(databaseConfig, blockChain.Node())
+
+ var t st.GenericTransformer
+ con := config.ContractConfig{}
+ con.PrepConfig()
+ switch mode {
+ case "light":
+ t = lt.NewTransformer(con, blockChain, &db)
+ case "full":
+ t = ft.NewTransformer(con, blockChain, &db)
+ default:
+ log.Fatal("Invalid mode")
+ }
+
+ err := t.Init()
+ if err != nil {
+ log.Fatal(fmt.Sprintf("Failed to initialized transformer\r\nerr: %v\r\n", err))
+ }
+
+ for range ticker.C {
+ t.Execute()
+ }
+}
+
+func init() {
+ rootCmd.AddCommand(contractWatcherCmd)
+
+ contractWatcherCmd.Flags().StringVarP(&mode, "mode", "o", "light", "'light' or 'full' mode to work with either light synced or fully synced vDB (default is light)")
+}
diff --git a/cmd/omniWatcher.go b/cmd/omniWatcher.go
deleted file mode 100644
index a07108a9..00000000
--- a/cmd/omniWatcher.go
+++ /dev/null
@@ -1,121 +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 cmd
-
-import (
- "fmt"
- "log"
- "time"
-
- "github.com/spf13/cobra"
-
- ft "github.com/vulcanize/vulcanizedb/pkg/omni/full/transformer"
- lt "github.com/vulcanize/vulcanizedb/pkg/omni/light/transformer"
- st "github.com/vulcanize/vulcanizedb/pkg/omni/shared/transformer"
- "github.com/vulcanize/vulcanizedb/utils"
-)
-
-// omniWatcherCmd represents the omniWatcher command
-var omniWatcherCmd = &cobra.Command{
- Use: "omniWatcher",
- Short: "Watches events at the provided contract address using fully synced vDB",
- Long: `Uses input contract address and event filters to watch events
-
-Expects an ethereum node to be running
-Expects an archival node synced into vulcanizeDB
-Requires a .toml config file:
-
- [database]
- name = "vulcanize_public"
- hostname = "localhost"
- port = 5432
-
- [client]
- ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
-`,
- Run: func(cmd *cobra.Command, args []string) {
- omniWatcher()
- },
-}
-
-var (
- network string
- contractAddress string
- contractAddresses []string
- contractEvents []string
- contractMethods []string
- eventArgs []string
- methodArgs []string
- methodPiping bool
- mode string
-)
-
-func omniWatcher() {
- if contractAddress == "" && len(contractAddresses) == 0 {
- log.Fatal("Contract address required")
- }
-
- ticker := time.NewTicker(5 * time.Second)
- defer ticker.Stop()
-
- blockChain := getBlockChain()
- db := utils.LoadPostgres(databaseConfig, blockChain.Node())
-
- var t st.Transformer
- switch mode {
- case "light":
- t = lt.NewTransformer(network, blockChain, &db)
- case "full":
- t = ft.NewTransformer(network, blockChain, &db)
- default:
- log.Fatal("Invalid mode")
- }
-
- contractAddresses = append(contractAddresses, contractAddress)
- for _, addr := range contractAddresses {
- t.SetEvents(addr, contractEvents)
- t.SetMethods(addr, contractMethods)
- t.SetEventArgs(addr, eventArgs)
- t.SetMethodArgs(addr, methodArgs)
- t.SetPiping(addr, methodPiping)
- t.SetStartingBlock(addr, startingBlockNumber)
- }
-
- err := t.Init()
- if err != nil {
- log.Fatal(fmt.Sprintf("Failed to initialized transformer\r\nerr: %v\r\n", err))
- }
-
- for range ticker.C {
- t.Execute()
- }
-}
-
-func init() {
- rootCmd.AddCommand(omniWatcherCmd)
-
- omniWatcherCmd.Flags().StringVarP(&mode, "mode", "o", "light", "'light' or 'full' mode to work with either light synced or fully synced vDB (default is light)")
- omniWatcherCmd.Flags().StringVarP(&contractAddress, "contract-address", "a", "", "Single address to generate watchers for")
- omniWatcherCmd.Flags().StringArrayVarP(&contractAddresses, "contract-addresses", "l", []string{}, "list of addresses to use; warning: watcher targets the same events and methods for each address")
- omniWatcherCmd.Flags().StringArrayVarP(&contractEvents, "events", "e", []string{}, "Subset of events to watch; by default all events are watched")
- omniWatcherCmd.Flags().StringArrayVarP(&contractMethods, "methods", "m", nil, "Subset of methods to poll; by default no methods are polled")
- omniWatcherCmd.Flags().StringArrayVarP(&eventArgs, "event-args", "f", []string{}, "Argument values to filter event logs for; will only persist event logs that emit at least one of the value specified")
- omniWatcherCmd.Flags().StringArrayVarP(&methodArgs, "method-args", "g", []string{}, "Argument values to limit methods to; will only call methods with emitted values that were specified here")
- omniWatcherCmd.Flags().StringVarP(&network, "network", "n", "", `Network the contract is deployed on; options: "ropsten", "kovan", and "rinkeby"; default is mainnet"`)
- omniWatcherCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "Block to begin watching- default is first block the contract exists")
- omniWatcherCmd.Flags().BoolVarP(&methodPiping, "piping", "p", false, "Turn on method output piping: methods listed first will be polled first and their output used as input to subsequent methods")
-}
diff --git a/integration_test/omni_full_transformer_test.go b/integration_test/omni_full_transformer_test.go
index 5346584d..cdfb93ae 100644
--- a/integration_test/omni_full_transformer_test.go
+++ b/integration_test/omni_full_transformer_test.go
@@ -2,6 +2,7 @@ package integration
import (
"fmt"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
"math/rand"
"strings"
"time"
@@ -41,8 +42,7 @@ var _ = Describe("Omni full transformer", func() {
It("Initializes transformer's contract objects", func() {
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -64,9 +64,7 @@ var _ = Describe("Omni full transformer", func() {
})
It("Transforms watched contract data into custom repositories", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, nil)
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -85,9 +83,12 @@ var _ = Describe("Omni full transformer", func() {
})
It("Keeps track of contract-related addresses while transforming event data if they need to be used for later method polling", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, []string{"balanceOf"})
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -119,9 +120,12 @@ var _ = Describe("Omni full transformer", func() {
})
It("Polls given methods using generated token holder address", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, []string{"balanceOf"})
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -145,9 +149,7 @@ var _ = Describe("Omni full transformer", func() {
})
It("Fails if initialization has not been done", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, nil)
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
err = t.Execute()
Expect(err).To(HaveOccurred())
@@ -161,9 +163,7 @@ var _ = Describe("Omni full transformer", func() {
})
It("Transforms watched contract data into custom repositories", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, nil)
+ t := transformer.NewTransformer(mocks.ENSConfig, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -183,9 +183,12 @@ var _ = Describe("Omni full transformer", func() {
})
It("Keeps track of contract-related hashes while transforming event data if they need to be used for later method polling", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -210,9 +213,12 @@ var _ = Describe("Omni full transformer", func() {
})
It("Polls given methods using generated token holder address", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -235,10 +241,12 @@ var _ = Describe("Omni full transformer", func() {
})
It("It does not perist events if they do not pass the emitted arg filter", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, nil)
- t.SetEventArgs(constants.EnsContractAddress, []string{"fake_filter_value"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.EventArgs = map[string][]string{
+ ensAddr: {"fake_filter_value"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -252,10 +260,15 @@ var _ = Describe("Omni full transformer", func() {
})
It("If a method arg filter is applied, only those arguments are used in polling", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
- t.SetMethodArgs(constants.EnsContractAddress, []string{"0x0000000000000000000000000000000000000000000000000000c02aaa39b223"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.MethodArgs = map[string][]string{
+ ensAddr: {"0x0000000000000000000000000000000000000000000000000000c02aaa39b223"},
+ }
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
diff --git a/integration_test/omni_light_transformer_test.go b/integration_test/omni_light_transformer_test.go
index 5634ba14..6cc8bb93 100644
--- a/integration_test/omni_light_transformer_test.go
+++ b/integration_test/omni_light_transformer_test.go
@@ -2,6 +2,7 @@ package integration
import (
"fmt"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
"strings"
"github.com/ethereum/go-ethereum/common"
@@ -39,8 +40,7 @@ var _ = Describe("Omnit light transformer", func() {
It("Initializes transformer's contract objects", func() {
headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
@@ -70,9 +70,7 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Transforms watched contract data into custom repositories", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, nil)
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
@@ -89,9 +87,12 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Keeps track of contract-related addresses while transforming event data if they need to be used for later method polling", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, []string{"balanceOf"})
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
c, ok := t.Contracts[tusdAddr]
@@ -131,9 +132,12 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Polls given methods using generated token holder address", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, []string{"balanceOf"})
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
@@ -150,9 +154,7 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Fails if initialization has not been done", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, nil)
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
err = t.Execute()
Expect(err).To(HaveOccurred())
})
@@ -173,9 +175,7 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Transforms watched contract data into custom repositories", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, nil)
+ t := transformer.NewTransformer(mocks.ENSConfig, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
@@ -192,9 +192,12 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Keeps track of contract-related hashes while transforming event data if they need to be used for later method polling", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
c, ok := t.Contracts[ensAddr]
@@ -218,9 +221,12 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Polls given method using list of collected hashes", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
@@ -242,10 +248,12 @@ var _ = Describe("Omnit light transformer", func() {
})
It("It does not persist events if they do not pass the emitted arg filter", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, nil)
- t.SetEventArgs(constants.EnsContractAddress, []string{"fake_filter_value"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.EventArgs = map[string][]string{
+ ensAddr: {"fake_filter_value"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
@@ -257,10 +265,15 @@ var _ = Describe("Omnit light transformer", func() {
})
It("If a method arg filter is applied, only those arguments are used in polling", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
- t.SetMethodArgs(constants.EnsContractAddress, []string{"0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.MethodArgs = map[string][]string{
+ ensAddr: {"0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"},
+ }
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
@@ -302,11 +315,7 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Transforms watched contract data into custom repositories", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, nil)
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, nil)
+ t := transformer.NewTransformer(mocks.ENSandTusdConfig, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
@@ -332,11 +341,13 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Keeps track of contract-related hashes and addresses while transforming event data if they need to be used for later method polling", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, []string{"balanceOf"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSandTusdConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
ens, ok := t.Contracts[ensAddr]
@@ -376,11 +387,13 @@ var _ = Describe("Omnit light transformer", func() {
})
It("Polls given methods for each contract, using list of collected values", func() {
- t := transformer.NewTransformer("", blockChain, db)
- t.SetEvents(constants.EnsContractAddress, []string{"NewOwner"})
- t.SetMethods(constants.EnsContractAddress, []string{"owner"})
- t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
- t.SetMethods(constants.TusdContractAddress, []string{"balanceOf"})
+ var testConf config.ContractConfig
+ testConf = mocks.ENSandTusdConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
err = t.Init()
Expect(err).ToNot(HaveOccurred())
err = t.Execute()
diff --git a/pkg/config/contract.go b/pkg/config/contract.go
new file mode 100644
index 00000000..a55933ea
--- /dev/null
+++ b/pkg/config/contract.go
@@ -0,0 +1,177 @@
+// VulcanizeDB
+// Copyright © 2018 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 config
+
+import (
+ log "github.com/sirupsen/logrus"
+ "github.com/spf13/viper"
+ "strings"
+)
+
+// Config struct for generic contract transformer
+type ContractConfig struct {
+ // Name for the transformer
+ Name string
+
+ // Ethereum network name; default "" is mainnet
+ Network string
+
+ // List of contract addresses (map to ensure no duplicates)
+ Addresses map[string]bool
+
+ // Map of contract address to abi
+ // If an address has no associated abi the parser will attempt to fetch one from etherscan
+ Abis map[string]string
+
+ // Map of contract address to slice of events
+ // Used to set which addresses to watch
+ // If any events are listed in the slice only those will be watched
+ // Otherwise all events in the contract ABI are watched
+ Events map[string][]string
+
+ // Map of contract address to slice of methods
+ // If any methods are listed in the slice only those will be polled
+ // Otherwise no methods will be polled
+ Methods map[string][]string
+
+ // Map of contract address to slice of event arguments to filter for
+ // If arguments are provided then only events which emit those arguments are watched
+ // Otherwise arguments are not filtered on events
+ EventArgs map[string][]string
+
+ // Map of contract address to slice of method arguments to limit polling to
+ // If arguments are provided then only those arguments are allowed as arguments in method polling
+ // Otherwise any argument of the right type seen emitted from events at that contract will be used in method polling
+ MethodArgs map[string][]string
+
+ // Map of contract address to their starting block
+ StartingBlocks map[string]int64
+
+ // Map of contract address to whether or not to pipe method polling results forward into subsequent method calls
+ Piping map[string]bool
+}
+
+func (oc *ContractConfig) PrepConfig() {
+ addrs := viper.GetStringSlice("contract.addresses")
+ oc.Network = viper.GetString("contract.network")
+ oc.Addresses = make(map[string]bool, len(addrs))
+ oc.Abis = make(map[string]string, len(addrs))
+ oc.Methods = make(map[string][]string, len(addrs))
+ oc.EventArgs = make(map[string][]string, len(addrs))
+ oc.MethodArgs = make(map[string][]string, len(addrs))
+ oc.EventArgs = make(map[string][]string, len(addrs))
+ oc.StartingBlocks = make(map[string]int64, len(addrs))
+ oc.Piping = make(map[string]bool, len(addrs))
+ // De-dupe addresses
+ for _, addr := range addrs {
+ oc.Addresses[strings.ToLower(addr)] = true
+ }
+
+ // Iterate over addresses to pull out config info for each contract
+ for _, addr := range addrs {
+ transformer := viper.GetStringMap("contract." + addr)
+
+ // Get and check abi
+ abi, abiOK := transformer["abi"]
+ if !abiOK || abi == nil {
+ log.Fatal(addr, "transformer config is missing `abi` value")
+ }
+ abiRef, abiOK := abi.(string)
+ if !abiOK {
+ log.Fatal(addr, "transformer `events` not of type []string")
+ }
+ oc.Abis[strings.ToLower(addr)] = abiRef
+
+ // Get and check events
+ events, eventsOK := transformer["events"]
+ if !eventsOK || events == nil {
+ log.Fatal(addr, "transformer config is missing `events` value")
+ }
+ eventsRef, eventsOK := events.([]string)
+ if !eventsOK {
+ log.Fatal(addr, "transformer `events` not of type []string")
+ }
+ if eventsRef == nil {
+ eventsRef = []string{}
+ }
+ oc.Events[strings.ToLower(addr)] = eventsRef
+
+ // Get and check methods
+ methods, methodsOK := transformer["methods"]
+ if !methodsOK || methods == nil {
+ log.Fatal(addr, "transformer config is missing `methods` value")
+ }
+ methodsRef, methodsOK := methods.([]string)
+ if !methodsOK {
+ log.Fatal(addr, "transformer `methods` not of type []string")
+ }
+ if methodsRef == nil {
+ methodsRef = []string{}
+ }
+ oc.Methods[strings.ToLower(addr)] = methodsRef
+
+ // Get and check eventArgs
+ eventArgs, eventArgsOK := transformer["eventArgs"]
+ if !eventArgsOK || eventArgs == nil {
+ log.Fatal(addr, "transformer config is missing `eventArgs` value")
+ }
+ eventArgsRef, eventArgsOK := eventArgs.([]string)
+ if !eventArgsOK {
+ log.Fatal(addr, "transformer `eventArgs` not of type []string")
+ }
+ if eventArgsRef == nil {
+ eventArgsRef = []string{}
+ }
+ oc.EventArgs[strings.ToLower(addr)] = eventArgsRef
+
+ // Get and check methodArgs
+ methodArgs, methodArgsOK := transformer["methodArgs"]
+ if !methodArgsOK || methodArgs == nil {
+ log.Fatal(addr, "transformer config is missing `methodArgs` value")
+ }
+ methodArgsRef, methodArgsOK := methodArgs.([]string)
+ if !methodArgsOK {
+ log.Fatal(addr, "transformer `methodArgs` not of type []string")
+ }
+ if methodArgsRef == nil {
+ methodArgsRef = []string{}
+ }
+ oc.MethodArgs[strings.ToLower(addr)] = methodArgsRef
+
+ // Get and check startingBlock
+ start, startOK := transformer["startingBlock"]
+ if !startOK || start == nil {
+ log.Fatal(addr, "transformer config is missing `startingBlock` value")
+ }
+ startRef, startOK := start.(int64)
+ if !startOK {
+ log.Fatal(addr, "transformer `startingBlock` not of type int")
+ }
+ oc.StartingBlocks[strings.ToLower(addr)] = startRef
+
+ // Get pipping
+ pipe, pipeOK := transformer["pipping"]
+ if !pipeOK || pipe == nil {
+ log.Fatal(addr, "transformer config is missing `pipping` value")
+ }
+ pipeRef, pipeOK := pipe.(bool)
+ if !pipeOK {
+ log.Fatal(addr, "transformer `piping` not of type bool")
+ }
+ oc.Piping[strings.ToLower(addr)] = pipeRef
+ }
+}
diff --git a/pkg/omni/full/converter/converter.go b/pkg/omni/full/converter/converter.go
index 2810438a..1c615c33 100644
--- a/pkg/omni/full/converter/converter.go
+++ b/pkg/omni/full/converter/converter.go
@@ -55,10 +55,10 @@ func (c *converter) Update(info *contract.Contract) {
// Convert 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)
+ contract := bind.NewBoundContract(common.HexToAddress(c.ContractInfo.Address), c.ContractInfo.ParsedAbi, nil, nil, nil)
values := make(map[string]interface{})
log := helpers.ConvertToLog(watchedEvent)
- err := boundContract.UnpackLogIntoMap(values, event.Name, log)
+ err := contract.UnpackLogIntoMap(values, event.Name, log)
if err != nil {
return nil, err
}
diff --git a/pkg/omni/full/transformer/transformer.go b/pkg/omni/full/transformer/transformer.go
index 3a37c084..6d51bdb1 100644
--- a/pkg/omni/full/transformer/transformer.go
+++ b/pkg/omni/full/transformer/transformer.go
@@ -18,8 +18,8 @@ package transformer
import (
"errors"
- "strings"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
@@ -34,7 +34,7 @@ import (
)
// Requires a fully synced vDB and a running eth node (or infura)
-type Transformer struct {
+type transformer struct {
// Database interfaces
datastore.FilterRepository // Log filters repo; accepts filters generated by Contract.GenerateFilters()
datastore.WatchedEventRepository // Watched event log views, created by the log filters
@@ -48,52 +48,25 @@ type Transformer struct {
converter.Converter // Converts watched event logs into custom log
poller.Poller // Polls methods using contract's token holder addresses and persists them using method datastore
- // Ethereum network name; default "" is mainnet
- Network string
+ // Store contract configuration information
+ Config config.ContractConfig
// Store contract info as mapping to contract address
Contracts map[string]*contract.Contract
-
- // Targeted subset of events/methods
- // Stored as map sof contract address to events/method names of interest
- WatchedEvents map[string][]string // Default/empty event list means all are watched
- WantedMethods map[string][]string // Default/empty method list means none are polled
-
- // Starting block for contracts
- ContractStart map[string]int64
-
- // Lists of addresses to filter event or method data
- // before persisting; if empty no filter is applied
- EventArgs map[string][]string
- MethodArgs map[string][]string
-
- // Whether or not to create a list of emitted address or hashes for the contract in postgres
- CreateAddrList map[string]bool
- CreateHashList map[string]bool
-
- // Method piping on/off for a contract
- Piping map[string]bool
}
// Transformer takes in config for blockchain, database, and network id
-func NewTransformer(network string, BC core.BlockChain, DB *postgres.DB) *Transformer {
- return &Transformer{
+func NewTransformer(con config.ContractConfig, BC core.BlockChain, DB *postgres.DB) *transformer {
+ return &transformer{
Poller: poller.NewPoller(BC, DB, types.FullSync),
- Parser: parser.NewParser(network),
+ Parser: parser.NewParser(con.Network),
BlockRetriever: retriever.NewBlockRetriever(DB),
Converter: converter.NewConverter(&contract.Contract{}),
Contracts: map[string]*contract.Contract{},
WatchedEventRepository: repositories.WatchedEventRepository{DB: DB},
FilterRepository: repositories.FilterRepository{DB: DB},
EventRepository: repository.NewEventRepository(DB, types.FullSync),
- WatchedEvents: map[string][]string{},
- WantedMethods: map[string][]string{},
- ContractStart: map[string]int64{},
- EventArgs: map[string][]string{},
- MethodArgs: map[string][]string{},
- CreateAddrList: map[string]bool{},
- CreateHashList: map[string]bool{},
- Piping: map[string]bool{},
+ Config: con,
}
}
@@ -101,59 +74,66 @@ func NewTransformer(network string, BC core.BlockChain, DB *postgres.DB) *Transf
// Loops over all of the addr => filter sets
// Uses parser to pull event info from abi
// Use this info to generate event filters
-func (transformer *Transformer) Init() error {
- for contractAddr, subset := range transformer.WatchedEvents {
- // Get Abi
- err := transformer.Parser.Parse(contractAddr)
- if err != nil {
- return err
+func (tr *transformer) Init() error {
+ for contractAddr := range tr.Config.Addresses {
+ // Configure Abi
+ if tr.Config.Abis[contractAddr] == "" {
+ // If no abi is given in the config, this method will try fetching from internal look-up table and etherscan
+ err := tr.Parser.Parse(contractAddr)
+ if err != nil {
+ return err
+ }
+ } else {
+ // If we have an abi from the config, load that into the parser
+ err := tr.Parser.ParseAbiStr(tr.Config.Abis[contractAddr])
+ if err != nil {
+ return err
+ }
}
// Get first block and most recent block number in the header repo
- firstBlock, err := transformer.BlockRetriever.RetrieveFirstBlock(contractAddr)
+ firstBlock, err := tr.BlockRetriever.RetrieveFirstBlock(contractAddr)
if err != nil {
return err
}
- lastBlock, err := transformer.BlockRetriever.RetrieveMostRecentBlock()
+ lastBlock, err := tr.BlockRetriever.RetrieveMostRecentBlock()
if err != nil {
return err
}
// Set to specified range if it falls within the bounds
- if firstBlock < transformer.ContractStart[contractAddr] {
- firstBlock = transformer.ContractStart[contractAddr]
+ if firstBlock < tr.Config.StartingBlocks[contractAddr] {
+ firstBlock = tr.Config.StartingBlocks[contractAddr]
}
// Get contract name if it has one
var name = new(string)
- transformer.Poller.FetchContractData(transformer.Abi(), contractAddr, "name", nil, name, lastBlock)
+ tr.FetchContractData(tr.Abi(), contractAddr, "name", nil, &name, lastBlock)
// Remove any potential accidental duplicate inputs in arg filter values
eventArgs := map[string]bool{}
- for _, arg := range transformer.EventArgs[contractAddr] {
+ for _, arg := range tr.Config.EventArgs[contractAddr] {
eventArgs[arg] = true
}
methodArgs := map[string]bool{}
- for _, arg := range transformer.MethodArgs[contractAddr] {
+ for _, arg := range tr.Config.MethodArgs[contractAddr] {
methodArgs[arg] = true
}
// Aggregate info into contract object
info := contract.Contract{
- Name: *name,
- Network: transformer.Network,
- Address: contractAddr,
- Abi: transformer.Parser.Abi(),
- ParsedAbi: transformer.Parser.ParsedAbi(),
- StartingBlock: firstBlock,
- LastBlock: lastBlock,
- Events: transformer.Parser.GetEvents(subset),
- Methods: transformer.Parser.GetSelectMethods(transformer.WantedMethods[contractAddr]),
- FilterArgs: eventArgs,
- MethodArgs: methodArgs,
- CreateAddrList: transformer.CreateAddrList[contractAddr],
- CreateHashList: transformer.CreateHashList[contractAddr],
- Piping: transformer.Piping[contractAddr],
+ Name: *name,
+ Network: tr.Config.Network,
+ Address: contractAddr,
+ Abi: tr.Parser.Abi(),
+ ParsedAbi: tr.Parser.ParsedAbi(),
+ StartingBlock: firstBlock,
+ LastBlock: lastBlock,
+ Events: tr.Parser.GetEvents(tr.Config.Events[contractAddr]),
+ Methods: tr.Parser.GetSelectMethods(tr.Config.Methods[contractAddr]),
+ FilterArgs: eventArgs,
+ MethodArgs: methodArgs,
+ Piping: tr.Config.Piping[contractAddr],
}.Init()
// Use info to create filters
@@ -164,14 +144,14 @@ func (transformer *Transformer) Init() error {
// Iterate over filters and push them to the repo using filter repository interface
for _, filter := range info.Filters {
- err = transformer.FilterRepository.CreateFilter(filter)
+ err = tr.CreateFilter(filter)
if err != nil {
return err
}
}
// Store contract info for further processing
- transformer.Contracts[contractAddr] = info
+ tr.Contracts[contractAddr] = info
}
return nil
@@ -182,18 +162,18 @@ func (transformer *Transformer) Init() error {
// Uses converter to convert logs into custom log type
// Persists converted logs into custuom postgres tables
// Calls selected methods, using token holder address generated during event log conversion
-func (transformer Transformer) Execute() error {
- if len(transformer.Contracts) == 0 {
+func (tr *transformer) Execute() error {
+ if len(tr.Contracts) == 0 {
return errors.New("error: transformer has no initialized contracts to work with")
}
// Iterate through all internal contracts
- for _, con := range transformer.Contracts {
+ for _, con := range tr.Contracts {
// Update converter with current contract
- transformer.Update(con)
+ tr.Update(con)
// Iterate through contract filters and get watched event logs
for eventSig, filter := range con.Filters {
- watchedEvents, err := transformer.GetWatchedEvents(filter.Name)
+ watchedEvents, err := tr.GetWatchedEvents(filter.Name)
if err != nil {
return err
}
@@ -201,7 +181,7 @@ func (transformer Transformer) Execute() error {
// Iterate over watched event logs
for _, we := range watchedEvents {
// Convert them to our custom log type
- cstm, err := transformer.Converter.Convert(*we, con.Events[eventSig])
+ cstm, err := tr.Converter.Convert(*we, con.Events[eventSig])
if err != nil {
return err
}
@@ -211,7 +191,7 @@ func (transformer Transformer) Execute() error {
// If log is not empty, immediately persist in repo
// Run this in seperate goroutine?
- err = transformer.PersistLogs([]types.Log{*cstm}, con.Events[eventSig], con.Address, con.Name)
+ err = tr.PersistLogs([]types.Log{*cstm}, con.Events[eventSig], con.Address, con.Name)
if err != nil {
return err
}
@@ -222,7 +202,7 @@ func (transformer Transformer) Execute() error {
// poller polls select contract methods
// and persists the results into custom pg tables
// Run this in seperate goroutine?
- if err := transformer.PollContract(*con); err != nil {
+ if err := tr.PollContract(*con); err != nil {
return err
}
}
@@ -230,42 +210,6 @@ func (transformer Transformer) Execute() error {
return nil
}
-// Used to set which contract addresses and which of their events to watch
-func (transformer *Transformer) SetEvents(contractAddr string, filterSet []string) {
- transformer.WatchedEvents[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set subset of account addresses to watch events for
-func (transformer *Transformer) SetEventArgs(contractAddr string, filterSet []string) {
- transformer.EventArgs[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set which contract addresses and which of their methods to call
-func (transformer *Transformer) SetMethods(contractAddr string, filterSet []string) {
- transformer.WantedMethods[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set subset of account addresses to poll methods on
-func (transformer *Transformer) SetMethodArgs(contractAddr string, filterSet []string) {
- transformer.MethodArgs[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set the block range to watch for a given address
-func (transformer *Transformer) SetStartingBlock(contractAddr string, start int64) {
- transformer.ContractStart[strings.ToLower(contractAddr)] = start
-}
-
-// Used to set whether or not to persist an account address list
-func (transformer *Transformer) SetCreateAddrList(contractAddr string, on bool) {
- transformer.CreateAddrList[strings.ToLower(contractAddr)] = on
-}
-
-// Used to set whether or not to persist an hash list
-func (transformer *Transformer) SetCreateHashList(contractAddr string, on bool) {
- transformer.CreateHashList[strings.ToLower(contractAddr)] = on
-}
-
-// Used to turn method piping on for a contract
-func (transformer *Transformer) SetPiping(contractAddr string, on bool) {
- transformer.Piping[strings.ToLower(contractAddr)] = on
+func (tr *transformer) GetConfig() config.ContractConfig {
+ return tr.Config
}
diff --git a/pkg/omni/full/transformer/transformer_test.go b/pkg/omni/full/transformer/transformer_test.go
index f25ca8f6..e302b6f5 100644
--- a/pkg/omni/full/transformer/transformer_test.go
+++ b/pkg/omni/full/transformer/transformer_test.go
@@ -17,160 +17,307 @@
package transformer_test
import (
+ "fmt"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
"math/rand"
+ "strings"
"time"
+ "github.com/ethereum/go-ethereum/common"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
- "github.com/vulcanize/vulcanizedb/pkg/fakes"
- "github.com/vulcanize/vulcanizedb/pkg/omni/full/retriever"
+ "github.com/vulcanize/vulcanizedb/pkg/core"
+ "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
+ "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/omni/full/transformer"
- "github.com/vulcanize/vulcanizedb/pkg/omni/shared/contract"
- "github.com/vulcanize/vulcanizedb/pkg/omni/shared/parser"
- "github.com/vulcanize/vulcanizedb/pkg/omni/shared/poller"
- "github.com/vulcanize/vulcanizedb/pkg/omni/shared/types"
+ "github.com/vulcanize/vulcanizedb/pkg/omni/shared/constants"
+ "github.com/vulcanize/vulcanizedb/pkg/omni/shared/helpers/test_helpers"
+ "github.com/vulcanize/vulcanizedb/pkg/omni/shared/helpers/test_helpers/mocks"
)
var _ = Describe("Transformer", func() {
- var fakeAddress = "0x1234567890abcdef"
+ var db *postgres.DB
+ var err error
+ var blockChain core.BlockChain
+ var blockRepository repositories.BlockRepository
+ var ensAddr = strings.ToLower(constants.EnsContractAddress)
+ var tusdAddr = strings.ToLower(constants.TusdContractAddress)
rand.Seed(time.Now().UnixNano())
- Describe("SetEvents", func() {
- It("Sets which events to watch from the given contract address", func() {
- watchedEvents := []string{"Transfer", "Mint"}
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetEvents(fakeAddress, watchedEvents)
- Expect(t.WatchedEvents[fakeAddress]).To(Equal(watchedEvents))
- })
+ BeforeEach(func() {
+ db, blockChain = test_helpers.SetupDBandBC()
+ blockRepository = *repositories.NewBlockRepository(db)
})
- Describe("SetEventAddrs", func() {
- It("Sets which account addresses to watch events for", func() {
- eventAddrs := []string{"test1", "test2"}
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetEventArgs(fakeAddress, eventAddrs)
- Expect(t.EventArgs[fakeAddress]).To(Equal(eventAddrs))
- })
- })
-
- Describe("SetMethods", func() {
- It("Sets which methods to poll at the given contract address", func() {
- watchedMethods := []string{"balanceOf", "totalSupply"}
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetMethods(fakeAddress, watchedMethods)
- Expect(t.WantedMethods[fakeAddress]).To(Equal(watchedMethods))
- })
- })
-
- Describe("SetMethodAddrs", func() {
- It("Sets which account addresses to poll methods against", func() {
- methodAddrs := []string{"test1", "test2"}
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetMethodArgs(fakeAddress, methodAddrs)
- Expect(t.MethodArgs[fakeAddress]).To(Equal(methodAddrs))
- })
- })
-
- Describe("SetStartingBlock", func() {
- It("Sets the block range that the contract should be watched within", func() {
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetStartingBlock(fakeAddress, 11)
- Expect(t.ContractStart[fakeAddress]).To(Equal(int64(11)))
- })
- })
-
- Describe("SetCreateAddrList", func() {
- It("Sets the block range that the contract should be watched within", func() {
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetCreateAddrList(fakeAddress, true)
- Expect(t.CreateAddrList[fakeAddress]).To(Equal(true))
- })
- })
-
- Describe("SetCreateHashList", func() {
- It("Sets the block range that the contract should be watched within", func() {
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetCreateHashList(fakeAddress, true)
- Expect(t.CreateHashList[fakeAddress]).To(Equal(true))
- })
+ AfterEach(func() {
+ test_helpers.TearDown(db)
})
Describe("Init", func() {
It("Initializes transformer's contract objects", func() {
- blockRetriever := &fakes.MockFullBlockRetriever{}
- firstBlock := int64(1)
- mostRecentBlock := int64(2)
- blockRetriever.FirstBlock = firstBlock
- blockRetriever.MostRecentBlock = mostRecentBlock
-
- parsr := &fakes.MockParser{}
- fakeAbi := "fake_abi"
- eventName := "Transfer"
- event := types.Event{}
- parsr.AbiToReturn = fakeAbi
- parsr.EventName = eventName
- parsr.Event = event
-
- pollr := &fakes.MockPoller{}
- fakeContractName := "fake_contract_name"
- pollr.ContractName = fakeContractName
-
- t := getTransformer(blockRetriever, parsr, pollr)
- t.SetEvents(fakeAddress, []string{"Transfer"})
-
- err := t.Init()
-
+ blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
+ blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+ err = t.Init()
Expect(err).ToNot(HaveOccurred())
- c, ok := t.Contracts[fakeAddress]
+ c, ok := t.Contracts[tusdAddr]
Expect(ok).To(Equal(true))
- Expect(c.StartingBlock).To(Equal(firstBlock))
- Expect(c.LastBlock).To(Equal(mostRecentBlock))
- Expect(c.Abi).To(Equal(fakeAbi))
- Expect(c.Name).To(Equal(fakeContractName))
- Expect(c.Address).To(Equal(fakeAddress))
+ Expect(c.StartingBlock).To(Equal(int64(6194633)))
+ Expect(c.LastBlock).To(Equal(int64(6194634)))
+ Expect(c.Abi).To(Equal(constants.TusdAbiString))
+ Expect(c.Name).To(Equal("TrueUSD"))
+ Expect(c.Address).To(Equal(tusdAddr))
})
It("Fails to initialize if first and most recent blocks cannot be fetched from vDB", func() {
- blockRetriever := &fakes.MockFullBlockRetriever{}
- blockRetriever.FirstBlockErr = fakes.FakeError
- t := getTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetEvents(fakeAddress, []string{"Transfer"})
-
- err := t.Init()
-
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+ err = t.Init()
Expect(err).To(HaveOccurred())
- Expect(err).To(MatchError(fakes.FakeError))
})
It("Does nothing if watched events are unset", func() {
- t := getTransformer(&fakes.MockFullBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
+ blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
+ blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Events = nil
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).To(HaveOccurred())
- err := t.Init()
-
- Expect(err).ToNot(HaveOccurred())
-
- _, ok := t.Contracts[fakeAddress]
+ _, ok := t.Contracts[tusdAddr]
Expect(ok).To(Equal(false))
})
})
-})
-func getTransformer(blockRetriever retriever.BlockRetriever, parsr parser.Parser, pollr poller.Poller) transformer.Transformer {
- return transformer.Transformer{
- FilterRepository: &fakes.MockFilterRepository{},
- Parser: parsr,
- BlockRetriever: blockRetriever,
- Poller: pollr,
- Contracts: map[string]*contract.Contract{},
- WatchedEvents: map[string][]string{},
- WantedMethods: map[string][]string{},
- ContractStart: map[string]int64{},
- EventArgs: map[string][]string{},
- MethodArgs: map[string][]string{},
- CreateAddrList: map[string]bool{},
- CreateHashList: map[string]bool{},
- }
-}
+ Describe("Execute", func() {
+ BeforeEach(func() {
+ blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
+ blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
+ })
+
+ It("Transforms watched contract data into custom repositories", func() {
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ log := test_helpers.TransferLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.transfer_event WHERE block = 6194634", tusdAddr)).StructScan(&log)
+
+ // We don't know vulcID, so compare individual fields instead of complete structures
+ Expect(log.Tx).To(Equal("0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee"))
+ Expect(log.Block).To(Equal(int64(6194634)))
+ Expect(log.From).To(Equal("0x000000000000000000000000000000000000Af21"))
+ Expect(log.To).To(Equal("0x09BbBBE21a5975cAc061D82f7b843bCE061BA391"))
+ Expect(log.Value).To(Equal("1097077688018008265106216665536940668749033598146"))
+ })
+
+ It("Keeps track of contract-related addresses while transforming event data if they need to be used for later method polling", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ c, ok := t.Contracts[tusdAddr]
+ Expect(ok).To(Equal(true))
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ b, ok := c.EmittedAddrs[common.HexToAddress("0x000000000000000000000000000000000000Af21")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = c.EmittedAddrs[common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843bCE061BA391")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843b1234567890")]
+ Expect(ok).To(Equal(false))
+
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x")]
+ Expect(ok).To(Equal(false))
+
+ _, ok = c.EmittedAddrs[""]
+ Expect(ok).To(Equal(false))
+
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x09THISE21a5IS5cFAKE1D82fAND43bCE06MADEUP")]
+ Expect(ok).To(Equal(false))
+ })
+
+ It("Polls given methods using generated token holder address", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ res := test_helpers.BalanceOf{}
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0x000000000000000000000000000000000000Af21' AND block = '6194634'", tusdAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Balance).To(Equal("0"))
+ Expect(res.TokenName).To(Equal("TrueUSD"))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843bCE061BA391' AND block = '6194634'", tusdAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Balance).To(Equal("0"))
+ Expect(res.TokenName).To(Equal("TrueUSD"))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6194634'", tusdAddr)).StructScan(&res)
+ Expect(err).To(HaveOccurred())
+ })
+
+ It("Fails if initialization has not been done", func() {
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+
+ err = t.Execute()
+ Expect(err).To(HaveOccurred())
+ })
+ })
+
+ Describe("Execute- against ENS registry contract", func() {
+ BeforeEach(func() {
+ blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock1)
+ blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock2)
+ })
+
+ It("Transforms watched contract data into custom repositories", func() {
+ t := transformer.NewTransformer(mocks.ENSConfig, blockChain, db)
+
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ log := test_helpers.NewOwnerLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.newowner_event", ensAddr)).StructScan(&log)
+
+ // We don't know vulcID, so compare individual fields instead of complete structures
+ Expect(log.Tx).To(Equal("0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654bbb"))
+ Expect(log.Block).To(Equal(int64(6194635)))
+ Expect(log.Node).To(Equal("0x0000000000000000000000000000000000000000000000000000c02aaa39b223"))
+ Expect(log.Label).To(Equal("0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391"))
+ Expect(log.Owner).To(Equal("0x000000000000000000000000000000000000Af21"))
+ })
+
+ It("Keeps track of contract-related hashes while transforming event data if they need to be used for later method polling", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ c, ok := t.Contracts[ensAddr]
+ Expect(ok).To(Equal(true))
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(len(c.EmittedHashes)).To(Equal(3))
+
+ b, ok := c.EmittedHashes[common.HexToHash("0x0000000000000000000000000000000000000000000000000000c02aaa39b223")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = c.EmittedHashes[common.HexToHash("0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ // Doesn't keep track of address since it wouldn't be used in calling the 'owner' method
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x000000000000000000000000000000000000Af21")]
+ Expect(ok).To(Equal(false))
+ })
+
+ It("Polls given methods using generated token holder address", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ res := test_helpers.Owner{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x0000000000000000000000000000000000000000000000000000c02aaa39b223' AND block = '6194636'", ensAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
+ Expect(res.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391' AND block = '6194636'", ensAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
+ Expect(res.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x9THIS110dcc444fIS242510c09bbAbe21aFAKEcacNODE82f7b843HASH61ba391' AND block = '6194636'", ensAddr)).StructScan(&res)
+ Expect(err).To(HaveOccurred())
+ })
+
+ It("It does not perist events if they do not pass the emitted arg filter", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.EventArgs = map[string][]string{
+ ensAddr: {"fake_filter_value"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ log := test_helpers.LightNewOwnerLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.newowner_event", ensAddr)).StructScan(&log)
+ Expect(err).To(HaveOccurred())
+ })
+
+ It("If a method arg filter is applied, only those arguments are used in polling", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ testConf.MethodArgs = map[string][]string{
+ ensAddr: {"0x0000000000000000000000000000000000000000000000000000c02aaa39b223"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ res := test_helpers.Owner{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x0000000000000000000000000000000000000000000000000000c02aaa39b223' AND block = '6194636'", ensAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
+ Expect(res.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391' AND block = '6194636'", ensAddr)).StructScan(&res)
+ Expect(err).To(HaveOccurred())
+ })
+ })
+})
diff --git a/pkg/omni/light/converter/converter.go b/pkg/omni/light/converter/converter.go
index b2c0d7ac..36c18f68 100644
--- a/pkg/omni/light/converter/converter.go
+++ b/pkg/omni/light/converter/converter.go
@@ -54,7 +54,7 @@ func (c *converter) Update(info *contract.Contract) {
// Convert the given watched event log into a types.Log for the given event
func (c *converter) Convert(logs []gethTypes.Log, event types.Event, headerID int64) ([]types.Log, error) {
- boundContract := bind.NewBoundContract(common.HexToAddress(c.ContractInfo.Address), c.ContractInfo.ParsedAbi, nil, nil, nil)
+ contract := bind.NewBoundContract(common.HexToAddress(c.ContractInfo.Address), c.ContractInfo.ParsedAbi, nil, nil, nil)
returnLogs := make([]types.Log, 0, len(logs))
for _, log := range logs {
values := make(map[string]interface{})
@@ -63,7 +63,7 @@ func (c *converter) Convert(logs []gethTypes.Log, event types.Event, headerID in
values[field.Name] = i
}
- err := boundContract.UnpackLogIntoMap(values, event.Name, log)
+ err := contract.UnpackLogIntoMap(values, event.Name, log)
if err != nil {
return nil, err
}
@@ -133,7 +133,7 @@ func (c *converter) Convert(logs []gethTypes.Log, event types.Event, headerID in
// Convert 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)
+ contract := bind.NewBoundContract(common.HexToAddress(c.ContractInfo.Address), c.ContractInfo.ParsedAbi, nil, nil, nil)
eventsToLogs := make(map[string][]types.Log)
for _, event := range events {
eventsToLogs[event.Name] = make([]types.Log, 0, len(logs))
@@ -142,7 +142,7 @@ func (c *converter) ConvertBatch(logs []gethTypes.Log, events map[string]types.E
// If the log is of this event type, process it as such
if event.Sig() == log.Topics[0] {
values := make(map[string]interface{})
- err := boundContract.UnpackLogIntoMap(values, event.Name, log)
+ err := contract.UnpackLogIntoMap(values, event.Name, log)
if err != nil {
return nil, err
}
diff --git a/pkg/omni/light/repository/header_repository.go b/pkg/omni/light/repository/header_repository.go
index 87d81990..d2aeac3d 100644
--- a/pkg/omni/light/repository/header_repository.go
+++ b/pkg/omni/light/repository/header_repository.go
@@ -17,8 +17,8 @@
package repository
import (
+ "database/sql"
"fmt"
- "github.com/jmoiron/sqlx"
"github.com/hashicorp/golang-lru"
@@ -125,7 +125,7 @@ func (r *headerRepository) MarkHeaderCheckedForAll(headerID int64, ids []string)
}
func (r *headerRepository) MarkHeadersCheckedForAll(headers []core.Header, ids []string) error {
- tx, err := r.db.Beginx()
+ tx, err := r.db.Begin()
if err != nil {
return err
}
@@ -250,7 +250,7 @@ func (r *headerRepository) CheckCache(key string) (interface{}, bool) {
return r.columns.Get(key)
}
-func MarkHeaderCheckedInTransaction(headerID int64, tx *sqlx.Tx, eventID string) error {
+func MarkHeaderCheckedInTransaction(headerID int64, tx *sql.Tx, eventID string) error {
_, err := tx.Exec(`INSERT INTO public.checked_headers (header_id, `+eventID+`)
VALUES ($1, $2)
ON CONFLICT (header_id) DO
diff --git a/pkg/omni/light/retriever/retriever_suite_test.go b/pkg/omni/light/retriever/retriever_suite_test.go
index 83a16269..5a254ef2 100644
--- a/pkg/omni/light/retriever/retriever_suite_test.go
+++ b/pkg/omni/light/retriever/retriever_suite_test.go
@@ -27,7 +27,7 @@ import (
func TestRetriever(t *testing.T) {
RegisterFailHandler(Fail)
- RunSpecs(t, "Light Block Number Retriever Suite Test")
+ RunSpecs(t, "Light BLock Number Retriever Suite Test")
}
var _ = BeforeSuite(func() {
diff --git a/pkg/omni/light/transformer/transformer.go b/pkg/omni/light/transformer/transformer.go
index f7738c0a..b0d4f37d 100644
--- a/pkg/omni/light/transformer/transformer.go
+++ b/pkg/omni/light/transformer/transformer.go
@@ -18,6 +18,7 @@ package transformer
import (
"errors"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
"strings"
"github.com/ethereum/go-ethereum/common"
@@ -37,7 +38,7 @@ import (
)
// Requires a light synced vDB (headers) and a running eth node (or infura)
-type Transformer struct {
+type transformer struct {
// Database interfaces
srep.EventRepository // Holds transformed watched event log data
repository.HeaderRepository // Interface for interaction with header repositories
@@ -51,32 +52,12 @@ type Transformer struct {
converter.Converter // Converts watched event logs into custom log
poller.Poller // Polls methods using arguments collected from events and persists them using a method datastore
- // Ethereum network name; default "" is mainnet
- Network string
+ // Store contract configuration information
+ Config config.ContractConfig
// Store contract info as mapping to contract address
Contracts map[string]*contract.Contract
- // Targeted subset of events/methods
- // Stored as maps of contract address to events/method names of interest
- WatchedEvents map[string][]string // Default/empty event list means all are watched
- WantedMethods map[string][]string // Default/empty method list means none are polled
-
- // Starting block number for each contract
- ContractStart map[string]int64
-
- // Lists of argument values to filter event or
- // method data with; if empty no filter is applied
- EventArgs map[string][]string
- MethodArgs map[string][]string
-
- // Whether or not to create a list of emitted address or hashes for the contract in postgres
- CreateAddrList map[string]bool
- CreateHashList map[string]bool
-
- // Method piping on/off for a contract
- Piping map[string]bool
-
// Internally configured transformer variables
contractAddresses []string // Holds all contract addresses, for batch fetching of logs
sortedEventIds map[string][]string // Map to sort event column ids by contract, for post fetch processing and persisting of logs
@@ -93,26 +74,18 @@ type Transformer struct {
// 4. Execute
// Transformer takes in config for blockchain, database, and network id
-func NewTransformer(network string, bc core.BlockChain, db *postgres.DB) *Transformer {
+func NewTransformer(con config.ContractConfig, bc core.BlockChain, db *postgres.DB) *transformer {
- return &Transformer{
+ return &transformer{
Poller: poller.NewPoller(bc, db, types.LightSync),
Fetcher: fetcher.NewFetcher(bc),
- Parser: parser.NewParser(network),
+ Parser: parser.NewParser(con.Network),
HeaderRepository: repository.NewHeaderRepository(db),
BlockRetriever: retriever.NewBlockRetriever(db),
Converter: converter.NewConverter(&contract.Contract{}),
Contracts: map[string]*contract.Contract{},
EventRepository: srep.NewEventRepository(db, types.LightSync),
- WatchedEvents: map[string][]string{},
- WantedMethods: map[string][]string{},
- ContractStart: map[string]int64{},
- EventArgs: map[string][]string{},
- MethodArgs: map[string][]string{},
- CreateAddrList: map[string]bool{},
- CreateHashList: map[string]bool{},
- Piping: map[string]bool{},
- Network: network,
+ Config: con,
}
}
@@ -120,7 +93,7 @@ func NewTransformer(network string, bc core.BlockChain, db *postgres.DB) *Transf
// Loops over all of the addr => filter sets
// Uses parser to pull event info from abi
// Use this info to generate event filters
-func (tr *Transformer) Init() error {
+func (tr *transformer) Init() error {
// Initialize internally configured transformer settings
tr.contractAddresses = make([]string, 0) // Holds all contract addresses, for batch fetching of logs
tr.sortedEventIds = make(map[string][]string) // Map to sort event column ids by contract, for post fetch processing and persisting of logs
@@ -130,11 +103,20 @@ func (tr *Transformer) Init() error {
tr.start = 100000000000 // Hold the lowest starting block and the highest ending block
// Iterate through all internal contract addresses
- for contractAddr, subset := range tr.WatchedEvents {
- // Get Abi
- err := tr.Parser.Parse(contractAddr)
- if err != nil {
- return err
+ for contractAddr := range tr.Config.Addresses {
+ // Configure Abi
+ if tr.Config.Abis[contractAddr] == "" {
+ // If no abi is given in the config, this method will try fetching from internal look-up table and etherscan
+ err := tr.Parser.Parse(contractAddr)
+ if err != nil {
+ return err
+ }
+ } else {
+ // If we have an abi from the config, load that into the parser
+ err := tr.Parser.ParseAbiStr(tr.Config.Abis[contractAddr])
+ if err != nil {
+ return err
+ }
}
// Get first block and most recent block number in the header repo
@@ -148,40 +130,38 @@ func (tr *Transformer) Init() error {
}
// Set to specified range if it falls within the bounds
- if firstBlock < tr.ContractStart[contractAddr] {
- firstBlock = tr.ContractStart[contractAddr]
+ if firstBlock < tr.Config.StartingBlocks[contractAddr] {
+ firstBlock = tr.Config.StartingBlocks[contractAddr]
}
// Get contract name if it has one
var name = new(string)
- tr.Poller.FetchContractData(tr.Abi(), contractAddr, "name", nil, name, lastBlock)
+ tr.FetchContractData(tr.Abi(), contractAddr, "name", nil, &name, lastBlock)
- // Remove any potential accidental duplicate inputs in arg filter values
+ // Remove any potential accidental duplicate inputs
eventArgs := map[string]bool{}
- for _, arg := range tr.EventArgs[contractAddr] {
+ for _, arg := range tr.Config.EventArgs[contractAddr] {
eventArgs[arg] = true
}
methodArgs := map[string]bool{}
- for _, arg := range tr.MethodArgs[contractAddr] {
+ for _, arg := range tr.Config.MethodArgs[contractAddr] {
methodArgs[arg] = true
}
// Aggregate info into contract object and store for execution
con := contract.Contract{
- Name: *name,
- Network: tr.Network,
- Address: contractAddr,
- Abi: tr.Parser.Abi(),
- ParsedAbi: tr.Parser.ParsedAbi(),
- StartingBlock: firstBlock,
- LastBlock: -1,
- Events: tr.Parser.GetEvents(subset),
- Methods: tr.Parser.GetSelectMethods(tr.WantedMethods[contractAddr]),
- FilterArgs: eventArgs,
- MethodArgs: methodArgs,
- CreateAddrList: tr.CreateAddrList[contractAddr],
- CreateHashList: tr.CreateHashList[contractAddr],
- Piping: tr.Piping[contractAddr],
+ Name: *name,
+ Network: tr.Config.Network,
+ Address: contractAddr,
+ Abi: tr.Parser.Abi(),
+ ParsedAbi: tr.Parser.ParsedAbi(),
+ StartingBlock: firstBlock,
+ LastBlock: -1,
+ Events: tr.Parser.GetEvents(tr.Config.Events[contractAddr]),
+ Methods: tr.Parser.GetSelectMethods(tr.Config.Methods[contractAddr]),
+ FilterArgs: eventArgs,
+ MethodArgs: methodArgs,
+ Piping: tr.Config.Piping[contractAddr],
}.Init()
tr.Contracts[contractAddr] = con
tr.contractAddresses = append(tr.contractAddresses, con.Address)
@@ -221,7 +201,7 @@ func (tr *Transformer) Init() error {
return nil
}
-func (tr *Transformer) Execute() error {
+func (tr *transformer) Execute() error {
if len(tr.Contracts) == 0 {
return errors.New("error: transformer has no initialized contracts")
}
@@ -311,7 +291,7 @@ func (tr *Transformer) Execute() error {
}
// Used to poll contract methods at a given header
-func (tr *Transformer) methodPolling(header core.Header, sortedMethodIds map[string][]string) error {
+func (tr *transformer) methodPolling(header core.Header, sortedMethodIds map[string][]string) error {
for _, con := range tr.Contracts {
// Skip method polling processes if no methods are specified
// Also don't try to poll methods below this contract's specified starting block
@@ -335,42 +315,6 @@ func (tr *Transformer) methodPolling(header core.Header, sortedMethodIds map[str
return nil
}
-// Used to set which contract addresses and which of their events to watch
-func (tr *Transformer) SetEvents(contractAddr string, filterSet []string) {
- tr.WatchedEvents[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set subset of account addresses to watch events for
-func (tr *Transformer) SetEventArgs(contractAddr string, filterSet []string) {
- tr.EventArgs[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set which contract addresses and which of their methods to call
-func (tr *Transformer) SetMethods(contractAddr string, filterSet []string) {
- tr.WantedMethods[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set subset of account addresses to poll methods on
-func (tr *Transformer) SetMethodArgs(contractAddr string, filterSet []string) {
- tr.MethodArgs[strings.ToLower(contractAddr)] = filterSet
-}
-
-// Used to set the block range to watch for a given address
-func (tr *Transformer) SetStartingBlock(contractAddr string, start int64) {
- tr.ContractStart[strings.ToLower(contractAddr)] = start
-}
-
-// Used to set whether or not to persist an account address list
-func (tr *Transformer) SetCreateAddrList(contractAddr string, on bool) {
- tr.CreateAddrList[strings.ToLower(contractAddr)] = on
-}
-
-// Used to set whether or not to persist an hash list
-func (tr *Transformer) SetCreateHashList(contractAddr string, on bool) {
- tr.CreateHashList[strings.ToLower(contractAddr)] = on
-}
-
-// Used to turn method piping on for a contract
-func (tr *Transformer) SetPiping(contractAddr string, on bool) {
- tr.Piping[strings.ToLower(contractAddr)] = on
+func (tr *transformer) GetConfig() config.ContractConfig {
+ return tr.Config
}
diff --git a/pkg/omni/light/transformer/transformer_test.go b/pkg/omni/light/transformer/transformer_test.go
index 31e3c194..093cd5f2 100644
--- a/pkg/omni/light/transformer/transformer_test.go
+++ b/pkg/omni/light/transformer/transformer_test.go
@@ -17,150 +17,446 @@
package transformer_test
import (
+ "fmt"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
- "github.com/vulcanize/vulcanizedb/pkg/fakes"
- "github.com/vulcanize/vulcanizedb/pkg/omni/light/retriever"
+ "github.com/vulcanize/vulcanizedb/pkg/core"
+ "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
+ "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/omni/light/transformer"
- "github.com/vulcanize/vulcanizedb/pkg/omni/shared/contract"
- "github.com/vulcanize/vulcanizedb/pkg/omni/shared/parser"
- "github.com/vulcanize/vulcanizedb/pkg/omni/shared/poller"
+ "github.com/vulcanize/vulcanizedb/pkg/omni/shared/constants"
+ "github.com/vulcanize/vulcanizedb/pkg/omni/shared/helpers/test_helpers"
+ "github.com/vulcanize/vulcanizedb/pkg/omni/shared/helpers/test_helpers/mocks"
)
var _ = Describe("Transformer", func() {
- var fakeAddress = "0x1234567890abcdef"
+ var db *postgres.DB
+ var err error
+ var blockChain core.BlockChain
+ var headerRepository repositories.HeaderRepository
+ var headerID, headerID2 int64
+ var ensAddr = strings.ToLower(constants.EnsContractAddress)
+ var tusdAddr = strings.ToLower(constants.TusdContractAddress)
- Describe("SetEvents", func() {
- It("Sets which events to watch from the given contract address", func() {
- watchedEvents := []string{"Transfer", "Mint"}
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetEvents(fakeAddress, watchedEvents)
- Expect(t.WatchedEvents[fakeAddress]).To(Equal(watchedEvents))
- })
+ BeforeEach(func() {
+ db, blockChain = test_helpers.SetupDBandBC()
+ headerRepository = repositories.NewHeaderRepository(db)
})
- Describe("SetEventAddrs", func() {
- It("Sets which account addresses to watch events for", func() {
- eventAddrs := []string{"test1", "test2"}
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetEventArgs(fakeAddress, eventAddrs)
- Expect(t.EventArgs[fakeAddress]).To(Equal(eventAddrs))
- })
- })
-
- Describe("SetMethods", func() {
- It("Sets which methods to poll at the given contract address", func() {
- watchedMethods := []string{"balanceOf", "totalSupply"}
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetMethods(fakeAddress, watchedMethods)
- Expect(t.WantedMethods[fakeAddress]).To(Equal(watchedMethods))
- })
- })
-
- Describe("SetMethodAddrs", func() {
- It("Sets which account addresses to poll methods against", func() {
- methodAddrs := []string{"test1", "test2"}
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetMethodArgs(fakeAddress, methodAddrs)
- Expect(t.MethodArgs[fakeAddress]).To(Equal(methodAddrs))
- })
- })
-
- Describe("SetStartingBlock", func() {
- It("Sets the block range that the contract should be watched within", func() {
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetStartingBlock(fakeAddress, 11)
- Expect(t.ContractStart[fakeAddress]).To(Equal(int64(11)))
- })
- })
-
- Describe("SetCreateAddrList", func() {
- It("Sets the block range that the contract should be watched within", func() {
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetCreateAddrList(fakeAddress, true)
- Expect(t.CreateAddrList[fakeAddress]).To(Equal(true))
- })
- })
-
- Describe("SetCreateHashList", func() {
- It("Sets the block range that the contract should be watched within", func() {
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetCreateHashList(fakeAddress, true)
- Expect(t.CreateHashList[fakeAddress]).To(Equal(true))
- })
+ AfterEach(func() {
+ test_helpers.TearDown(db)
})
Describe("Init", func() {
It("Initializes transformer's contract objects", func() {
- blockRetriever := &fakes.MockLightBlockRetriever{}
- firstBlock := int64(1)
- blockRetriever.FirstBlock = firstBlock
-
- parsr := &fakes.MockParser{}
- fakeAbi := "fake_abi"
- parsr.AbiToReturn = fakeAbi
-
- pollr := &fakes.MockPoller{}
- fakeContractName := "fake_contract_name"
- pollr.ContractName = fakeContractName
-
- t := getFakeTransformer(blockRetriever, parsr, pollr)
- t.SetEvents(fakeAddress, []string{"Transfer"})
-
- err := t.Init()
-
+ headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
+ headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+ err = t.Init()
Expect(err).ToNot(HaveOccurred())
- c, ok := t.Contracts[fakeAddress]
+ c, ok := t.Contracts[tusdAddr]
Expect(ok).To(Equal(true))
- Expect(c.StartingBlock).To(Equal(firstBlock))
+ Expect(c.StartingBlock).To(Equal(int64(6194632)))
Expect(c.LastBlock).To(Equal(int64(-1)))
- Expect(c.Abi).To(Equal(fakeAbi))
- Expect(c.Name).To(Equal(fakeContractName))
- Expect(c.Address).To(Equal(fakeAddress))
+ Expect(c.Abi).To(Equal(constants.TusdAbiString))
+ Expect(c.Name).To(Equal("TrueUSD"))
+ Expect(c.Address).To(Equal(tusdAddr))
})
It("Fails to initialize if first and most recent block numbers cannot be fetched from vDB headers table", func() {
- blockRetriever := &fakes.MockLightBlockRetriever{}
- blockRetriever.FirstBlockErr = fakes.FakeError
- t := getFakeTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
- t.SetEvents(fakeAddress, []string{"Transfer"})
-
- err := t.Init()
-
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+ err = t.Init()
Expect(err).To(HaveOccurred())
- Expect(err).To(MatchError(fakes.FakeError))
})
- It("Does nothing if watched events are unset", func() {
- t := getFakeTransformer(&fakes.MockLightBlockRetriever{}, &fakes.MockParser{}, &fakes.MockPoller{})
-
- err := t.Init()
-
+ It("Does nothing if nothing if no addresses are configured", func() {
+ headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
+ headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Addresses = nil
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
Expect(err).ToNot(HaveOccurred())
- _, ok := t.Contracts[fakeAddress]
+ _, ok := t.Contracts[tusdAddr]
Expect(ok).To(Equal(false))
})
})
-})
+ Describe("Execute- against TrueUSD contract", func() {
+ BeforeEach(func() {
+ header1, err := blockChain.GetHeaderByNumber(6791668)
+ Expect(err).ToNot(HaveOccurred())
+ header2, err := blockChain.GetHeaderByNumber(6791669)
+ Expect(err).ToNot(HaveOccurred())
+ header3, err := blockChain.GetHeaderByNumber(6791670)
+ Expect(err).ToNot(HaveOccurred())
+ headerRepository.CreateOrUpdateHeader(header1)
+ headerID, err = headerRepository.CreateOrUpdateHeader(header2)
+ Expect(err).ToNot(HaveOccurred())
+ headerRepository.CreateOrUpdateHeader(header3)
+ })
-func getFakeTransformer(blockRetriever retriever.BlockRetriever, parsr parser.Parser, pollr poller.Poller) transformer.Transformer {
- return transformer.Transformer{
- Parser: parsr,
- BlockRetriever: blockRetriever,
- Poller: pollr,
- HeaderRepository: &fakes.MockLightHeaderRepository{},
- Contracts: map[string]*contract.Contract{},
- WatchedEvents: map[string][]string{},
- WantedMethods: map[string][]string{},
- ContractStart: map[string]int64{},
- EventArgs: map[string][]string{},
- MethodArgs: map[string][]string{},
- CreateAddrList: map[string]bool{},
- CreateHashList: map[string]bool{},
- }
-}
+ It("Transforms watched contract data into custom repositories", func() {
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ log := test_helpers.LightTransferLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.transfer_event", tusdAddr)).StructScan(&log)
+ Expect(err).ToNot(HaveOccurred())
+ // We don't know vulcID, so compare individual fields instead of complete structures
+ Expect(log.HeaderID).To(Equal(headerID))
+ Expect(log.From).To(Equal("0x1062a747393198f70F71ec65A582423Dba7E5Ab3"))
+ Expect(log.To).To(Equal("0x2930096dB16b4A44Ecd4084EA4bd26F7EeF1AEf0"))
+ Expect(log.Value).To(Equal("9998940000000000000000"))
+ })
+
+ It("Keeps track of contract-related addresses while transforming event data if they need to be used for later method polling", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ c, ok := t.Contracts[tusdAddr]
+ Expect(ok).To(Equal(true))
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(len(c.EmittedAddrs)).To(Equal(4))
+ Expect(len(c.EmittedHashes)).To(Equal(0))
+
+ b, ok := c.EmittedAddrs[common.HexToAddress("0x1062a747393198f70F71ec65A582423Dba7E5Ab3")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = c.EmittedAddrs[common.HexToAddress("0x2930096dB16b4A44Ecd4084EA4bd26F7EeF1AEf0")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = c.EmittedAddrs[common.HexToAddress("0x571A326f5B15E16917dC17761c340c1ec5d06f6d")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = c.EmittedAddrs[common.HexToAddress("0xFBb1b73C4f0BDa4f67dcA266ce6Ef42f520fBB98")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843b1234567890")]
+ Expect(ok).To(Equal(false))
+
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x")]
+ Expect(ok).To(Equal(false))
+
+ _, ok = c.EmittedAddrs[""]
+ Expect(ok).To(Equal(false))
+
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x09THISE21a5IS5cFAKE1D82fAND43bCE06MADEUP")]
+ Expect(ok).To(Equal(false))
+ })
+
+ It("Polls given methods using generated token holder address", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.TusdConfig
+ testConf.Methods = map[string][]string{
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ res := test_helpers.BalanceOf{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x1062a747393198f70F71ec65A582423Dba7E5Ab3' AND block = '6791669'", tusdAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Balance).To(Equal("55849938025000000000000"))
+ Expect(res.TokenName).To(Equal("TrueUSD"))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6791669'", tusdAddr)).StructScan(&res)
+ Expect(err).To(HaveOccurred())
+ })
+
+ It("Fails if initialization has not been done", func() {
+ t := transformer.NewTransformer(mocks.TusdConfig, blockChain, db)
+ err = t.Execute()
+ Expect(err).To(HaveOccurred())
+ })
+ })
+
+ Describe("Execute- against ENS registry contract", func() {
+ BeforeEach(func() {
+ header1, err := blockChain.GetHeaderByNumber(6885695)
+ Expect(err).ToNot(HaveOccurred())
+ header2, err := blockChain.GetHeaderByNumber(6885696)
+ Expect(err).ToNot(HaveOccurred())
+ header3, err := blockChain.GetHeaderByNumber(6885697)
+ Expect(err).ToNot(HaveOccurred())
+ headerRepository.CreateOrUpdateHeader(header1)
+ headerID, err = headerRepository.CreateOrUpdateHeader(header2)
+ Expect(err).ToNot(HaveOccurred())
+ headerRepository.CreateOrUpdateHeader(header3)
+ })
+
+ It("Transforms watched contract data into custom repositories", func() {
+ t := transformer.NewTransformer(mocks.ENSConfig, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ log := test_helpers.LightNewOwnerLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.newowner_event", ensAddr)).StructScan(&log)
+ Expect(err).ToNot(HaveOccurred())
+ // We don't know vulcID, so compare individual fields instead of complete structures
+ Expect(log.HeaderID).To(Equal(headerID))
+ Expect(log.Node).To(Equal("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"))
+ Expect(log.Label).To(Equal("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047"))
+ Expect(log.Owner).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
+ })
+
+ It("Keeps track of contract-related hashes while transforming event data if they need to be used for later method polling", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ c, ok := t.Contracts[ensAddr]
+ Expect(ok).To(Equal(true))
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(len(c.EmittedHashes)).To(Equal(2))
+ Expect(len(c.EmittedAddrs)).To(Equal(0))
+
+ b, ok := c.EmittedHashes[common.HexToHash("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = c.EmittedHashes[common.HexToHash("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ // Doesn't keep track of address since it wouldn't be used in calling the 'owner' method
+ _, ok = c.EmittedAddrs[common.HexToAddress("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef")]
+ Expect(ok).To(Equal(false))
+ })
+
+ It("Polls given method using list of collected hashes", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ res := test_helpers.Owner{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
+ Expect(res.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
+ Expect(res.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x9THIS110dcc444fIS242510c09bbAbe21aFAKEcacNODE82f7b843HASH61ba391' AND block = '6885696'", ensAddr)).StructScan(&res)
+ Expect(err).To(HaveOccurred())
+ })
+
+ It("It does not persist events if they do not pass the emitted arg filter", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.EventArgs = map[string][]string{
+ ensAddr: {"fake_filter_value"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ log := test_helpers.LightNewOwnerLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.newowner_event", ensAddr)).StructScan(&log)
+ Expect(err).To(HaveOccurred())
+ })
+
+ It("If a method arg filter is applied, only those arguments are used in polling", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ }
+ testConf.MethodArgs = map[string][]string{
+ ensAddr: {"0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ res := test_helpers.Owner{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
+ Expect(res.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
+ Expect(err).To(HaveOccurred())
+ })
+ })
+
+ Describe("Execute- against both ENS and TrueUSD", func() {
+ BeforeEach(func() {
+ header1, err := blockChain.GetHeaderByNumber(6791668)
+ Expect(err).ToNot(HaveOccurred())
+ header2, err := blockChain.GetHeaderByNumber(6791669)
+ Expect(err).ToNot(HaveOccurred())
+ header3, err := blockChain.GetHeaderByNumber(6791670)
+ Expect(err).ToNot(HaveOccurred())
+ header4, err := blockChain.GetHeaderByNumber(6885695)
+ Expect(err).ToNot(HaveOccurred())
+ header5, err := blockChain.GetHeaderByNumber(6885696)
+ Expect(err).ToNot(HaveOccurred())
+ header6, err := blockChain.GetHeaderByNumber(6885697)
+ Expect(err).ToNot(HaveOccurred())
+ headerRepository.CreateOrUpdateHeader(header1)
+ headerID, err = headerRepository.CreateOrUpdateHeader(header2)
+ Expect(err).ToNot(HaveOccurred())
+ headerRepository.CreateOrUpdateHeader(header3)
+ headerRepository.CreateOrUpdateHeader(header4)
+ headerID2, err = headerRepository.CreateOrUpdateHeader(header5)
+ Expect(err).ToNot(HaveOccurred())
+ headerRepository.CreateOrUpdateHeader(header6)
+ })
+
+ It("Transforms watched contract data into custom repositories", func() {
+ t := transformer.NewTransformer(mocks.ENSandTusdConfig, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ newOwnerLog := test_helpers.LightNewOwnerLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.newowner_event", ensAddr)).StructScan(&newOwnerLog)
+ Expect(err).ToNot(HaveOccurred())
+ // We don't know vulcID, so compare individual fields instead of complete structures
+ Expect(newOwnerLog.HeaderID).To(Equal(headerID2))
+ Expect(newOwnerLog.Node).To(Equal("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"))
+ Expect(newOwnerLog.Label).To(Equal("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047"))
+ Expect(newOwnerLog.Owner).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
+
+ transferLog := test_helpers.LightTransferLog{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.transfer_event", tusdAddr)).StructScan(&transferLog)
+ Expect(err).ToNot(HaveOccurred())
+ // We don't know vulcID, so compare individual fields instead of complete structures
+ Expect(transferLog.HeaderID).To(Equal(headerID))
+ Expect(transferLog.From).To(Equal("0x1062a747393198f70F71ec65A582423Dba7E5Ab3"))
+ Expect(transferLog.To).To(Equal("0x2930096dB16b4A44Ecd4084EA4bd26F7EeF1AEf0"))
+ Expect(transferLog.Value).To(Equal("9998940000000000000000"))
+ })
+
+ 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 = mocks.ENSandTusdConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ ens, ok := t.Contracts[ensAddr]
+ Expect(ok).To(Equal(true))
+ tusd, ok := t.Contracts[tusdAddr]
+ Expect(ok).To(Equal(true))
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(len(ens.EmittedHashes)).To(Equal(2))
+ Expect(len(ens.EmittedAddrs)).To(Equal(0))
+ Expect(len(tusd.EmittedAddrs)).To(Equal(4))
+ Expect(len(tusd.EmittedHashes)).To(Equal(0))
+
+ b, ok := ens.EmittedHashes[common.HexToHash("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = ens.EmittedHashes[common.HexToHash("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = tusd.EmittedAddrs[common.HexToAddress("0x1062a747393198f70F71ec65A582423Dba7E5Ab3")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = tusd.EmittedAddrs[common.HexToAddress("0x2930096dB16b4A44Ecd4084EA4bd26F7EeF1AEf0")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = tusd.EmittedAddrs[common.HexToAddress("0x571A326f5B15E16917dC17761c340c1ec5d06f6d")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+
+ b, ok = tusd.EmittedAddrs[common.HexToAddress("0xFBb1b73C4f0BDa4f67dcA266ce6Ef42f520fBB98")]
+ Expect(ok).To(Equal(true))
+ Expect(b).To(Equal(true))
+ })
+
+ It("Polls given methods for each contract, using list of collected values", func() {
+ var testConf config.ContractConfig
+ testConf = mocks.ENSandTusdConfig
+ testConf.Methods = map[string][]string{
+ ensAddr: {"owner"},
+ tusdAddr: {"balanceOf"},
+ }
+ t := transformer.NewTransformer(testConf, blockChain, db)
+ err = t.Init()
+ Expect(err).ToNot(HaveOccurred())
+ err = t.Execute()
+ Expect(err).ToNot(HaveOccurred())
+
+ owner := test_helpers.Owner{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&owner)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(owner.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
+ Expect(owner.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&owner)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(owner.Address).To(Equal("0x0000000000000000000000000000000000000000"))
+ Expect(owner.TokenName).To(Equal(""))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ceMADEUPaaf4HASHc186badTHItransformers.8IS625bFAKE' AND block = '6885696'", ensAddr)).StructScan(&owner)
+ Expect(err).To(HaveOccurred())
+
+ bal := test_helpers.BalanceOf{}
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x1062a747393198f70F71ec65A582423Dba7E5Ab3' AND block = '6791669'", tusdAddr)).StructScan(&bal)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(bal.Balance).To(Equal("55849938025000000000000"))
+ Expect(bal.TokenName).To(Equal("TrueUSD"))
+
+ err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6791669'", tusdAddr)).StructScan(&bal)
+ Expect(err).To(HaveOccurred())
+ })
+ })
+})
diff --git a/pkg/omni/shared/constants/constants.go b/pkg/omni/shared/constants/constants.go
index 9ac4bd80..86012222 100644
--- a/pkg/omni/shared/constants/constants.go
+++ b/pkg/omni/shared/constants/constants.go
@@ -72,7 +72,6 @@ var TusdContractAddress = "0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E"
var EnsContractAddress = "0x314159265dD8dbb310642f98f50C066173C1259b"
var PublicResolverAddress = "0x1da022710dF5002339274AaDEe8D58218e9D6AB5"
-// TODO: Consider whether these should be moved to plugins
// Contract Owner
var DaiContractOwner = "0x0000000000000000000000000000000000000000"
var TusdContractOwner = "0x9978d2d229a69b3aef93420d132ab22b44e3578f"
diff --git a/pkg/omni/shared/contract/contract.go b/pkg/omni/shared/contract/contract.go
index 19364d91..e9f06fb2 100644
--- a/pkg/omni/shared/contract/contract.go
+++ b/pkg/omni/shared/contract/contract.go
@@ -64,12 +64,6 @@ func (c Contract) Init() *Contract {
}
}
- // If we are creating an address list in postgres
- // we initialize the map despite what method call, if any
- if c.CreateAddrList {
- c.EmittedAddrs = map[interface{}]bool{}
- }
-
return &c
}
diff --git a/pkg/omni/shared/helpers/test_helpers/database.go b/pkg/omni/shared/helpers/test_helpers/database.go
index fb12cb3c..8a1620f7 100644
--- a/pkg/omni/shared/helpers/test_helpers/database.go
+++ b/pkg/omni/shared/helpers/test_helpers/database.go
@@ -106,7 +106,6 @@ type Owner struct {
Address string `db:"returned"`
}
-// TODO: consider whether this should be moved to libraries/shared
func SetupBC() core.BlockChain {
infuraIPC := "https://mainnet.infura.io/v3/b09888c1113640cc9ab42750ce750c05"
rawRpcClient, err := rpc.Dial(infuraIPC)
@@ -114,9 +113,9 @@ func SetupBC() core.BlockChain {
rpcClient := client.NewRpcClient(rawRpcClient, infuraIPC)
ethClient := ethclient.NewClient(rawRpcClient)
blockChainClient := client.NewEthClient(ethClient)
- blockChainNode := node.MakeNode(rpcClient)
+ node := node.MakeNode(rpcClient)
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
- blockChain := geth.NewBlockChain(blockChainClient, rpcClient, blockChainNode, transactionConverter)
+ blockChain := geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
return blockChain
}
@@ -128,9 +127,9 @@ func SetupDBandBC() (*postgres.DB, core.BlockChain) {
rpcClient := client.NewRpcClient(rawRpcClient, infuraIPC)
ethClient := ethclient.NewClient(rawRpcClient)
blockChainClient := client.NewEthClient(ethClient)
- blockChainNode := node.MakeNode(rpcClient)
+ node := node.MakeNode(rpcClient)
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
- blockChain := geth.NewBlockChain(blockChainClient, rpcClient, blockChainNode, transactionConverter)
+ blockChain := geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
db, err := postgres.NewDB(config.Database{
Hostname: "localhost",
@@ -189,7 +188,6 @@ func SetupTusdContract(wantedEvents, wantedMethods []string) *contract.Contract
}.Init()
}
-// TODO: consider whether this can be moved to plugin or libraries/shared
func SetupENSRepo(vulcanizeLogId *int64, wantedEvents, wantedMethods []string) (*postgres.DB, *contract.Contract) {
db, err := postgres.NewDB(config.Database{
Hostname: "localhost",
@@ -238,7 +236,7 @@ func SetupENSContract(wantedEvents, wantedMethods []string) *contract.Contract {
}
func TearDown(db *postgres.DB) {
- tx, err := db.Beginx()
+ tx, err := db.Begin()
Expect(err).NotTo(HaveOccurred())
_, err = tx.Exec(`DELETE FROM blocks`)
diff --git a/pkg/omni/shared/helpers/test_helpers/mocks/entities.go b/pkg/omni/shared/helpers/test_helpers/mocks/entities.go
index 9a2800c5..1829448b 100644
--- a/pkg/omni/shared/helpers/test_helpers/mocks/entities.go
+++ b/pkg/omni/shared/helpers/test_helpers/mocks/entities.go
@@ -18,6 +18,8 @@ package mocks
import (
"encoding/json"
+ "github.com/vulcanize/vulcanizedb/pkg/config"
+ "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -249,3 +251,78 @@ var MockNewOwnerLog2 = types.Log{
},
Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000af21"),
}
+
+var ens = strings.ToLower(constants.EnsContractAddress)
+var tusd = strings.ToLower(constants.TusdContractAddress)
+
+var TusdConfig = config.ContractConfig{
+ Network: "",
+ Addresses: map[string]bool{
+ tusd: true,
+ },
+ Abis: map[string]string{
+ tusd: "",
+ },
+ Events: map[string][]string{
+ tusd: []string{"Transfer"},
+ },
+ Methods: map[string][]string{
+ tusd: nil,
+ },
+ MethodArgs: map[string][]string{
+ tusd: nil,
+ },
+ EventArgs: map[string][]string{
+ tusd: nil,
+ },
+}
+
+var ENSConfig = config.ContractConfig{
+ Network: "",
+ Addresses: map[string]bool{
+ ens: true,
+ },
+ Abis: map[string]string{
+ ens: "",
+ },
+ Events: map[string][]string{
+ ens: []string{"NewOwner"},
+ },
+ Methods: map[string][]string{
+ ens: nil,
+ },
+ MethodArgs: map[string][]string{
+ ens: nil,
+ },
+ EventArgs: map[string][]string{
+ ens: nil,
+ },
+}
+
+var ENSandTusdConfig = config.ContractConfig{
+ Network: "",
+ Addresses: map[string]bool{
+ ens: true,
+ tusd: true,
+ },
+ Abis: map[string]string{
+ ens: "",
+ tusd: "",
+ },
+ Events: map[string][]string{
+ ens: []string{"NewOwner"},
+ tusd: []string{"Transfer"},
+ },
+ Methods: map[string][]string{
+ ens: nil,
+ tusd: nil,
+ },
+ MethodArgs: map[string][]string{
+ ens: nil,
+ tusd: nil,
+ },
+ EventArgs: map[string][]string{
+ ens: nil,
+ tusd: nil,
+ },
+}
diff --git a/pkg/omni/shared/repository/event_repository.go b/pkg/omni/shared/repository/event_repository.go
index a08849b4..4be2a1fe 100644
--- a/pkg/omni/shared/repository/event_repository.go
+++ b/pkg/omni/shared/repository/event_repository.go
@@ -97,7 +97,7 @@ func (r *eventRepository) persistLogs(logs []types.Log, eventInfo types.Event, c
// Creates a custom postgres command to persist logs for the given event (compatible with light synced vDB)
func (r *eventRepository) persistLightSyncLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error {
- tx, err := r.db.Beginx()
+ tx, err := r.db.Begin()
if err != nil {
return err
}
@@ -151,7 +151,7 @@ func (r *eventRepository) persistLightSyncLogs(logs []types.Log, eventInfo types
// Creates a custom postgres command to persist logs for the given event (compatible with fully synced vDB)
func (r *eventRepository) persistFullSyncLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error {
- tx, err := r.db.Beginx()
+ tx, err := r.db.Begin()
if err != nil {
return err
}
diff --git a/pkg/omni/shared/repository/method_repository.go b/pkg/omni/shared/repository/method_repository.go
index e916e045..a55adbf5 100644
--- a/pkg/omni/shared/repository/method_repository.go
+++ b/pkg/omni/shared/repository/method_repository.go
@@ -77,7 +77,7 @@ func (r *methodRepository) PersistResults(results []types.Result, methodInfo typ
// Creates a custom postgres command to persist logs for the given event
func (r *methodRepository) persistResults(results []types.Result, methodInfo types.Method, contractAddr, contractName string) error {
- tx, err := r.DB.Beginx()
+ tx, err := r.DB.Begin()
if err != nil {
return err
}
diff --git a/pkg/omni/shared/transformer/interface.go b/pkg/omni/shared/transformer/interface.go
deleted file mode 100644
index ef0d331b..00000000
--- a/pkg/omni/shared/transformer/interface.go
+++ /dev/null
@@ -1,32 +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 transformer
-
-// Used to extract any/all events and a subset of method (state variable)
-// data for any contract and persists it to custom postgres tables in vDB
-type Transformer interface {
- SetEvents(contractAddr string, filterSet []string)
- SetEventArgs(contractAddr string, filterSet []string)
- SetMethods(contractAddr string, filterSet []string)
- SetMethodArgs(contractAddr string, filterSet []string)
- SetStartingBlock(contractAddr string, start int64)
- SetCreateAddrList(contractAddr string, on bool)
- SetCreateHashList(contractAddr string, on bool)
- SetPiping(contractAddr string, on bool)
- Init() error
- Execute() error
-}