2019-03-11 16:19:18 +00:00
|
|
|
// VulcanizeDB
|
2019-03-11 21:18:13 +00:00
|
|
|
// Copyright © 2019 Vulcanize
|
2019-03-11 16:19:18 +00:00
|
|
|
|
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
2019-09-24 23:44:04 +00:00
|
|
|
"strings"
|
|
|
|
|
2019-03-11 16:19:18 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/spf13/viper"
|
2019-09-24 23:44:04 +00:00
|
|
|
|
2019-03-14 21:49:27 +00:00
|
|
|
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
2019-03-11 16:19:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2019-03-14 21:49:27 +00:00
|
|
|
func (contractConfig *ContractConfig) PrepConfig() {
|
2019-03-11 16:19:18 +00:00
|
|
|
addrs := viper.GetStringSlice("contract.addresses")
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.Network = viper.GetString("contract.network")
|
|
|
|
contractConfig.Addresses = make(map[string]bool, len(addrs))
|
|
|
|
contractConfig.Abis = make(map[string]string, len(addrs))
|
|
|
|
contractConfig.Methods = make(map[string][]string, len(addrs))
|
|
|
|
contractConfig.Events = make(map[string][]string, len(addrs))
|
|
|
|
contractConfig.MethodArgs = make(map[string][]string, len(addrs))
|
|
|
|
contractConfig.EventArgs = make(map[string][]string, len(addrs))
|
|
|
|
contractConfig.StartingBlocks = make(map[string]int64, len(addrs))
|
|
|
|
contractConfig.Piping = make(map[string]bool, len(addrs))
|
2019-03-11 16:19:18 +00:00
|
|
|
// De-dupe addresses
|
|
|
|
for _, addr := range addrs {
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.Addresses[strings.ToLower(addr)] = true
|
2019-03-11 16:19:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate over addresses to pull out config info for each contract
|
|
|
|
for _, addr := range addrs {
|
|
|
|
transformer := viper.GetStringMap("contract." + addr)
|
|
|
|
|
|
|
|
// Get and check abi
|
2019-03-13 16:14:35 +00:00
|
|
|
var abi string
|
2019-03-14 21:49:27 +00:00
|
|
|
abiInterface, abiOK := transformer["abi"]
|
2019-03-11 16:19:18 +00:00
|
|
|
if !abiOK {
|
2019-03-13 16:14:35 +00:00
|
|
|
log.Warnf("contract %s not configured with an ABI, will attempt to fetch it from Etherscan\r\n", addr)
|
|
|
|
} else {
|
|
|
|
abi, abiOK = abiInterface.(string)
|
|
|
|
if !abiOK {
|
|
|
|
log.Fatal(addr, "transformer `abi` not of type []string")
|
|
|
|
}
|
2019-03-11 16:19:18 +00:00
|
|
|
}
|
2019-03-14 21:49:27 +00:00
|
|
|
if abi != "" {
|
|
|
|
if _, abiErr := geth.ParseAbi(abi); abiErr != nil {
|
|
|
|
log.Fatal(addr, "transformer `abi` not valid JSON")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
contractConfig.Abis[strings.ToLower(addr)] = abi
|
2019-03-11 16:19:18 +00:00
|
|
|
|
|
|
|
// Get and check events
|
2019-03-13 16:14:35 +00:00
|
|
|
events := make([]string, 0)
|
2019-03-14 21:49:27 +00:00
|
|
|
eventsInterface, eventsOK := transformer["events"]
|
2019-03-11 16:19:18 +00:00
|
|
|
if !eventsOK {
|
2019-03-13 16:14:35 +00:00
|
|
|
log.Warnf("contract %s not configured with a list of events to watch, will watch all events\r\n", addr)
|
|
|
|
events = []string{}
|
|
|
|
} else {
|
|
|
|
eventsI, eventsOK := eventsInterface.([]interface{})
|
|
|
|
if !eventsOK {
|
|
|
|
log.Fatal(addr, "transformer `events` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
for _, strI := range eventsI {
|
|
|
|
str, strOK := strI.(string)
|
|
|
|
if !strOK {
|
|
|
|
log.Fatal(addr, "transformer `events` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
events = append(events, str)
|
|
|
|
}
|
|
|
|
}
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.Events[strings.ToLower(addr)] = events
|
2019-03-11 16:19:18 +00:00
|
|
|
|
|
|
|
// Get and check methods
|
2019-03-13 16:14:35 +00:00
|
|
|
methods := make([]string, 0)
|
2019-03-14 21:49:27 +00:00
|
|
|
methodsInterface, methodsOK := transformer["methods"]
|
2019-03-11 16:19:18 +00:00
|
|
|
if !methodsOK {
|
2019-03-13 16:14:35 +00:00
|
|
|
log.Warnf("contract %s not configured with a list of methods to poll, will not poll any methods\r\n", addr)
|
|
|
|
methods = []string{}
|
|
|
|
} else {
|
|
|
|
methodsI, methodsOK := methodsInterface.([]interface{})
|
|
|
|
if !methodsOK {
|
|
|
|
log.Fatal(addr, "transformer `methods` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
for _, strI := range methodsI {
|
|
|
|
str, strOK := strI.(string)
|
|
|
|
if !strOK {
|
|
|
|
log.Fatal(addr, "transformer `methods` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
methods = append(methods, str)
|
|
|
|
}
|
|
|
|
}
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.Methods[strings.ToLower(addr)] = methods
|
2019-03-11 16:19:18 +00:00
|
|
|
|
|
|
|
// Get and check eventArgs
|
2019-03-13 16:14:35 +00:00
|
|
|
eventArgs := make([]string, 0)
|
2019-03-14 21:49:27 +00:00
|
|
|
eventArgsInterface, eventArgsOK := transformer["eventArgs"]
|
2019-03-11 16:19:18 +00:00
|
|
|
if !eventArgsOK {
|
2019-03-13 16:14:35 +00:00
|
|
|
log.Warnf("contract %s not configured with a list of event arguments to filter for, will not filter events for specific emitted values\r\n", addr)
|
|
|
|
eventArgs = []string{}
|
|
|
|
} else {
|
|
|
|
eventArgsI, eventArgsOK := eventArgsInterface.([]interface{})
|
|
|
|
if !eventArgsOK {
|
|
|
|
log.Fatal(addr, "transformer `eventArgs` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
for _, strI := range eventArgsI {
|
|
|
|
str, strOK := strI.(string)
|
|
|
|
if !strOK {
|
|
|
|
log.Fatal(addr, "transformer `eventArgs` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
eventArgs = append(eventArgs, str)
|
|
|
|
}
|
|
|
|
}
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.EventArgs[strings.ToLower(addr)] = eventArgs
|
2019-03-11 16:19:18 +00:00
|
|
|
|
|
|
|
// Get and check methodArgs
|
2019-03-13 16:14:35 +00:00
|
|
|
methodArgs := make([]string, 0)
|
2019-03-14 21:49:27 +00:00
|
|
|
methodArgsInterface, methodArgsOK := transformer["methodArgs"]
|
2019-03-11 16:19:18 +00:00
|
|
|
if !methodArgsOK {
|
2019-03-13 16:14:35 +00:00
|
|
|
log.Warnf("contract %s not configured with a list of method argument values to poll with, will poll methods with all available arguments\r\n", addr)
|
|
|
|
methodArgs = []string{}
|
|
|
|
} else {
|
|
|
|
methodArgsI, methodArgsOK := methodArgsInterface.([]interface{})
|
|
|
|
if !methodArgsOK {
|
|
|
|
log.Fatal(addr, "transformer `methodArgs` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
for _, strI := range methodArgsI {
|
|
|
|
str, strOK := strI.(string)
|
|
|
|
if !strOK {
|
|
|
|
log.Fatal(addr, "transformer `methodArgs` not of type []string\r\n")
|
|
|
|
}
|
|
|
|
methodArgs = append(methodArgs, str)
|
|
|
|
}
|
|
|
|
}
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.MethodArgs[strings.ToLower(addr)] = methodArgs
|
2019-03-11 16:19:18 +00:00
|
|
|
|
|
|
|
// Get and check startingBlock
|
2019-03-13 16:14:35 +00:00
|
|
|
startInterface, startOK := transformer["startingblock"]
|
|
|
|
if !startOK {
|
|
|
|
log.Fatal(addr, "transformer config is missing `startingBlock` value\r\n")
|
2019-03-11 16:19:18 +00:00
|
|
|
}
|
2019-03-13 16:14:35 +00:00
|
|
|
start, startOK := startInterface.(int64)
|
2019-03-11 16:19:18 +00:00
|
|
|
if !startOK {
|
2019-03-13 16:14:35 +00:00
|
|
|
log.Fatal(addr, "transformer `startingBlock` not of type int\r\n")
|
2019-03-11 16:19:18 +00:00
|
|
|
}
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.StartingBlocks[strings.ToLower(addr)] = start
|
2019-03-11 16:19:18 +00:00
|
|
|
|
|
|
|
// Get pipping
|
2019-03-13 16:14:35 +00:00
|
|
|
var piping bool
|
|
|
|
_, pipeOK := transformer["piping"]
|
2019-03-11 16:19:18 +00:00
|
|
|
if !pipeOK {
|
2019-03-13 16:14:35 +00:00
|
|
|
log.Warnf("contract %s does not have its `piping` set, by default piping is turned off\r\n", addr)
|
|
|
|
piping = false
|
|
|
|
} else {
|
|
|
|
pipingInterface := transformer["piping"]
|
|
|
|
piping, pipeOK = pipingInterface.(bool)
|
|
|
|
if !pipeOK {
|
|
|
|
log.Fatal(addr, "transformer `piping` not of type bool\r\n")
|
|
|
|
}
|
|
|
|
}
|
2019-03-14 21:49:27 +00:00
|
|
|
contractConfig.Piping[strings.ToLower(addr)] = piping
|
2019-03-11 16:19:18 +00:00
|
|
|
}
|
|
|
|
}
|