refactoring event converter and repository so that event logs are
persisted as they are converted, preventing build up in memory, and finishing method polling (for full sync)
This commit is contained in:
parent
390a60f7f6
commit
817bd76713
@ -117,7 +117,7 @@ func init() {
|
|||||||
omniWatcherCmd.Flags().StringVarP(&contractAddress, "contract-address", "a", "", "Single address to generate watchers for")
|
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(&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, "contract-events", "e", []string{}, "Subset of events to watch; by default all events are watched")
|
omniWatcherCmd.Flags().StringArrayVarP(&contractEvents, "contract-events", "e", []string{}, "Subset of events to watch; by default all events are watched")
|
||||||
omniWatcherCmd.Flags().StringArrayVarP(&contractEvents, "contract-methods", "m", nil, "Subset of methods to poll; by default no methods are polled")
|
omniWatcherCmd.Flags().StringArrayVarP(&contractMethods, "contract-methods", "m", nil, "Subset of methods to poll; by default no methods are polled")
|
||||||
omniWatcherCmd.Flags().StringArrayVarP(&eventAddrs, "event-filter-addresses", "f", []string{}, "Account addresses to persist event data for; default is to persist for all found token holder addresses")
|
omniWatcherCmd.Flags().StringArrayVarP(&eventAddrs, "event-filter-addresses", "f", []string{}, "Account addresses to persist event data for; default is to persist for all found token holder addresses")
|
||||||
omniWatcherCmd.Flags().StringArrayVarP(&methodAddrs, "method-filter-addresses", "g", []string{}, "Account addresses to poll methods with; default is to poll with all found token holder addresses")
|
omniWatcherCmd.Flags().StringArrayVarP(&methodAddrs, "method-filter-addresses", "g", []string{}, "Account addresses to poll methods with; default is to poll with all found token holder addresses")
|
||||||
omniWatcherCmd.Flags().StringVarP(&network, "network", "n", "", `Network the contract is deployed on; options: "ropsten", "kovan", and "rinkeby"; default is mainnet"`)
|
omniWatcherCmd.Flags().StringVarP(&network, "network", "n", "", `Network the contract is deployed on; options: "ropsten", "kovan", and "rinkeby"; default is mainnet"`)
|
||||||
|
@ -34,8 +34,8 @@ type Contract struct {
|
|||||||
LastBlock int64
|
LastBlock int64
|
||||||
Abi string
|
Abi string
|
||||||
ParsedAbi abi.ABI
|
ParsedAbi abi.ABI
|
||||||
Events map[string]*types.Event // Map of events to their names
|
Events map[string]types.Event // Map of events to their names
|
||||||
Methods map[string]*types.Method // Map of methods to their names
|
Methods map[string]types.Method // Map of methods to their names
|
||||||
Filters map[string]filters.LogFilter // Map of event filters to their names
|
Filters map[string]filters.LogFilter // Map of event filters to their names
|
||||||
EventAddrs map[string]bool // User-input list of account addresses to watch events for
|
EventAddrs map[string]bool // User-input list of account addresses to watch events for
|
||||||
MethodAddrs map[string]bool // User-input list of account addresses to poll methods for
|
MethodAddrs map[string]bool // User-input list of account addresses to poll methods for
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
// Converter is used to convert watched event logs to
|
// Converter is used to convert watched event logs to
|
||||||
// custom logs containing event input name => value maps
|
// custom logs containing event input name => value maps
|
||||||
type Converter interface {
|
type Converter interface {
|
||||||
Convert(watchedEvent core.WatchedEvent, event *types.Event) error
|
Convert(watchedEvent core.WatchedEvent, event types.Event) (*types.Log, error)
|
||||||
Update(info *contract.Contract)
|
Update(info *contract.Contract)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ func (c *converter) CheckInfo() *contract.Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert the given watched event log into a types.Log for the given event
|
// Convert the given watched event log into a types.Log for the given event
|
||||||
func (c *converter) Convert(watchedEvent core.WatchedEvent, event *types.Event) error {
|
func (c *converter) Convert(watchedEvent core.WatchedEvent, event types.Event) (*types.Log, error) {
|
||||||
contract := 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{})
|
values := make(map[string]interface{})
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ func (c *converter) Convert(watchedEvent core.WatchedEvent, event *types.Event)
|
|||||||
log := helpers.ConvertToLog(watchedEvent)
|
log := helpers.ConvertToLog(watchedEvent)
|
||||||
err := contract.UnpackLogIntoMap(values, event.Name, log)
|
err := contract.UnpackLogIntoMap(values, event.Name, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
strValues := make(map[string]string, len(values))
|
strValues := make(map[string]string, len(values))
|
||||||
@ -95,22 +95,22 @@ func (c *converter) Convert(watchedEvent core.WatchedEvent, event *types.Event)
|
|||||||
case bool:
|
case bool:
|
||||||
strValues[fieldName] = strconv.FormatBool(input.(bool))
|
strValues[fieldName] = strconv.FormatBool(input.(bool))
|
||||||
default:
|
default:
|
||||||
return errors.New("error: unhandled abi type")
|
return nil, errors.New("error: unhandled abi type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only hold onto logs that pass our address filter, if any
|
// Only hold onto logs that pass our address filter, if any
|
||||||
// Persist log here and don't hold onto it
|
|
||||||
if c.contractInfo.PassesEventFilter(strValues) {
|
if c.contractInfo.PassesEventFilter(strValues) {
|
||||||
eventLog := types.Log{
|
eventLog := &types.Log{
|
||||||
|
Event: event,
|
||||||
Id: watchedEvent.LogID,
|
Id: watchedEvent.LogID,
|
||||||
Values: strValues,
|
Values: strValues,
|
||||||
Block: watchedEvent.BlockNumber,
|
Block: watchedEvent.BlockNumber,
|
||||||
Tx: watchedEvent.TxHash,
|
Tx: watchedEvent.TxHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
event.Logs[watchedEvent.LogID] = eventLog
|
return eventLog, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -76,24 +76,24 @@ var _ = Describe("Converter", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
c := converter.NewConverter(info)
|
c := converter.NewConverter(info)
|
||||||
err = c.Convert(mockEvent, event)
|
log, err := c.Convert(mockEvent, event)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
from := common.HexToAddress("0x000000000000000000000000000000000000000000000000000000000000af21")
|
from := common.HexToAddress("0x000000000000000000000000000000000000000000000000000000000000af21")
|
||||||
to := common.HexToAddress("0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391")
|
to := common.HexToAddress("0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391")
|
||||||
value := helpers.BigFromString("1097077688018008265106216665536940668749033598146")
|
value := helpers.BigFromString("1097077688018008265106216665536940668749033598146")
|
||||||
|
|
||||||
v := event.Logs[1].Values["value"]
|
v := log.Values["value"]
|
||||||
|
|
||||||
Expect(event.Logs[1].Values["to"]).To(Equal(to.String()))
|
Expect(log.Values["to"]).To(Equal(to.String()))
|
||||||
Expect(event.Logs[1].Values["from"]).To(Equal(from.String()))
|
Expect(log.Values["from"]).To(Equal(from.String()))
|
||||||
Expect(v).To(Equal(value.String()))
|
Expect(v).To(Equal(value.String()))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Fails with an empty contract", func() {
|
It("Fails with an empty contract", func() {
|
||||||
event := info.Events["Transfer"]
|
event := info.Events["Transfer"]
|
||||||
c := converter.NewConverter(&contract.Contract{})
|
c := converter.NewConverter(&contract.Contract{})
|
||||||
err = c.Convert(mockEvent, event)
|
_, err = c.Convert(mockEvent, event)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -57,8 +57,8 @@ func (p *parser) Parse() error {
|
|||||||
|
|
||||||
// Returns wanted methods, if they meet the criteria, as map of types.Methods
|
// Returns wanted methods, if they meet the criteria, as map of types.Methods
|
||||||
// Only returns specified methods
|
// Only returns specified methods
|
||||||
func (p *parser) GetMethods(wanted []string) map[string]*types.Method {
|
func (p *parser) GetMethods(wanted []string) map[string]types.Method {
|
||||||
addrMethods := map[string]*types.Method{}
|
addrMethods := map[string]types.Method{}
|
||||||
|
|
||||||
for _, m := range p.parsedAbi.Methods {
|
for _, m := range p.parsedAbi.Methods {
|
||||||
// Only return methods that have less than 3 inputs, 1 output, and wanted
|
// Only return methods that have less than 3 inputs, 1 output, and wanted
|
||||||
@ -83,8 +83,8 @@ func (p *parser) GetMethods(wanted []string) map[string]*types.Method {
|
|||||||
|
|
||||||
// Returns wanted events as map of types.Events
|
// Returns wanted events as map of types.Events
|
||||||
// If no events are specified, all events are returned
|
// If no events are specified, all events are returned
|
||||||
func (p *parser) GetEvents(wanted []string) map[string]*types.Event {
|
func (p *parser) GetEvents(wanted []string) map[string]types.Event {
|
||||||
events := map[string]*types.Event{}
|
events := map[string]types.Event{}
|
||||||
|
|
||||||
for _, e := range p.parsedAbi.Events {
|
for _, e := range p.parsedAbi.Events {
|
||||||
if len(wanted) == 0 || stringInSlice(wanted, e.Name) {
|
if len(wanted) == 0 || stringInSlice(wanted, e.Name) {
|
||||||
|
@ -41,7 +41,7 @@ var ExpectedTransferFilter = filters.LogFilter{
|
|||||||
Name: "Transfer",
|
Name: "Transfer",
|
||||||
Address: constants.TusdContractAddress,
|
Address: constants.TusdContractAddress,
|
||||||
ToBlock: -1,
|
ToBlock: -1,
|
||||||
FromBlock: 5197514,
|
FromBlock: 6194634,
|
||||||
Topics: core.Topics{constants.TransferEvent.Signature()},
|
Topics: core.Topics{constants.TransferEvent.Signature()},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ var ExpectedApprovalFilter = filters.LogFilter{
|
|||||||
Name: "Approval",
|
Name: "Approval",
|
||||||
Address: constants.TusdContractAddress,
|
Address: constants.TusdContractAddress,
|
||||||
ToBlock: -1,
|
ToBlock: -1,
|
||||||
FromBlock: 5197514,
|
FromBlock: 6194634,
|
||||||
Topics: core.Topics{constants.ApprovalEvent.Signature()},
|
Topics: core.Topics{constants.ApprovalEvent.Signature()},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +57,6 @@ type TransferLog struct {
|
|||||||
Id int64 `db:"id"`
|
Id int64 `db:"id"`
|
||||||
VulvanizeLogId int64 `db:"vulcanize_log_id"`
|
VulvanizeLogId int64 `db:"vulcanize_log_id"`
|
||||||
TokenName string `db:"token_name"`
|
TokenName string `db:"token_name"`
|
||||||
TokenAddress string `db:"token_address"`
|
|
||||||
EventName string `db:"event_name"`
|
|
||||||
Block int64 `db:"block"`
|
Block int64 `db:"block"`
|
||||||
Tx string `db:"tx"`
|
Tx string `db:"tx"`
|
||||||
From string `db:"from_"`
|
From string `db:"from_"`
|
||||||
@ -66,6 +64,14 @@ type TransferLog struct {
|
|||||||
Value string `db:"value_"`
|
Value string `db:"value_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BalanceOf struct {
|
||||||
|
Id int64 `db:"id"`
|
||||||
|
TokenName string `db:"token_name"`
|
||||||
|
Block int64 `db:"block"`
|
||||||
|
Address string `db:"who_"`
|
||||||
|
Balance string `db:"returned"`
|
||||||
|
}
|
||||||
|
|
||||||
func SetupBC() core.BlockChain {
|
func SetupBC() core.BlockChain {
|
||||||
infuraIPC := "https://mainnet.infura.io/v3/b09888c1113640cc9ab42750ce750c05"
|
infuraIPC := "https://mainnet.infura.io/v3/b09888c1113640cc9ab42750ce750c05"
|
||||||
rawRpcClient, err := rpc.Dial(infuraIPC)
|
rawRpcClient, err := rpc.Dial(infuraIPC)
|
||||||
@ -139,7 +145,7 @@ func SetupTusdContract(wantedEvents, wantedMethods []string) *contract.Contract
|
|||||||
Address: constants.TusdContractAddress,
|
Address: constants.TusdContractAddress,
|
||||||
Abi: p.Abi(),
|
Abi: p.Abi(),
|
||||||
ParsedAbi: p.ParsedAbi(),
|
ParsedAbi: p.ParsedAbi(),
|
||||||
StartingBlock: 5197514,
|
StartingBlock: 6194634,
|
||||||
LastBlock: 6507323,
|
LastBlock: 6507323,
|
||||||
Events: p.GetEvents(wantedEvents),
|
Events: p.GetEvents(wantedEvents),
|
||||||
Methods: p.GetMethods(wantedMethods),
|
Methods: p.GetMethods(wantedMethods),
|
||||||
|
@ -29,9 +29,9 @@ type Parser interface {
|
|||||||
Parse(contractAddr string) error
|
Parse(contractAddr string) error
|
||||||
Abi() string
|
Abi() string
|
||||||
ParsedAbi() abi.ABI
|
ParsedAbi() abi.ABI
|
||||||
GetMethods(wanted []string) map[string]*types.Method
|
GetMethods(wanted []string) map[string]types.Method
|
||||||
GetAddrMethods(wanted []string) map[string]*types.Method
|
GetAddrMethods(wanted []string) map[string]types.Method
|
||||||
GetEvents(wanted []string) map[string]*types.Event
|
GetEvents(wanted []string) map[string]types.Event
|
||||||
}
|
}
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
@ -73,8 +73,8 @@ func (p *parser) Parse(contractAddr string) error {
|
|||||||
// Returns wanted methods, if they meet the criteria, as map of types.Methods
|
// Returns wanted methods, if they meet the criteria, as map of types.Methods
|
||||||
// Empty wanted array => all methods that fit are returned
|
// Empty wanted array => all methods that fit are returned
|
||||||
// Nil wanted array => no events are returned
|
// Nil wanted array => no events are returned
|
||||||
func (p *parser) GetAddrMethods(wanted []string) map[string]*types.Method {
|
func (p *parser) GetAddrMethods(wanted []string) map[string]types.Method {
|
||||||
addrMethods := map[string]*types.Method{}
|
addrMethods := map[string]types.Method{}
|
||||||
if wanted == nil {
|
if wanted == nil {
|
||||||
return addrMethods
|
return addrMethods
|
||||||
}
|
}
|
||||||
@ -103,8 +103,8 @@ func (p *parser) GetAddrMethods(wanted []string) map[string]*types.Method {
|
|||||||
// Returns wanted methods as map of types.Methods
|
// Returns wanted methods as map of types.Methods
|
||||||
// Empty wanted array => all events are returned
|
// Empty wanted array => all events are returned
|
||||||
// Nil wanted array => no events are returned
|
// Nil wanted array => no events are returned
|
||||||
func (p *parser) GetMethods(wanted []string) map[string]*types.Method {
|
func (p *parser) GetMethods(wanted []string) map[string]types.Method {
|
||||||
methods := map[string]*types.Method{}
|
methods := map[string]types.Method{}
|
||||||
if wanted == nil {
|
if wanted == nil {
|
||||||
return methods
|
return methods
|
||||||
}
|
}
|
||||||
@ -122,8 +122,8 @@ func (p *parser) GetMethods(wanted []string) map[string]*types.Method {
|
|||||||
// Returns wanted events as map of types.Events
|
// Returns wanted events as map of types.Events
|
||||||
// Empty wanted array => all events are returned
|
// Empty wanted array => all events are returned
|
||||||
// Nil wanted array => no events are returned
|
// Nil wanted array => no events are returned
|
||||||
func (p *parser) GetEvents(wanted []string) map[string]*types.Event {
|
func (p *parser) GetEvents(wanted []string) map[string]types.Event {
|
||||||
events := map[string]*types.Event{}
|
events := map[string]types.Event{}
|
||||||
if wanted == nil {
|
if wanted == nil {
|
||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,8 @@ var _ = Describe("Parser", func() {
|
|||||||
Expect(ok).To(Equal(false))
|
Expect(ok).To(Equal(false))
|
||||||
m, ok := methods["balanceOf"]
|
m, ok := methods["balanceOf"]
|
||||||
Expect(ok).To(Equal(true))
|
Expect(ok).To(Equal(true))
|
||||||
Expect(len(m.Inputs)).To(Equal(1))
|
Expect(len(m.Args)).To(Equal(1))
|
||||||
Expect(len(m.Outputs)).To(Equal(1))
|
Expect(len(m.Return)).To(Equal(1))
|
||||||
|
|
||||||
events := mp.GetEvents([]string{"Transfer"})
|
events := mp.GetEvents([]string{"Transfer"})
|
||||||
_, ok = events["Mint"]
|
_, ok = events["Mint"]
|
||||||
@ -130,16 +130,16 @@ var _ = Describe("Parser", func() {
|
|||||||
m, ok := selectMethods["balanceOf"]
|
m, ok := selectMethods["balanceOf"]
|
||||||
Expect(ok).To(Equal(true))
|
Expect(ok).To(Equal(true))
|
||||||
|
|
||||||
abiTy := m.Inputs[0].Type.T
|
abiTy := m.Args[0].Type.T
|
||||||
Expect(abiTy).To(Equal(abi.AddressTy))
|
Expect(abiTy).To(Equal(abi.AddressTy))
|
||||||
|
|
||||||
pgTy := m.Inputs[0].PgType
|
pgTy := m.Args[0].PgType
|
||||||
Expect(pgTy).To(Equal("CHARACTER VARYING(66)"))
|
Expect(pgTy).To(Equal("CHARACTER VARYING(66)"))
|
||||||
|
|
||||||
abiTy = m.Outputs[0].Type.T
|
abiTy = m.Return[0].Type.T
|
||||||
Expect(abiTy).To(Equal(abi.UintTy))
|
Expect(abiTy).To(Equal(abi.UintTy))
|
||||||
|
|
||||||
pgTy = m.Outputs[0].PgType
|
pgTy = m.Return[0].PgType
|
||||||
Expect(pgTy).To(Equal("DECIMAL"))
|
Expect(pgTy).To(Equal("DECIMAL"))
|
||||||
|
|
||||||
_, ok = selectMethods["totalSupply"]
|
_, ok = selectMethods["totalSupply"]
|
||||||
|
@ -18,37 +18,44 @@ package poller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/constants"
|
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/contract"
|
"github.com/vulcanize/vulcanizedb/pkg/omni/contract"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/repository"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Poller interface {
|
type Poller interface {
|
||||||
PollContract(con *contract.Contract) error
|
PollContract(con contract.Contract) error
|
||||||
PollMethod(contractAbi, contractAddress, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error
|
FetchContractData(contractAbi, contractAddress, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type poller struct {
|
type poller struct {
|
||||||
|
repository.MethodDatastore
|
||||||
bc core.BlockChain
|
bc core.BlockChain
|
||||||
contract *contract.Contract
|
contract contract.Contract
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPoller(blockChain core.BlockChain) *poller {
|
func NewPoller(blockChain core.BlockChain, db *postgres.DB) *poller {
|
||||||
|
|
||||||
return &poller{
|
return &poller{
|
||||||
bc: blockChain,
|
MethodDatastore: repository.NewMethodDatastore(db),
|
||||||
|
bc: blockChain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to call contract's methods found in abi using list of contract-related addresses
|
// Used to call contract's methods found in abi using list of contract-related addresses
|
||||||
func (p *poller) PollContract(con *contract.Contract) error {
|
func (p *poller) PollContract(con contract.Contract) error {
|
||||||
p.contract = con
|
p.contract = con
|
||||||
// Iterate over each of the contracts methods
|
// Iterate over each of the contracts methods
|
||||||
for _, m := range con.Methods {
|
for _, m := range con.Methods {
|
||||||
switch len(m.Inputs) {
|
switch len(m.Args) {
|
||||||
case 0:
|
case 0:
|
||||||
if err := p.pollNoArg(m); err != nil {
|
if err := p.pollNoArg(m); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -71,76 +78,122 @@ func (p *poller) PollContract(con *contract.Contract) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Poll methods that take no arguments
|
// Poll methods that take no arguments
|
||||||
func (p *poller) pollNoArg(m *types.Method) error {
|
func (p *poller) pollNoArg(m types.Method) error {
|
||||||
result := &types.Result{
|
result := types.Result{
|
||||||
Inputs: nil,
|
Method: m,
|
||||||
Outputs: map[int64]interface{}{},
|
Inputs: nil,
|
||||||
PgType: m.Outputs[0].PgType,
|
PgType: m.Return[0].PgType,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := p.contract.StartingBlock; i <= p.contract.LastBlock; i++ {
|
for i := p.contract.StartingBlock; i <= p.contract.LastBlock; i++ {
|
||||||
var res interface{}
|
var out interface{}
|
||||||
err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, result.Inputs, &res, i)
|
err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, nil, &out, i)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("poller error calling 0 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", i, m.Name, p.contract.Address, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
strOut, err := stringify(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
result.Outputs[i] = res
|
|
||||||
}
|
|
||||||
|
|
||||||
// Persist results now instead of holding onto them
|
result.Output = strOut
|
||||||
m.Results = append(m.Results, result)
|
result.Block = i
|
||||||
|
|
||||||
|
// Persist result immediately
|
||||||
|
err = p.PersistResult(result, p.contract.Address, p.contract.Name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("poller error persisting 0 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", i, m.Name, p.contract.Address, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use token holder address to poll methods that take 1 address argument (e.g. balanceOf)
|
// Use token holder address to poll methods that take 1 address argument (e.g. balanceOf)
|
||||||
func (p *poller) pollSingleArg(m *types.Method) error {
|
func (p *poller) pollSingleArg(m types.Method) error {
|
||||||
for addr := range p.contract.TknHolderAddrs {
|
result := types.Result{
|
||||||
result := &types.Result{
|
Method: m,
|
||||||
Inputs: make([]interface{}, 1),
|
Inputs: make([]interface{}, 1),
|
||||||
Outputs: map[int64]interface{}{},
|
PgType: m.Return[0].PgType,
|
||||||
PgType: m.Outputs[0].PgType,
|
}
|
||||||
}
|
|
||||||
result.Inputs[0] = common.HexToAddress(addr)
|
|
||||||
|
|
||||||
|
for addr := range p.contract.TknHolderAddrs {
|
||||||
for i := p.contract.StartingBlock; i <= p.contract.LastBlock; i++ {
|
for i := p.contract.StartingBlock; i <= p.contract.LastBlock; i++ {
|
||||||
var res interface{}
|
hashArgs := []common.Address{common.HexToAddress(addr)}
|
||||||
err := p.bc.FetchContractData(constants.TusdAbiString, p.contract.Address, m.Name, result.Inputs, &res, i)
|
in := make([]interface{}, len(hashArgs))
|
||||||
|
strIn := make([]interface{}, len(hashArgs))
|
||||||
|
for i, s := range hashArgs {
|
||||||
|
in[i] = s
|
||||||
|
strIn[i] = s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var out interface{}
|
||||||
|
err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, in, &out, i)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("poller error calling 1 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", i, m.Name, p.contract.Address, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
strOut, err := stringify(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
result.Outputs[i] = res
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Results = append(m.Results, result)
|
result.Output = strOut
|
||||||
|
result.Block = i
|
||||||
|
result.Inputs = strIn
|
||||||
|
|
||||||
|
err = p.PersistResult(result, p.contract.Address, p.contract.Name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("poller error persisting 1 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", i, m.Name, p.contract.Address, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use token holder address to poll methods that take 2 address arguments (e.g. allowance)
|
// Use token holder address to poll methods that take 2 address arguments (e.g. allowance)
|
||||||
func (p *poller) pollDoubleArg(m *types.Method) error {
|
func (p *poller) pollDoubleArg(m types.Method) error {
|
||||||
// For a large block range and address list this will take a really, really long time- maybe we should only do 1 arg methods
|
// For a large block range and address list this will take a really, really long time- maybe we should only do 1 arg methods
|
||||||
|
result := types.Result{
|
||||||
|
Method: m,
|
||||||
|
Inputs: make([]interface{}, 2),
|
||||||
|
PgType: m.Return[0].PgType,
|
||||||
|
}
|
||||||
|
|
||||||
for addr1 := range p.contract.TknHolderAddrs {
|
for addr1 := range p.contract.TknHolderAddrs {
|
||||||
for addr2 := range p.contract.TknHolderAddrs {
|
for addr2 := range p.contract.TknHolderAddrs {
|
||||||
result := &types.Result{
|
|
||||||
Inputs: make([]interface{}, 2),
|
|
||||||
Outputs: map[int64]interface{}{},
|
|
||||||
PgType: m.Outputs[0].PgType,
|
|
||||||
}
|
|
||||||
result.Inputs[0] = common.HexToAddress(addr1)
|
|
||||||
result.Inputs[1] = common.HexToAddress(addr2)
|
|
||||||
|
|
||||||
for i := p.contract.StartingBlock; i <= p.contract.LastBlock; i++ {
|
for i := p.contract.StartingBlock; i <= p.contract.LastBlock; i++ {
|
||||||
var res interface{}
|
hashArgs := []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}
|
||||||
err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, result.Inputs, &res, i)
|
in := make([]interface{}, len(hashArgs))
|
||||||
|
strIn := make([]interface{}, len(hashArgs))
|
||||||
|
for i, s := range hashArgs {
|
||||||
|
in[i] = s
|
||||||
|
strIn[i] = s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var out interface{}
|
||||||
|
err := p.bc.FetchContractData(p.contract.Abi, p.contract.Address, m.Name, in, &out, i)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("poller error calling 2 argument method\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", i, m.Name, p.contract.Address, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
strOut, err := stringify(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
result.Outputs[i] = res
|
|
||||||
|
result.Output = strOut
|
||||||
|
result.Block = i
|
||||||
|
result.Inputs = strIn
|
||||||
|
|
||||||
|
err = p.PersistResult(result, p.contract.Address, p.contract.Name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("poller error persisting 2 argument method result\r\nblock: %d, method: %s, contract: %s\r\nerr: %v", i, m.Name, p.contract.Address, err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Results = append(m.Results, result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,6 +201,29 @@ func (p *poller) pollDoubleArg(m *types.Method) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is just a wrapper around the poller blockchain's FetchContractData method
|
// This is just a wrapper around the poller blockchain's FetchContractData method
|
||||||
func (p *poller) PollMethod(contractAbi, contractAddress, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error {
|
func (p *poller) FetchContractData(contractAbi, contractAddress, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error {
|
||||||
return p.bc.FetchContractData(contractAbi, contractAddress, method, methodArgs, result, blockNumber)
|
return p.bc.FetchContractData(contractAbi, contractAddress, method, methodArgs, result, blockNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringify(input interface{}) (string, error) {
|
||||||
|
switch input.(type) {
|
||||||
|
case *big.Int:
|
||||||
|
var b *big.Int
|
||||||
|
b = input.(*big.Int)
|
||||||
|
return b.String(), nil
|
||||||
|
case common.Address:
|
||||||
|
var a common.Address
|
||||||
|
a = input.(common.Address)
|
||||||
|
return a.String(), nil
|
||||||
|
case common.Hash:
|
||||||
|
var h common.Hash
|
||||||
|
h = input.(common.Hash)
|
||||||
|
return h.String(), nil
|
||||||
|
case string:
|
||||||
|
return input.(string), nil
|
||||||
|
case bool:
|
||||||
|
return strconv.FormatBool(input.(bool)), nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("error: unhandled return type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,12 +17,13 @@
|
|||||||
package poller_test
|
package poller_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"fmt"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/helpers"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/constants"
|
"github.com/vulcanize/vulcanizedb/pkg/omni/constants"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/contract"
|
"github.com/vulcanize/vulcanizedb/pkg/omni/contract"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/helpers/test_helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/omni/helpers/test_helpers"
|
||||||
@ -33,13 +34,20 @@ var _ = Describe("Poller", func() {
|
|||||||
|
|
||||||
var p poller.Poller
|
var p poller.Poller
|
||||||
var con *contract.Contract
|
var con *contract.Contract
|
||||||
|
var db *postgres.DB
|
||||||
|
var bc core.BlockChain
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
p = poller.NewPoller(test_helpers.SetupBC())
|
db, bc = test_helpers.SetupDBandBC()
|
||||||
|
p = poller.NewPoller(bc, db)
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
test_helpers.TearDown(db)
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("PollContract", func() {
|
Describe("PollContract", func() {
|
||||||
It("Polls contract methods using token holder address list", func() {
|
It("Polls specified contract methods using contract's token holder address list", func() {
|
||||||
con = test_helpers.SetupTusdContract(nil, []string{"balanceOf"})
|
con = test_helpers.SetupTusdContract(nil, []string{"balanceOf"})
|
||||||
Expect(con.Abi).To(Equal(constants.TusdAbiString))
|
Expect(con.Abi).To(Equal(constants.TusdAbiString))
|
||||||
con.StartingBlock = 6707322
|
con.StartingBlock = 6707322
|
||||||
@ -49,36 +57,56 @@ var _ = Describe("Poller", func() {
|
|||||||
"0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE": true,
|
"0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := p.PollContract(con)
|
err := p.PollContract(*con)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(len(con.Methods["balanceOf"].Results)).To(Equal(2))
|
scanStruct := test_helpers.BalanceOf{}
|
||||||
res1 := con.Methods["balanceOf"].Results[0]
|
|
||||||
res2 := con.Methods["balanceOf"].Results[1]
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
expectedRes11 := helpers.BigFromString("66386309548896882859581786")
|
Expect(err).ToNot(HaveOccurred())
|
||||||
expectedRes12 := helpers.BigFromString("66386309548896882859581786")
|
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||||
expectedRes21 := helpers.BigFromString("17982350181394112023885864")
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
expectedRes22 := helpers.BigFromString("17982350181394112023885864")
|
|
||||||
if res1.Inputs[0].(common.Address).String() == "0xfE9e8709d3215310075d67E3ed32A380CCf451C8" {
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(res2.Inputs[0].(common.Address).String()).To(Equal("0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE"))
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res1.Outputs[6707322].(*big.Int).String()).To(Equal(expectedRes11.String()))
|
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||||
Expect(res1.Outputs[6707323].(*big.Int).String()).To(Equal(expectedRes12.String()))
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
Expect(res2.Outputs[6707322].(*big.Int).String()).To(Equal(expectedRes21.String()))
|
|
||||||
Expect(res2.Outputs[6707323].(*big.Int).String()).To(Equal(expectedRes22.String()))
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
} else {
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res2.Inputs[0].(common.Address).String()).To(Equal("0xfE9e8709d3215310075d67E3ed32A380CCf451C8"))
|
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||||
Expect(res2.Outputs[6707322].(*big.Int).String()).To(Equal(expectedRes11.String()))
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
Expect(res2.Outputs[6707323].(*big.Int).String()).To(Equal(expectedRes12.String()))
|
|
||||||
Expect(res1.Outputs[6707322].(*big.Int).String()).To(Equal(expectedRes21.String()))
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(res1.Outputs[6707323].(*big.Int).String()).To(Equal(expectedRes22.String()))
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||||
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Does not poll and persist any methods if none are specified", func() {
|
||||||
|
con = test_helpers.SetupTusdContract(nil, nil)
|
||||||
|
Expect(con.Abi).To(Equal(constants.TusdAbiString))
|
||||||
|
con.StartingBlock = 6707322
|
||||||
|
con.LastBlock = 6707323
|
||||||
|
con.TknHolderAddrs = map[string]bool{
|
||||||
|
"0xfE9e8709d3215310075d67E3ed32A380CCf451C8": true,
|
||||||
|
"0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := p.PollContract(*con)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
scanStruct := test_helpers.BalanceOf{}
|
||||||
|
|
||||||
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("PollMethod", func() {
|
Describe("PollMethod", func() {
|
||||||
It("Polls a single contract method", func() {
|
It("Polls a single contract method", func() {
|
||||||
var name = new(string)
|
var name = new(string)
|
||||||
err := p.PollMethod(constants.TusdAbiString, constants.TusdContractAddress, "name", nil, &name, 6197514)
|
err := p.FetchContractData(constants.TusdAbiString, constants.TusdContractAddress, "name", nil, &name, 6197514)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(*name).To(Equal("TrueUSD"))
|
Expect(*name).To(Equal("TrueUSD"))
|
||||||
})
|
})
|
||||||
|
@ -22,14 +22,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/contract"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event datastore is used to persist event data into custom tables
|
// Event datastore is used to persist event data into custom tables
|
||||||
type EventDatastore interface {
|
type EventDatastore interface {
|
||||||
PersistContractEvents(con *contract.Contract) error
|
PersistLog(event types.Log, contractAddr, contractName string) error
|
||||||
CreateEventTable(contractName string, event *types.Event) (bool, error)
|
CreateEventTable(contractName string, event types.Log) (bool, error)
|
||||||
CreateContractSchema(contractName string) (bool, error)
|
CreateContractSchema(contractName string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,93 +43,71 @@ func NewEventDataStore(db *postgres.DB) *eventDatastore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a schema for the contract
|
// Creates a schema for the contract if needed
|
||||||
// Creates tables for the watched contract events
|
// Creates table for the watched contract event if needed
|
||||||
// Persists converted event log data into these custom tables
|
// Persists converted event log data into this custom table
|
||||||
// Edit this method to accept a single event
|
func (d *eventDatastore) PersistLog(event types.Log, contractAddr, contractName string) error {
|
||||||
func (d *eventDatastore) PersistContractEvents(con *contract.Contract) error {
|
_, err := d.CreateContractSchema(contractAddr)
|
||||||
_, err := d.CreateContractSchema(con.Address)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for eventName := range con.Filters {
|
_, err = d.CreateEventTable(contractAddr, event)
|
||||||
event := con.Events[eventName]
|
if err != nil {
|
||||||
|
return err
|
||||||
// Move to next event if there are no logs to process
|
|
||||||
if len(event.Logs) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a pg table for the event if one does not already exist
|
|
||||||
_, err = d.CreateEventTable(con.Address, event)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Persist event log data in this table
|
|
||||||
err = d.persistLogs(eventName, con)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear logs when we are done using them
|
|
||||||
event.Logs = map[int64]types.Log{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return d.persistLog(event, contractAddr, contractName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a custom postgres command to persist logs for the given event
|
// Creates a custom postgres command to persist logs for the given event
|
||||||
func (d *eventDatastore) persistLogs(eventName string, con *contract.Contract) error {
|
func (d *eventDatastore) persistLog(event types.Log, contractAddr, contractName string) error {
|
||||||
for id, log := range con.Events[eventName].Logs {
|
// Begin postgres string
|
||||||
// Begin postgres string
|
pgStr := fmt.Sprintf("INSERT INTO c%s.%s_event ", strings.ToLower(contractAddr), strings.ToLower(event.Name))
|
||||||
pgStr := fmt.Sprintf("INSERT INTO c%s.%s ", strings.ToLower(con.Address), strings.ToLower(eventName))
|
pgStr = pgStr + "(vulcanize_log_id, token_name, block, tx"
|
||||||
pgStr = pgStr + "(vulcanize_log_id, token_name, block, tx"
|
|
||||||
|
|
||||||
// Pack the corresponding variables in a slice
|
// Pack the corresponding variables in a slice
|
||||||
var data []interface{}
|
var data []interface{}
|
||||||
data = append(data,
|
data = append(data,
|
||||||
id,
|
event.Id,
|
||||||
con.Name,
|
contractName,
|
||||||
log.Block,
|
event.Block,
|
||||||
log.Tx)
|
event.Tx)
|
||||||
|
|
||||||
// Iterate over name-value pairs in the log adding
|
// Iterate over name-value pairs in the log adding
|
||||||
// names to the string and pushing values to the slice
|
// names to the string and pushing values to the slice
|
||||||
counter := 0 // Keep track of number of inputs
|
counter := 0 // Keep track of number of inputs
|
||||||
for inputName, input := range log.Values {
|
for inputName, input := range event.Values {
|
||||||
counter += 1
|
counter += 1
|
||||||
pgStr = pgStr + fmt.Sprintf(", %s_", strings.ToLower(inputName)) // Add underscore after to avoid any collisions with reserved pg words
|
pgStr = pgStr + fmt.Sprintf(", %s_", strings.ToLower(inputName)) // Add underscore after to avoid any collisions with reserved pg words
|
||||||
data = append(data, input)
|
data = append(data, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish off the string and execute the command using the packed data
|
// Finish off the string and execute the command using the packed data
|
||||||
// For each input entry we created we add its postgres command variable to the string
|
// For each input entry we created we add its postgres command variable to the string
|
||||||
pgStr = pgStr + ") VALUES ($1, $2, $3, $4"
|
pgStr = pgStr + ") VALUES ($1, $2, $3, $4"
|
||||||
for i := 0; i < counter; i++ {
|
for i := 0; i < counter; i++ {
|
||||||
pgStr = pgStr + fmt.Sprintf(", $%d", i+5)
|
pgStr = pgStr + fmt.Sprintf(", $%d", i+5)
|
||||||
}
|
}
|
||||||
pgStr = pgStr + ") ON CONFLICT (vulcanize_log_id) DO NOTHING"
|
pgStr = pgStr + ") ON CONFLICT (vulcanize_log_id) DO NOTHING"
|
||||||
|
|
||||||
_, err := d.DB.Exec(pgStr, data...)
|
_, err := d.DB.Exec(pgStr, data...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for event table and creates it if it does not already exist
|
// Checks for event table and creates it if it does not already exist
|
||||||
func (d *eventDatastore) CreateEventTable(contractName string, event *types.Event) (bool, error) {
|
func (d *eventDatastore) CreateEventTable(contractAddr string, event types.Log) (bool, error) {
|
||||||
tableExists, err := d.checkForTable(contractName, event.Name)
|
tableExists, err := d.checkForTable(contractAddr, event.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tableExists {
|
if !tableExists {
|
||||||
err = d.newEventTable(contractName, event)
|
err = d.newEventTable(contractAddr, event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -140,9 +117,9 @@ func (d *eventDatastore) CreateEventTable(contractName string, event *types.Even
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates a table for the given contract and event
|
// Creates a table for the given contract and event
|
||||||
func (d *eventDatastore) newEventTable(contractAddr string, event *types.Event) error {
|
func (d *eventDatastore) newEventTable(contractAddr string, event types.Log) error {
|
||||||
// Begin pg string
|
// Begin pg string
|
||||||
pgStr := fmt.Sprintf("CREATE TABLE IF NOT EXISTS c%s.%s ", strings.ToLower(contractAddr), strings.ToLower(event.Name))
|
pgStr := fmt.Sprintf("CREATE TABLE IF NOT EXISTS c%s.%s_event ", strings.ToLower(contractAddr), strings.ToLower(event.Name))
|
||||||
pgStr = pgStr + "(id SERIAL, vulcanize_log_id INTEGER NOT NULL UNIQUE, token_name CHARACTER VARYING(66) NOT NULL, block INTEGER NOT NULL, tx CHARACTER VARYING(66) NOT NULL,"
|
pgStr = pgStr + "(id SERIAL, vulcanize_log_id INTEGER NOT NULL UNIQUE, token_name CHARACTER VARYING(66) NOT NULL, block INTEGER NOT NULL, tx CHARACTER VARYING(66) NOT NULL,"
|
||||||
|
|
||||||
// Iterate over event fields, using their name and pgType to grow the string
|
// Iterate over event fields, using their name and pgType to grow the string
|
||||||
@ -158,7 +135,7 @@ func (d *eventDatastore) newEventTable(contractAddr string, event *types.Event)
|
|||||||
|
|
||||||
// Checks if a table already exists for the given contract and event
|
// Checks if a table already exists for the given contract and event
|
||||||
func (d *eventDatastore) checkForTable(contractAddr string, eventName string) (bool, error) {
|
func (d *eventDatastore) checkForTable(contractAddr string, eventName string) (bool, error) {
|
||||||
pgStr := fmt.Sprintf("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'c%s' AND table_name = '%s')", strings.ToLower(contractAddr), strings.ToLower(eventName))
|
pgStr := fmt.Sprintf("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'c%s' AND table_name = '%s_event')", strings.ToLower(contractAddr), strings.ToLower(eventName))
|
||||||
|
|
||||||
var exists bool
|
var exists bool
|
||||||
err := d.DB.Get(&exists, pgStr)
|
err := d.DB.Get(&exists, pgStr)
|
||||||
@ -171,6 +148,7 @@ func (d *eventDatastore) CreateContractSchema(contractAddr string) (bool, error)
|
|||||||
if contractAddr == "" {
|
if contractAddr == "" {
|
||||||
return false, errors.New("error: no contract address specified")
|
return false, errors.New("error: no contract address specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
schemaExists, err := d.checkForSchema(contractAddr)
|
schemaExists, err := d.checkForSchema(contractAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -18,8 +18,6 @@ package repository_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
@ -51,11 +49,11 @@ var _ = Describe("Repository", func() {
|
|||||||
var db *postgres.DB
|
var db *postgres.DB
|
||||||
var dataStore repository.EventDatastore
|
var dataStore repository.EventDatastore
|
||||||
var err error
|
var err error
|
||||||
|
var log *types.Log
|
||||||
var con *contract.Contract
|
var con *contract.Contract
|
||||||
var vulcanizeLogId int64
|
var vulcanizeLogId int64
|
||||||
var wantedEvents = []string{"Transfer"}
|
var wantedEvents = []string{"Transfer"}
|
||||||
var event *types.Event
|
var event types.Event
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
db, con = test_helpers.SetupTusdRepo(&vulcanizeLogId, wantedEvents, []string{})
|
db, con = test_helpers.SetupTusdRepo(&vulcanizeLogId, wantedEvents, []string{})
|
||||||
@ -66,7 +64,7 @@ var _ = Describe("Repository", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
c := converter.NewConverter(con)
|
c := converter.NewConverter(con)
|
||||||
err = c.Convert(mockEvent, event)
|
log, err = c.Convert(mockEvent, event)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
dataStore = repository.NewEventDataStore(db)
|
dataStore = repository.NewEventDataStore(db)
|
||||||
@ -88,25 +86,25 @@ var _ = Describe("Repository", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("CreateContractTable", func() {
|
Describe("CreateEventTable", func() {
|
||||||
It("Creates table if it doesn't exist", func() {
|
It("Creates table if it doesn't exist", func() {
|
||||||
created, err := dataStore.CreateContractSchema(con.Address)
|
created, err := dataStore.CreateContractSchema(con.Address)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(created).To(Equal(true))
|
Expect(created).To(Equal(true))
|
||||||
|
|
||||||
created, err = dataStore.CreateEventTable(con.Address, event)
|
created, err = dataStore.CreateEventTable(con.Address, *log)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(created).To(Equal(true))
|
Expect(created).To(Equal(true))
|
||||||
|
|
||||||
created, err = dataStore.CreateEventTable(con.Address, event)
|
created, err = dataStore.CreateEventTable(con.Address, *log)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(created).To(Equal(false))
|
Expect(created).To(Equal(false))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("PersistContractEvents", func() {
|
Describe("PersistLog", func() {
|
||||||
It("Persists contract event values into custom tables, adding any addresses to a growing list of contract associated addresses", func() {
|
It("Persists contract event log values into custom tables, adding any addresses to a growing list of contract associated addresses", func() {
|
||||||
err = dataStore.PersistContractEvents(con)
|
err = dataStore.PersistLog(*log, con.Address, con.Name)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
b, ok := con.TknHolderAddrs["0x000000000000000000000000000000000000Af21"]
|
b, ok := con.TknHolderAddrs["0x000000000000000000000000000000000000Af21"]
|
||||||
@ -117,9 +115,9 @@ var _ = Describe("Repository", func() {
|
|||||||
Expect(ok).To(Equal(true))
|
Expect(ok).To(Equal(true))
|
||||||
Expect(b).To(Equal(true))
|
Expect(b).To(Equal(true))
|
||||||
|
|
||||||
log := test_helpers.TransferLog{}
|
scanLog := test_helpers.TransferLog{}
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.Transfer", constants.TusdContractAddress)).StructScan(&log)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.transfer_event", constants.TusdContractAddress)).StructScan(&scanLog)
|
||||||
expectedLog := test_helpers.TransferLog{
|
expectedLog := test_helpers.TransferLog{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
VulvanizeLogId: vulcanizeLogId,
|
VulvanizeLogId: vulcanizeLogId,
|
||||||
@ -130,17 +128,17 @@ var _ = Describe("Repository", func() {
|
|||||||
To: "0x09BbBBE21a5975cAc061D82f7b843bCE061BA391",
|
To: "0x09BbBBE21a5975cAc061D82f7b843bCE061BA391",
|
||||||
Value: "1097077688018008265106216665536940668749033598146",
|
Value: "1097077688018008265106216665536940668749033598146",
|
||||||
}
|
}
|
||||||
Expect(log).To(Equal(expectedLog))
|
Expect(scanLog).To(Equal(expectedLog))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Doesn't persist duplicate event logs", func() {
|
It("Doesn't persist duplicate event logs", func() {
|
||||||
// Perist once
|
// Perist once
|
||||||
err = dataStore.PersistContractEvents(con)
|
err = dataStore.PersistLog(*log, con.Address, con.Name)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
log := test_helpers.TransferLog{}
|
scanLog := test_helpers.TransferLog{}
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.Transfer", constants.TusdContractAddress)).StructScan(&log)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.transfer_event", constants.TusdContractAddress)).StructScan(&scanLog)
|
||||||
expectedLog := test_helpers.TransferLog{
|
expectedLog := test_helpers.TransferLog{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
VulvanizeLogId: vulcanizeLogId,
|
VulvanizeLogId: vulcanizeLogId,
|
||||||
@ -152,21 +150,21 @@ var _ = Describe("Repository", func() {
|
|||||||
Value: "1097077688018008265106216665536940668749033598146",
|
Value: "1097077688018008265106216665536940668749033598146",
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(log).To(Equal(expectedLog))
|
Expect(scanLog).To(Equal(expectedLog))
|
||||||
|
|
||||||
// Attempt to persist the same contract again
|
// Attempt to persist the same log again
|
||||||
err = dataStore.PersistContractEvents(con)
|
err = dataStore.PersistLog(*log, con.Address, con.Name)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
// Show that no new logs were entered
|
// Show that no new logs were entered
|
||||||
var count int
|
var count int
|
||||||
err = db.Get(&count, fmt.Sprintf("SELECT COUNT(*) FROM c%s.Transfer", constants.TusdContractAddress))
|
err = db.Get(&count, fmt.Sprintf("SELECT COUNT(*) FROM c%s.transfer_event", constants.TusdContractAddress))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(count).To(Equal(1))
|
Expect(count).To(Equal(1))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Fails with empty contract", func() {
|
It("Fails with empty log", func() {
|
||||||
err = dataStore.PersistContractEvents(&contract.Contract{})
|
err = dataStore.PersistLog(types.Log{}, con.Address, con.Name)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -17,18 +17,18 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/contract"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MethodDatastore interface {
|
type MethodDatastore interface {
|
||||||
PersistContractMethods(con *contract.Contract) error
|
PersistResult(method types.Result, contractAddr, contractName string) error
|
||||||
PersistResults(methodName string, con *contract.Contract) error
|
CreateMethodTable(contractAddr string, method types.Result) (bool, error)
|
||||||
CreateMethodTable(contractName string, method *types.Method) error
|
CreateContractSchema(contractAddr string) (bool, error)
|
||||||
CreateContractSchema(contractName string) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type methodDatastore struct {
|
type methodDatastore struct {
|
||||||
@ -42,68 +42,96 @@ func NewMethodDatastore(db *postgres.DB) *methodDatastore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *methodDatastore) PersistContractMethods(con *contract.Contract) error {
|
func (d *methodDatastore) PersistResult(method types.Result, contractAddr, contractName string) error {
|
||||||
err := d.CreateContractSchema(con.Name)
|
if len(method.Args) != len(method.Inputs) {
|
||||||
|
return errors.New("error: given number of inputs does not match number of method arguments")
|
||||||
|
}
|
||||||
|
if len(method.Return) != 1 {
|
||||||
|
return errors.New("error: given number of outputs does not match number of method return values")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := d.CreateContractSchema(contractAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, method := range con.Methods {
|
_, err = d.CreateMethodTable(contractAddr, method)
|
||||||
err = d.CreateMethodTable(con.Name, method)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Persist method data
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return d.persistResult(method, contractAddr, contractName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a custom postgres command to persist logs for the given event
|
// Creates a custom postgres command to persist logs for the given event
|
||||||
func (d *methodDatastore) PersistResults(methodName string, con *contract.Contract) error {
|
func (d *methodDatastore) persistResult(method types.Result, contractAddr, contractName string) error {
|
||||||
for _, result := range con.Methods[methodName].Results {
|
// Begin postgres string
|
||||||
println(result)
|
pgStr := fmt.Sprintf("INSERT INTO c%s.%s_method ", strings.ToLower(contractAddr), strings.ToLower(method.Name))
|
||||||
//TODO: Persist result data
|
pgStr = pgStr + "(token_name, block"
|
||||||
|
|
||||||
|
// Pack the corresponding variables in a slice
|
||||||
|
var data []interface{}
|
||||||
|
data = append(data,
|
||||||
|
contractName,
|
||||||
|
method.Block)
|
||||||
|
|
||||||
|
// Iterate over method args and return value, adding names
|
||||||
|
// to the string and pushing values to the slice
|
||||||
|
counter := 0 // Keep track of number of inputs
|
||||||
|
for i, arg := range method.Args {
|
||||||
|
counter += 1
|
||||||
|
pgStr = pgStr + fmt.Sprintf(", %s_", strings.ToLower(arg.Name)) // Add underscore after to avoid any collisions with reserved pg words
|
||||||
|
data = append(data, method.Inputs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
pgStr = pgStr + ", returned) VALUES ($1, $2"
|
||||||
|
data = append(data, method.Output)
|
||||||
|
|
||||||
|
// For each input entry we created we add its postgres command variable to the string
|
||||||
|
for i := 0; i < counter; i++ {
|
||||||
|
pgStr = pgStr + fmt.Sprintf(", $%d", i+3)
|
||||||
|
}
|
||||||
|
pgStr = pgStr + ")"
|
||||||
|
|
||||||
|
_, err := d.DB.Exec(pgStr, data...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for event table and creates it if it does not already exist
|
// Checks for event table and creates it if it does not already exist
|
||||||
func (d *methodDatastore) CreateMethodTable(contractAddr string, method *types.Method) error {
|
func (d *methodDatastore) CreateMethodTable(contractAddr string, method types.Result) (bool, error) {
|
||||||
tableExists, err := d.checkForTable(contractAddr, method.Name)
|
tableExists, err := d.checkForTable(contractAddr, method.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tableExists {
|
if !tableExists {
|
||||||
err = d.newMethodTable(contractAddr, method)
|
err = d.newMethodTable(contractAddr, method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return !tableExists, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a table for the given contract and event
|
// Creates a table for the given contract and event
|
||||||
func (d *methodDatastore) newMethodTable(contractAddr string, method *types.Method) error {
|
func (d *methodDatastore) newMethodTable(contractAddr string, method types.Result) error {
|
||||||
// Begin pg string
|
// Begin pg string
|
||||||
pgStr := fmt.Sprintf("CREATE TABLE IF NOT EXISTS _%s.%s ", strings.ToLower(contractAddr), strings.ToLower(method.Name))
|
pgStr := fmt.Sprintf("CREATE TABLE IF NOT EXISTS c%s.%s_method ", strings.ToLower(contractAddr), strings.ToLower(method.Name))
|
||||||
pgStr = pgStr + "(id SERIAL, token_name CHARACTER VARYING(66) NOT NULL, block INTEGER NOT NULL,"
|
pgStr = pgStr + "(id SERIAL, token_name CHARACTER VARYING(66) NOT NULL, block INTEGER NOT NULL,"
|
||||||
|
|
||||||
// Iterate over method inputs and outputs, using their name and pgType to grow the string
|
// Iterate over method inputs and outputs, using their name and pgType to grow the string
|
||||||
for _, input := range method.Inputs {
|
for _, arg := range method.Args {
|
||||||
pgStr = pgStr + fmt.Sprintf("%s_ %s NOT NULL,", strings.ToLower(input.Name), input.PgType)
|
pgStr = pgStr + fmt.Sprintf(" %s_ %s NOT NULL,", strings.ToLower(arg.Name), arg.PgType)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, output := range method.Outputs {
|
pgStr = pgStr + fmt.Sprintf(" returned %s NOT NULL)", method.Return[0].PgType)
|
||||||
pgStr = pgStr + fmt.Sprintf(" %s_ %s NOT NULL,", strings.ToLower(output.Name), output.PgType)
|
|
||||||
}
|
|
||||||
|
|
||||||
pgStr = pgStr[:len(pgStr)-1] + ")"
|
|
||||||
_, err := d.DB.Exec(pgStr)
|
_, err := d.DB.Exec(pgStr)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -111,7 +139,7 @@ func (d *methodDatastore) newMethodTable(contractAddr string, method *types.Meth
|
|||||||
|
|
||||||
// Checks if a table already exists for the given contract and event
|
// Checks if a table already exists for the given contract and event
|
||||||
func (d *methodDatastore) checkForTable(contractAddr string, methodName string) (bool, error) {
|
func (d *methodDatastore) checkForTable(contractAddr string, methodName string) (bool, error) {
|
||||||
pgStr := fmt.Sprintf("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = '_%s' AND table_name = '%s')", strings.ToLower(contractAddr), strings.ToLower(methodName))
|
pgStr := fmt.Sprintf("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'c%s' AND table_name = '%s_method')", strings.ToLower(contractAddr), strings.ToLower(methodName))
|
||||||
var exists bool
|
var exists bool
|
||||||
err := d.DB.Get(&exists, pgStr)
|
err := d.DB.Get(&exists, pgStr)
|
||||||
|
|
||||||
@ -119,32 +147,36 @@ func (d *methodDatastore) checkForTable(contractAddr string, methodName string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checks for contract schema and creates it if it does not already exist
|
// Checks for contract schema and creates it if it does not already exist
|
||||||
func (d *methodDatastore) CreateContractSchema(contractName string) error {
|
func (d *methodDatastore) CreateContractSchema(contractAddr string) (bool, error) {
|
||||||
schemaExists, err := d.checkForSchema(contractName)
|
if contractAddr == "" {
|
||||||
|
return false, errors.New("error: no contract address specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaExists, err := d.checkForSchema(contractAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !schemaExists {
|
if !schemaExists {
|
||||||
err = d.newContractSchema(contractName)
|
err = d.newContractSchema(contractAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return !schemaExists, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a schema for the given contract
|
// Creates a schema for the given contract
|
||||||
func (d *methodDatastore) newContractSchema(contractAddr string) error {
|
func (d *methodDatastore) newContractSchema(contractAddr string) error {
|
||||||
_, err := d.DB.Exec("CREATE SCHEMA IF NOT EXISTS _" + strings.ToLower(contractAddr))
|
_, err := d.DB.Exec("CREATE SCHEMA IF NOT EXISTS c" + strings.ToLower(contractAddr))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a schema already exists for the given contract
|
// Checks if a schema already exists for the given contract
|
||||||
func (d *methodDatastore) checkForSchema(contractAddr string) (bool, error) {
|
func (d *methodDatastore) checkForSchema(contractAddr string) (bool, error) {
|
||||||
pgStr := fmt.Sprintf("SELECT EXISTS (SELECT schema_name FROM information_schema.schemata WHERE schema_name = '_%s')", strings.ToLower(contractAddr))
|
pgStr := fmt.Sprintf("SELECT EXISTS (SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'c%s')", strings.ToLower(contractAddr))
|
||||||
|
|
||||||
var exists bool
|
var exists bool
|
||||||
err := d.DB.Get(&exists, pgStr)
|
err := d.DB.Get(&exists, pgStr)
|
||||||
|
@ -14,4 +14,98 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package repository
|
package repository_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/constants"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/contract"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/helpers/test_helpers"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/repository"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Repository", func() {
|
||||||
|
var db *postgres.DB
|
||||||
|
var dataStore repository.MethodDatastore
|
||||||
|
var con *contract.Contract
|
||||||
|
var err error
|
||||||
|
var mockResult types.Result
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
con = test_helpers.SetupTusdContract([]string{}, []string{"balanceOf"})
|
||||||
|
method := con.Methods["balanceOf"]
|
||||||
|
mockResult = types.Result{
|
||||||
|
Method: method,
|
||||||
|
PgType: method.Return[0].PgType,
|
||||||
|
Inputs: make([]interface{}, 1),
|
||||||
|
Output: new(interface{}),
|
||||||
|
Block: 6707323,
|
||||||
|
}
|
||||||
|
mockResult.Inputs[0] = "0xfE9e8709d3215310075d67E3ed32A380CCf451C8"
|
||||||
|
mockResult.Output = "66386309548896882859581786"
|
||||||
|
db, _ = test_helpers.SetupDBandBC()
|
||||||
|
dataStore = repository.NewMethodDatastore(db)
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
test_helpers.TearDown(db)
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("CreateContractSchema", func() {
|
||||||
|
It("Creates schema if it doesn't exist", func() {
|
||||||
|
created, err := dataStore.CreateContractSchema(constants.TusdContractAddress)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(created).To(Equal(true))
|
||||||
|
|
||||||
|
created, err = dataStore.CreateContractSchema(constants.TusdContractAddress)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(created).To(Equal(false))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("CreateMethodTable", func() {
|
||||||
|
It("Creates table if it doesn't exist", func() {
|
||||||
|
created, err := dataStore.CreateContractSchema(constants.TusdContractAddress)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(created).To(Equal(true))
|
||||||
|
|
||||||
|
created, err = dataStore.CreateMethodTable(constants.TusdContractAddress, mockResult)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(created).To(Equal(true))
|
||||||
|
|
||||||
|
created, err = dataStore.CreateMethodTable(constants.TusdContractAddress, mockResult)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(created).To(Equal(false))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("PersistResult", func() {
|
||||||
|
It("Persists result from method polling in custom pg table", func() {
|
||||||
|
err = dataStore.PersistResult(mockResult, con.Address, con.Name)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
scanStruct := test_helpers.BalanceOf{}
|
||||||
|
|
||||||
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.balanceof_method", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
|
expectedLog := test_helpers.BalanceOf{
|
||||||
|
Id: 1,
|
||||||
|
TokenName: "TrueUSD",
|
||||||
|
Block: 6707323,
|
||||||
|
Address: "0xfE9e8709d3215310075d67E3ed32A380CCf451C8",
|
||||||
|
Balance: "66386309548896882859581786",
|
||||||
|
}
|
||||||
|
Expect(scanStruct).To(Equal(expectedLog))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Fails with empty result", func() {
|
||||||
|
err = dataStore.PersistResult(types.Result{}, con.Address, con.Name)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -85,7 +85,7 @@ func (r *addressRetriever) retrieveTransferAddresses(con contract.Contract) ([]s
|
|||||||
|
|
||||||
if field.Type.T == abi.AddressTy { // If they have address type, retrieve those addresses
|
if field.Type.T == abi.AddressTy { // If they have address type, retrieve those addresses
|
||||||
addrs := make([]string, 0)
|
addrs := make([]string, 0)
|
||||||
pgStr := fmt.Sprintf("SELECT %s_ FROM c%s.%s", strings.ToLower(field.Name), strings.ToLower(con.Address), strings.ToLower(event.Name))
|
pgStr := fmt.Sprintf("SELECT %s_ FROM c%s.%s_event", strings.ToLower(field.Name), strings.ToLower(con.Address), strings.ToLower(event.Name))
|
||||||
err := r.DB.Select(&addrs, pgStr)
|
err := r.DB.Select(&addrs, pgStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, err
|
return []string{}, err
|
||||||
@ -106,7 +106,7 @@ func (r *addressRetriever) retrieveTokenMintees(con contract.Contract) ([]string
|
|||||||
|
|
||||||
if field.Type.T == abi.AddressTy { // If they have address type, retrieve those addresses
|
if field.Type.T == abi.AddressTy { // If they have address type, retrieve those addresses
|
||||||
addrs := make([]string, 0)
|
addrs := make([]string, 0)
|
||||||
pgStr := fmt.Sprintf("SELECT %s_ FROM c%s.%s", strings.ToLower(field.Name), strings.ToLower(con.Address), strings.ToLower(event.Name))
|
pgStr := fmt.Sprintf("SELECT %s_ FROM c%s.%s_event", strings.ToLower(field.Name), strings.ToLower(con.Address), strings.ToLower(event.Name))
|
||||||
err := r.DB.Select(&addrs, pgStr)
|
err := r.DB.Select(&addrs, pgStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, err
|
return []string{}, err
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/omni/types"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
@ -50,6 +51,7 @@ var _ = Describe("Address Retriever Test", func() {
|
|||||||
var err error
|
var err error
|
||||||
var info *contract.Contract
|
var info *contract.Contract
|
||||||
var vulcanizeLogId int64
|
var vulcanizeLogId int64
|
||||||
|
var log *types.Log
|
||||||
var r retriever.AddressRetriever
|
var r retriever.AddressRetriever
|
||||||
var addresses map[common.Address]bool
|
var addresses map[common.Address]bool
|
||||||
var wantedEvents = []string{"Transfer"}
|
var wantedEvents = []string{"Transfer"}
|
||||||
@ -63,11 +65,11 @@ var _ = Describe("Address Retriever Test", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
c := converter.NewConverter(info)
|
c := converter.NewConverter(info)
|
||||||
err = c.Convert(mockEvent, event)
|
log, err = c.Convert(mockEvent, event)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
dataStore = repository.NewEventDataStore(db)
|
dataStore = repository.NewEventDataStore(db)
|
||||||
dataStore.PersistContractEvents(info)
|
dataStore.PersistLog(*log, info.Address, info.Name)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
r = retriever.NewAddressRetriever(db)
|
r = retriever.NewAddressRetriever(db)
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package transformer_test
|
|
@ -49,19 +49,21 @@ type Transformer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type transformer struct {
|
type transformer struct {
|
||||||
Network string // Ethereum network name; default/"" is mainnet
|
|
||||||
|
|
||||||
// Database interfaces
|
// Database interfaces
|
||||||
datastore.FilterRepository // Holds watched event log filters
|
datastore.FilterRepository // Log filters repo; accepts filters generated by Contract.GenerateFilters()
|
||||||
datastore.WatchedEventRepository // Holds watched event log views, created with the filters
|
datastore.WatchedEventRepository // Watched event log views, created by the log filters
|
||||||
repository.EventDatastore // Holds transformed watched event log data
|
repository.EventDatastore // Holds transformed watched event log data
|
||||||
|
|
||||||
|
// Pre-processing interfaces
|
||||||
|
parser.Parser // Parses events and methods out of contract abi fetched using contract address
|
||||||
|
retriever.BlockRetriever // Retrieves first block for contract and current block height
|
||||||
|
|
||||||
// Processing interfaces
|
// Processing interfaces
|
||||||
parser.Parser // Parses events out of contract abi fetched with addr
|
converter.Converter // Converts watched event logs into custom log
|
||||||
retriever.BlockRetriever // Retrieves first block with contract addr referenced
|
poller.Poller // Polls methods using contract's token holder addresses and persists them using method datastore
|
||||||
retriever.AddressRetriever // Retrieves token holder addresses
|
|
||||||
converter.Converter // Converts watched event logs into custom log
|
// Ethereum network name; default "" is mainnet
|
||||||
poller.Poller // Polls methods using contract's token holder addresses
|
Network string
|
||||||
|
|
||||||
// Store contract info as mapping to contract address
|
// Store contract info as mapping to contract address
|
||||||
Contracts map[string]*contract.Contract
|
Contracts map[string]*contract.Contract
|
||||||
@ -84,7 +86,7 @@ type transformer struct {
|
|||||||
func NewTransformer(network string, BC core.BlockChain, DB *postgres.DB) *transformer {
|
func NewTransformer(network string, BC core.BlockChain, DB *postgres.DB) *transformer {
|
||||||
|
|
||||||
return &transformer{
|
return &transformer{
|
||||||
Poller: poller.NewPoller(BC),
|
Poller: poller.NewPoller(BC, DB),
|
||||||
Parser: parser.NewParser(network),
|
Parser: parser.NewParser(network),
|
||||||
BlockRetriever: retriever.NewBlockRetriever(DB),
|
BlockRetriever: retriever.NewBlockRetriever(DB),
|
||||||
Converter: converter.NewConverter(&contract.Contract{}),
|
Converter: converter.NewConverter(&contract.Contract{}),
|
||||||
@ -133,7 +135,7 @@ func (t *transformer) Init() error {
|
|||||||
|
|
||||||
// Get contract name
|
// Get contract name
|
||||||
var name = new(string)
|
var name = new(string)
|
||||||
err = t.PollMethod(t.Abi(), contractAddr, "name", nil, &name, lastBlock)
|
err = t.FetchContractData(t.Abi(), contractAddr, "name", nil, &name, lastBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("unable to fetch contract name: %v\r\n", err))
|
return errors.New(fmt.Sprintf("unable to fetch contract name: %v\r\n", err))
|
||||||
}
|
}
|
||||||
@ -169,22 +171,27 @@ func (t *transformer) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over filters and push them to the repo
|
// Iterate over filters and push them to the repo using filter repository interface
|
||||||
for _, filter := range info.Filters {
|
for _, filter := range info.Filters {
|
||||||
t.CreateFilter(filter)
|
t.CreateFilter(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store contract info for further processing
|
||||||
t.Contracts[contractAddr] = info
|
t.Contracts[contractAddr] = info
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through contracts, updating the
|
// Iterates through stored, initialized contract objects
|
||||||
// converter with each one and using it to
|
// Iterates through contract's event filters, grabbing watched event logs
|
||||||
// convert watched event logs.
|
// Uses converter to convert logs into custom log type
|
||||||
// Then persist them into the postgres db
|
// Persists converted logs into custuom postgres tables
|
||||||
|
// Calls selected methods, using token holder address generated during event log conversion
|
||||||
func (tr transformer) Execute() error {
|
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
|
// Iterate through all internal contracts
|
||||||
for _, con := range tr.Contracts {
|
for _, con := range tr.Contracts {
|
||||||
|
|
||||||
@ -199,20 +206,28 @@ func (tr transformer) Execute() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over watched event logs and convert them
|
// Iterate over watched event logs
|
||||||
for _, we := range watchedEvents {
|
for _, we := range watchedEvents {
|
||||||
err = tr.Converter.Convert(*we, con.Events[eventName])
|
// Convert them to our custom log type
|
||||||
|
log, err := tr.Converter.Convert(*we, con.Events[eventName])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// And immediately persist converted logs in repo
|
||||||
|
// Run this in seperate goroutine?
|
||||||
|
err = tr.PersistLog(*log, con.Address, con.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// After converting all logs for events of interest, persist all of the data
|
// After persisting all watched event logs
|
||||||
// Change this so that event logs are persisted immediately after being created
|
// poller polls select contract methods
|
||||||
// So as to prevent a huge growth of data in the contract memory
|
// and persists the results into custom pg tables
|
||||||
err := tr.PersistContractEvents(con)
|
// Run this in seperate goroutine?
|
||||||
if err != nil {
|
if err := tr.PollContract(*con); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,14 +34,14 @@ import (
|
|||||||
|
|
||||||
var block1 = core.Block{
|
var block1 = core.Block{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad123ert",
|
||||||
Number: 5194634,
|
Number: 6194633,
|
||||||
Transactions: []core.Transaction{{
|
Transactions: []core.Transaction{{
|
||||||
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
Hash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
||||||
Receipt: core.Receipt{
|
Receipt: core.Receipt{
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
||||||
ContractAddress: "",
|
ContractAddress: "",
|
||||||
Logs: []core.Log{{
|
Logs: []core.Log{{
|
||||||
BlockNumber: 5194634,
|
BlockNumber: 6194633,
|
||||||
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654aaa",
|
||||||
Address: constants.TusdContractAddress,
|
Address: constants.TusdContractAddress,
|
||||||
Topics: core.Topics{
|
Topics: core.Topics{
|
||||||
@ -155,7 +155,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
c, ok := t.Contracts[constants.TusdContractAddress]
|
c, ok := t.Contracts[constants.TusdContractAddress]
|
||||||
Expect(ok).To(Equal(true))
|
Expect(ok).To(Equal(true))
|
||||||
|
|
||||||
Expect(c.StartingBlock).To(Equal(int64(5194634)))
|
Expect(c.StartingBlock).To(Equal(int64(6194633)))
|
||||||
Expect(c.LastBlock).To(Equal(int64(6194634)))
|
Expect(c.LastBlock).To(Equal(int64(6194634)))
|
||||||
Expect(c.Abi).To(Equal(constants.TusdAbiString))
|
Expect(c.Abi).To(Equal(constants.TusdAbiString))
|
||||||
Expect(c.Name).To(Equal("TrueUSD"))
|
Expect(c.Name).To(Equal("TrueUSD"))
|
||||||
@ -190,6 +190,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
It("Transforms watched contract data into custom repositories", func() {
|
It("Transforms watched contract data into custom repositories", func() {
|
||||||
t := transformer.NewTransformer("", blockChain, db)
|
t := transformer.NewTransformer("", blockChain, db)
|
||||||
t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
|
t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
|
||||||
|
t.SetMethods(constants.TusdContractAddress, nil)
|
||||||
err = t.Init()
|
err = t.Init()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
@ -197,7 +198,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
log := test_helpers.TransferLog{}
|
log := test_helpers.TransferLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.transfer WHERE block = 6194634", constants.TusdContractAddress)).StructScan(&log)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM c%s.transfer_event WHERE block = 6194634", constants.TusdContractAddress)).StructScan(&log)
|
||||||
|
|
||||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||||
Expect(log.Tx).To(Equal("0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee"))
|
Expect(log.Tx).To(Equal("0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee"))
|
||||||
@ -210,6 +211,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
It("Keeps track of contract-related addresses while transforming event data", func() {
|
It("Keeps track of contract-related addresses while transforming event data", func() {
|
||||||
t := transformer.NewTransformer("", blockChain, db)
|
t := transformer.NewTransformer("", blockChain, db)
|
||||||
t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
|
t.SetEvents(constants.TusdContractAddress, []string{"Transfer"})
|
||||||
|
t.SetMethods(constants.TusdContractAddress, nil)
|
||||||
err = t.Init()
|
err = t.Init()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
@ -233,5 +235,35 @@ var _ = Describe("Transformer", func() {
|
|||||||
_, ok = c.TknHolderAddrs["0x"]
|
_, ok = c.TknHolderAddrs["0x"]
|
||||||
Expect(ok).To(Equal(false))
|
Expect(ok).To(Equal(false))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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"})
|
||||||
|
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 c%s.balanceof_method WHERE who_ = '0x000000000000000000000000000000000000Af21' AND block = '6194634'", constants.TusdContractAddress)).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 c%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6194634'", constants.TusdContractAddress)).StructScan(&res)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
err = t.Execute()
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -26,16 +26,14 @@ import (
|
|||||||
type Event struct {
|
type Event struct {
|
||||||
Name string
|
Name string
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
Fields []*Field
|
Fields []Field
|
||||||
Logs map[int64]Log // Map of VulcanizeIdLog to parsed event log
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Method struct {
|
type Method struct {
|
||||||
Name string
|
Name string
|
||||||
Const bool
|
Const bool
|
||||||
Inputs []*Field
|
Args []Field
|
||||||
Outputs []*Field
|
Return []Field
|
||||||
Results []*Result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Field struct {
|
type Field struct {
|
||||||
@ -43,15 +41,18 @@ type Field struct {
|
|||||||
PgType string // Holds type used when committing data held in this field to postgres
|
PgType string // Holds type used when committing data held in this field to postgres
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct to hold results from method call with given inputs across different blocks
|
// Struct to hold instance of result from method call with given inputs and block
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Inputs []interface{} // Will only use addresses
|
Method
|
||||||
Outputs map[int64]interface{}
|
Inputs []interface{} // Will only use addresses
|
||||||
PgType string // Holds output pg type
|
Output interface{}
|
||||||
|
PgType string // Holds output pg type
|
||||||
|
Block int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct to hold event log data data
|
// Struct to hold instance of an event log data
|
||||||
type Log struct {
|
type Log struct {
|
||||||
|
Event
|
||||||
Id int64 // VulcanizeIdLog
|
Id int64 // VulcanizeIdLog
|
||||||
Values map[string]string // Map of event input names to their values
|
Values map[string]string // Map of event input names to their values
|
||||||
Block int64
|
Block int64
|
||||||
@ -59,10 +60,10 @@ type Log struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unpack abi.Event into our custom Event struct
|
// Unpack abi.Event into our custom Event struct
|
||||||
func NewEvent(e abi.Event) *Event {
|
func NewEvent(e abi.Event) Event {
|
||||||
fields := make([]*Field, len(e.Inputs))
|
fields := make([]Field, len(e.Inputs))
|
||||||
for i, input := range e.Inputs {
|
for i, input := range e.Inputs {
|
||||||
fields[i] = &Field{}
|
fields[i] = Field{}
|
||||||
fields[i].Name = input.Name
|
fields[i].Name = input.Name
|
||||||
fields[i].Type = input.Type
|
fields[i].Type = input.Type
|
||||||
fields[i].Indexed = input.Indexed
|
fields[i].Indexed = input.Indexed
|
||||||
@ -87,19 +88,18 @@ func NewEvent(e abi.Event) *Event {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Event{
|
return Event{
|
||||||
Name: e.Name,
|
Name: e.Name,
|
||||||
Anonymous: e.Anonymous,
|
Anonymous: e.Anonymous,
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
Logs: map[int64]Log{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack abi.Method into our custom Method struct
|
// Unpack abi.Method into our custom Method struct
|
||||||
func NewMethod(m abi.Method) *Method {
|
func NewMethod(m abi.Method) Method {
|
||||||
inputs := make([]*Field, len(m.Inputs))
|
inputs := make([]Field, len(m.Inputs))
|
||||||
for i, input := range m.Inputs {
|
for i, input := range m.Inputs {
|
||||||
inputs[i] = &Field{}
|
inputs[i] = Field{}
|
||||||
inputs[i].Name = input.Name
|
inputs[i].Name = input.Name
|
||||||
inputs[i].Type = input.Type
|
inputs[i].Type = input.Type
|
||||||
inputs[i].Indexed = input.Indexed
|
inputs[i].Indexed = input.Indexed
|
||||||
@ -123,9 +123,9 @@ func NewMethod(m abi.Method) *Method {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs := make([]*Field, len(m.Outputs))
|
outputs := make([]Field, len(m.Outputs))
|
||||||
for i, output := range m.Outputs {
|
for i, output := range m.Outputs {
|
||||||
outputs[i] = &Field{}
|
outputs[i] = Field{}
|
||||||
outputs[i].Name = output.Name
|
outputs[i].Name = output.Name
|
||||||
outputs[i].Type = output.Type
|
outputs[i].Type = output.Type
|
||||||
outputs[i].Indexed = output.Indexed
|
outputs[i].Indexed = output.Indexed
|
||||||
@ -149,12 +149,11 @@ func NewMethod(m abi.Method) *Method {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Method{
|
return Method{
|
||||||
Name: m.Name,
|
Name: m.Name,
|
||||||
Const: m.Const,
|
Const: m.Const,
|
||||||
Inputs: inputs,
|
Args: inputs,
|
||||||
Outputs: outputs,
|
Return: outputs,
|
||||||
Results: make([]*Result, 0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,10 +168,10 @@ func (e Event) Sig() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m Method) Sig() string {
|
func (m Method) Sig() string {
|
||||||
types := make([]string, len(m.Inputs))
|
types := make([]string, len(m.Args))
|
||||||
i := 0
|
i := 0
|
||||||
for _, input := range m.Inputs {
|
for _, arg := range m.Args {
|
||||||
types[i] = input.Type.String()
|
types[i] = arg.Type.String()
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user