adjusted FetchContractData to work with variable number of contract method arguments, changed fetcher's FetchSupply method to more general FetchBigInt method and added FetchBool and FetchAddress methods

This commit is contained in:
Ian Norden 2018-08-09 11:58:06 -05:00
parent 93960e7c4e
commit ccd21f73c9
12 changed files with 313 additions and 96 deletions

View File

@ -19,45 +19,54 @@ import (
"log" "log"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/core"
) )
// Interface definition for a Fetcher
type ERC20FetcherInterface interface { type ERC20FetcherInterface interface {
FetchSupplyOf(contractAbi string, contractAddress string, blockNumber int64) (big.Int, error) FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error)
FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error)
FetchAddress(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Address, error)
GetBlockChain() core.BlockChain GetBlockChain() core.BlockChain
} }
func NewFetcher(blockchain core.BlockChain) Fetcher { // Initializes and returns a Fetcher with the given blockchain
func NewFetcher(blockChain core.BlockChain) Fetcher {
return Fetcher{ return Fetcher{
Blockchain: blockchain, BlockChain: blockChain,
} }
} }
// Fetcher struct
type Fetcher struct { type Fetcher struct {
Blockchain core.BlockChain BlockChain core.BlockChain
ContractAbi string ContractAbi string
ContractAddress string ContractAddress string
} }
// Fetcher error
type fetcherError struct { type fetcherError struct {
err string err string
fetchMethod string fetchMethod string
} }
// Fetcher error method
func (fe *fetcherError) Error() string { func (fe *fetcherError) Error() string {
return fmt.Sprintf("Error fetching %s: %s", fe.fetchMethod, fe.err) return fmt.Sprintf("Error fetching %s: %s", fe.fetchMethod, fe.err)
} }
// Used to create a new Fetcher error for a given error and fetch method
func newFetcherError(err error, fetchMethod string) *fetcherError { func newFetcherError(err error, fetchMethod string) *fetcherError {
e := fetcherError{err.Error(), fetchMethod} e := fetcherError{err.Error(), fetchMethod}
log.Println(e.Error()) log.Println(e.Error())
return &e return &e
} }
func (f Fetcher) FetchSupplyOf(contractAbi string, contractAddress string, blockNumber int64) (big.Int, error) { // Method used to fetch big.Int result from contract
method := "totalSupply" func (f Fetcher) FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error) {
var result = new(big.Int) var result = new(big.Int)
err := f.Blockchain.FetchContractData(contractAbi, contractAddress, method, nil, &result, blockNumber) err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber)
if err != nil { if err != nil {
return *result, newFetcherError(err, method) return *result, newFetcherError(err, method)
@ -66,6 +75,31 @@ func (f Fetcher) FetchSupplyOf(contractAbi string, contractAddress string, block
return *result, nil return *result, nil
} }
func (f Fetcher) GetBlockChain() core.BlockChain { // Method used to fetch bool result from contract
return f.Blockchain func (f Fetcher) FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error) {
var result = new(bool)
err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber)
if err != nil {
return *result, newFetcherError(err, method)
}
return *result, nil
}
// Method used to fetch address result from contract
func (f Fetcher) FetchAddress(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Address, error) {
var result = new(common.Address)
err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber)
if err != nil {
return *result, newFetcherError(err, method)
}
return *result, nil
}
// Getter method for Fetcher's blockchain
func (f Fetcher) GetBlockChain() core.BlockChain {
return f.BlockChain
} }

View File

@ -34,22 +34,23 @@ import (
var _ = Describe("ERC20 Fetcher", func() { var _ = Describe("ERC20 Fetcher", func() {
blockNumber := int64(5502914) blockNumber := int64(5502914)
Describe("FetchSupplyOf", func() { Describe("totalSupply", func() {
It("fetches data from the blockchain with the correct arguments", func() { It("fetches total supply data from the blockchain with the correct arguments", func() {
fakeBlockchain := fakes.NewMockBlockChain() fakeBlockChain := fakes.NewMockBlockChain()
testFetcher := every_block.NewFetcher(fakeBlockchain) testFetcher := every_block.NewFetcher(fakeBlockChain)
testAbi := "testAbi" testAbi := "testAbi"
testContractAddress := "testContractAddress" testContractAddress := "testContractAddress"
_, err := testFetcher.FetchSupplyOf(testAbi, testContractAddress, blockNumber)
_, err := testFetcher.FetchBigInt("totalSupply", testAbi, testContractAddress, blockNumber, nil)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
expectedResult := big.Int{} expectedResult := big.Int{}
expected := &expectedResult expected := &expectedResult
fakeBlockchain.AssertFetchContractDataCalledWith(testAbi, testContractAddress, "totalSupply", nil, &expected, blockNumber) fakeBlockChain.AssertFetchContractDataCalledWith(testAbi, testContractAddress, "totalSupply", nil, &expected, blockNumber)
}) })
It("fetches a token's total supply at the given block height", func() { It("fetches dai token's total supply at the given block height", func() {
infuraIPC := "https://mainnet.infura.io/J5Vd2fRtGsw0zZ0Ov3BL" infuraIPC := "https://mainnet.infura.io/v3/b09888c1113640cc9ab42750ce750c05"
rawRpcClient, err := rpc.Dial(infuraIPC) rawRpcClient, err := rpc.Dial(infuraIPC)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
rpcClient := client.NewRpcClient(rawRpcClient, infuraIPC) rpcClient := client.NewRpcClient(rawRpcClient, infuraIPC)
@ -59,7 +60,7 @@ var _ = Describe("ERC20 Fetcher", func() {
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient) transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
blockChain := geth.NewBlockChain(blockChainClient, node, transactionConverter) blockChain := geth.NewBlockChain(blockChainClient, node, transactionConverter)
realFetcher := every_block.NewFetcher(blockChain) realFetcher := every_block.NewFetcher(blockChain)
result, err := realFetcher.FetchSupplyOf(constants.DaiAbiString, constants.DaiContractAddress, blockNumber) result, err := realFetcher.FetchBigInt("totalSupply", constants.DaiAbiString, constants.DaiContractAddress, blockNumber, nil)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
expectedResult := big.Int{} expectedResult := big.Int{}
@ -71,7 +72,7 @@ var _ = Describe("ERC20 Fetcher", func() {
blockChain := fakes.NewMockBlockChain() blockChain := fakes.NewMockBlockChain()
blockChain.SetFetchContractDataErr(fakes.FakeError) blockChain.SetFetchContractDataErr(fakes.FakeError)
errorFetcher := every_block.NewFetcher(blockChain) errorFetcher := every_block.NewFetcher(blockChain)
result, err := errorFetcher.FetchSupplyOf("", "", 0) result, err := errorFetcher.FetchBigInt("totalSupply", "", "", 0, nil)
Expect(result.String()).To(Equal("0")) Expect(result.String()).To(Equal("0"))
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
@ -79,4 +80,4 @@ var _ = Describe("ERC20 Fetcher", func() {
Expect(err.Error()).To(ContainSubstring(fakes.FakeError.Error())) Expect(err.Error()).To(ContainSubstring(fakes.FakeError.Error()))
}) })
}) })
}) })

View File

@ -14,8 +14,26 @@
package every_block package every_block
// Struct to hold token supply data
type TokenSupply struct { type TokenSupply struct {
Value string Value string
TokenAddress string TokenAddress string
BlockNumber int64 BlockNumber int64
} }
// Struct to hold token holder address balance data
type TokenBalance struct {
Value string
TokenAddress string
BlockNumber int64
TokenHolderAddress string
}
// Struct to hold token allowance data
type TokenAllowance struct {
Value string
TokenAddress string
BlockNumber int64
TokenHolderAddress string
TokenSpenderAddress string
}

View File

@ -20,38 +20,46 @@ import (
"log" "log"
) )
// Interface definition for a generic ERC20 token repository
type ERC20RepositoryInterface interface { type ERC20RepositoryInterface interface {
Create(supply TokenSupply) error CreateSupply(supply TokenSupply) error
MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error)
} }
type TokenSupplyRepository struct { // Generic ERC20 token Repo struct
type ERC20TokenRepository struct {
*postgres.DB *postgres.DB
} }
// Repo error
type repositoryError struct { type repositoryError struct {
err string err string
msg string msg string
blockNumber int64 blockNumber int64
} }
// Repo error method
func (re *repositoryError) Error() string { func (re *repositoryError) Error() string {
return fmt.Sprintf(re.msg, re.blockNumber, re.err) return fmt.Sprintf(re.msg, re.blockNumber, re.err)
} }
// Used to create a new Repo error for a given error and fetch method
func newRepositoryError(err error, msg string, blockNumber int64) error { func newRepositoryError(err error, msg string, blockNumber int64) error {
e := repositoryError{err.Error(), msg, blockNumber} e := repositoryError{err.Error(), msg, blockNumber}
log.Println(e.Error()) log.Println(e.Error())
return &e return &e
} }
// Constant error definitions
const ( const (
GetBlockError = "Error fetching block number %d: %s" GetBlockError = "Error fetching block number %d: %s"
InsertTokenSupplyError = "Error inserting token_supply for block number %d: %s" InsertTokenSupplyError = "Error inserting token_supply for block number %d: %s"
MissingBlockError = "Error finding missing token_supply records starting at block %d: %s" MissingBlockError = "Error finding missing token_supply records starting at block %d: %s"
) )
func (tsp *TokenSupplyRepository) Create(supply TokenSupply) error { // Supply methods
// This method inserts the supply for a given token contract address at a given block height into the token_supply table
func (tsp *ERC20TokenRepository) CreateSupply(supply TokenSupply) error {
var blockId int var blockId int
err := tsp.DB.Get(&blockId, `SELECT id FROM blocks WHERE number = $1 AND eth_node_id = $2`, supply.BlockNumber, tsp.NodeID) err := tsp.DB.Get(&blockId, `SELECT id FROM blocks WHERE number = $1 AND eth_node_id = $2`, supply.BlockNumber, tsp.NodeID)
if err != nil { if err != nil {
@ -68,7 +76,8 @@ func (tsp *TokenSupplyRepository) Create(supply TokenSupply) error {
return nil return nil
} }
func (tsp *TokenSupplyRepository) MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) { // This method returns an array of blocks that are missing a token_supply entry for a given tokenAddress
func (tsp *ERC20TokenRepository) MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) {
blockNumbers := make([]int64, 0) blockNumbers := make([]int64, 0)
err := tsp.DB.Select( err := tsp.DB.Select(
@ -88,4 +97,4 @@ func (tsp *TokenSupplyRepository) MissingBlocks(startingBlock int64, highestBloc
return []int64{}, newRepositoryError(err, MissingBlockError, startingBlock) return []int64{}, newRepositoryError(err, MissingBlockError, startingBlock)
} }
return blockNumbers, err return blockNumbers, err
} }

View File

@ -31,13 +31,13 @@ var _ = Describe("ERC20 Token Repository", func() {
var db *postgres.DB var db *postgres.DB
var blockId int64 var blockId int64
var blockNumber int64 var blockNumber int64
var repository every_block.TokenSupplyRepository var repository every_block.ERC20TokenRepository
var blockRepository repositories.BlockRepository var blockRepository repositories.BlockRepository
testAddress := "abc" testAddress := "abc"
BeforeEach(func() { BeforeEach(func() {
db = test_helpers.CreateNewDatabase() db = test_helpers.CreateNewDatabase()
repository = every_block.TokenSupplyRepository{DB: db} repository = every_block.ERC20TokenRepository{DB: db}
_, err := db.Query(`DELETE FROM token_supply`) _, err := db.Query(`DELETE FROM token_supply`)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -49,7 +49,7 @@ var _ = Describe("ERC20 Token Repository", func() {
Describe("Create", func() { Describe("Create", func() {
It("creates a token supply record", func() { It("creates a token supply record", func() {
supply := supplyModel(blockNumber, testAddress, "100") supply := supplyModel(blockNumber, testAddress, "100")
err := repository.Create(supply) err := repository.CreateSupply(supply)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
dbResult := test_helpers.TokenSupplyDBRow{} dbResult := test_helpers.TokenSupplyDBRow{}
@ -73,7 +73,7 @@ var _ = Describe("ERC20 Token Repository", func() {
It("returns an error if fetching the block's id from the database fails", func() { It("returns an error if fetching the block's id from the database fails", func() {
errorSupply := supplyModel(-1, "", "") errorSupply := supplyModel(-1, "", "")
err := repository.Create(errorSupply) err := repository.CreateSupply(errorSupply)
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("sql")) Expect(err.Error()).To(ContainSubstring("sql"))
@ -82,7 +82,7 @@ var _ = Describe("ERC20 Token Repository", func() {
It("returns an error if inserting the token_supply fails", func() { It("returns an error if inserting the token_supply fails", func() {
errorSupply := supplyModel(blockNumber, "", "") errorSupply := supplyModel(blockNumber, "", "")
err := repository.Create(errorSupply) err := repository.CreateSupply(errorSupply)
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("pq")) Expect(err.Error()).To(ContainSubstring("pq"))
@ -94,7 +94,7 @@ var _ = Describe("ERC20 Token Repository", func() {
var node2DB *postgres.DB var node2DB *postgres.DB
var node2BlockRepo *repositories.BlockRepository var node2BlockRepo *repositories.BlockRepository
var node2BlockId int64 var node2BlockId int64
var node2TokenSupplyRepo every_block.TokenSupplyRepository var node2TokenSupplyRepo every_block.ERC20TokenRepository
var tokenSupply every_block.TokenSupply var tokenSupply every_block.TokenSupply
BeforeEach(func() { BeforeEach(func() {
@ -105,11 +105,11 @@ var _ = Describe("ERC20 Token Repository", func() {
node2BlockId = test_helpers.CreateBlock(blockNumber, *node2BlockRepo) node2BlockId = test_helpers.CreateBlock(blockNumber, *node2BlockRepo)
tokenSupply = supplyModel(blockNumber, "abc", "100") tokenSupply = supplyModel(blockNumber, "abc", "100")
node2TokenSupplyRepo = every_block.TokenSupplyRepository{DB: node2DB} node2TokenSupplyRepo = every_block.ERC20TokenRepository{DB: node2DB}
}) })
It("only creates token_supply records for the current node (node2)", func() { It("only creates token_supply records for the current node (node2)", func() {
err := node2TokenSupplyRepo.Create(tokenSupply) err := node2TokenSupplyRepo.CreateSupply(tokenSupply)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
var tokenSupplies []test_helpers.TokenSupplyDBRow var tokenSupplies []test_helpers.TokenSupplyDBRow
@ -121,14 +121,14 @@ var _ = Describe("ERC20 Token Repository", func() {
It("only includes missing block numbers for the current node", func() { It("only includes missing block numbers for the current node", func() {
//create token_supply on original node //create token_supply on original node
err := repository.Create(tokenSupply) err := repository.CreateSupply(tokenSupply)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
originalNodeMissingBlocks, err := repository.MissingBlocks(blockNumber, blockNumber) originalNodeMissingBlocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(len(originalNodeMissingBlocks)).To(Equal(0)) Expect(len(originalNodeMissingBlocks)).To(Equal(0))
node2MissingBlocks, err := node2TokenSupplyRepo.MissingBlocks(blockNumber, blockNumber) node2MissingBlocks, err := node2TokenSupplyRepo.MissingSupplyBlocks(blockNumber, blockNumber, testAddress)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(len(node2MissingBlocks)).To(Equal(1)) Expect(len(node2MissingBlocks)).To(Equal(1))
}) })
@ -140,7 +140,7 @@ var _ = Describe("ERC20 Token Repository", func() {
newBlockNumber := blockNumber + 1 newBlockNumber := blockNumber + 1
test_helpers.CreateBlock(newBlockNumber, blockRepository) test_helpers.CreateBlock(newBlockNumber, blockRepository)
blocks, err := repository.MissingBlocks(blockNumber, newBlockNumber) blocks, err := repository.MissingSupplyBlocks(blockNumber, newBlockNumber, testAddress)
Expect(blocks).To(ConsistOf(newBlockNumber)) Expect(blocks).To(ConsistOf(newBlockNumber))
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -149,7 +149,7 @@ var _ = Describe("ERC20 Token Repository", func() {
It("only returns blocks within the given range", func() { It("only returns blocks within the given range", func() {
newBlockNumber := blockNumber + 1 newBlockNumber := blockNumber + 1
test_helpers.CreateBlock(newBlockNumber, blockRepository) test_helpers.CreateBlock(newBlockNumber, blockRepository)
blocks, err := repository.MissingBlocks(blockNumber, blockNumber) blocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress)
Expect(blocks).NotTo(ConsistOf(newBlockNumber)) Expect(blocks).NotTo(ConsistOf(newBlockNumber))
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -157,7 +157,7 @@ var _ = Describe("ERC20 Token Repository", func() {
It("does not return numbers that already have an associated TokenSupply record", func() { It("does not return numbers that already have an associated TokenSupply record", func() {
createTokenSupplyFor(repository, blockNumber) createTokenSupplyFor(repository, blockNumber)
blocks, err := repository.MissingBlocks(blockNumber, blockNumber) blocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress)
Expect(blocks).To(BeEmpty()) Expect(blocks).To(BeEmpty())
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -165,7 +165,7 @@ var _ = Describe("ERC20 Token Repository", func() {
}) })
It("deletes the token supply record when the associated block is deleted", func() { It("deletes the token supply record when the associated block is deleted", func() {
err := repository.Create(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) err := repository.CreateSupply(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
var count int var count int
@ -190,8 +190,8 @@ func supplyModel(blockNumber int64, tokenAddress string, supplyValue string) eve
} }
} }
func createTokenSupplyFor(repository every_block.TokenSupplyRepository, blockNumber int64) { func createTokenSupplyFor(repository every_block.ERC20TokenRepository, blockNumber int64) {
err := repository.Create(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) err := repository.CreateSupply(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }

View File

@ -40,7 +40,7 @@ type TokenSupplyTransformerInitializer struct {
func (i TokenSupplyTransformerInitializer) NewTokenSupplyTransformer(db *postgres.DB, blockchain core.BlockChain) shared.Transformer { func (i TokenSupplyTransformerInitializer) NewTokenSupplyTransformer(db *postgres.DB, blockchain core.BlockChain) shared.Transformer {
fetcher := NewFetcher(blockchain) fetcher := NewFetcher(blockchain)
repository := TokenSupplyRepository{DB: db} repository := ERC20TokenRepository{DB: db}
transformer := Transformer{ transformer := Transformer{
Fetcher: &fetcher, Fetcher: &fetcher,
Repository: &repository, Repository: &repository,
@ -83,21 +83,29 @@ func (t Transformer) Execute() error {
upperBoundBlock = t.Config.LastBlock upperBoundBlock = t.Config.LastBlock
} }
blocks, err := t.Repository.MissingBlocks(t.Config.FirstBlock, upperBoundBlock) // Supply transformations:
// Fetch missing supply blocks
blocks, err := t.Repository.MissingSupplyBlocks(t.Config.FirstBlock, upperBoundBlock, t.Config.Address)
if err != nil { if err != nil {
return newTransformerError(err, t.Config.FirstBlock, FetchingBlocksError) return newTransformerError(err, t.Config.FirstBlock, FetchingBlocksError)
} }
// Fetch supply for missing blocks
log.Printf("Fetching totalSupply for %d blocks", len(blocks)) log.Printf("Fetching totalSupply for %d blocks", len(blocks))
// For each block missing total supply, create supply model and feed the missing data into the repository
for _, blockNumber := range blocks { for _, blockNumber := range blocks {
totalSupply, err := t.Fetcher.FetchSupplyOf(t.Config.Abi, t.Config.Address, blockNumber) totalSupply, err := t.Fetcher.FetchBigInt("totalSupply", t.Config.Abi, t.Config.Address, blockNumber, nil)
if err != nil { if err != nil {
return newTransformerError(err, blockNumber, FetchingSupplyError) return newTransformerError(err, blockNumber, FetchingSupplyError)
} }
// Create the supply model
model := createTokenSupplyModel(totalSupply, t.Config.Address, blockNumber) model := createTokenSupplyModel(totalSupply, t.Config.Address, blockNumber)
err = t.Repository.Create(model) // Feed it into the repository
err = t.Repository.CreateSupply(model)
if err != nil { if err != nil {
return newTransformerError(err, blockNumber, CreateSupplyError) return newTransformerError(err, blockNumber, CreateSupplyError)

View File

@ -39,7 +39,7 @@ var config = testContractConfig
var _ = Describe("Everyblock transformer", func() { var _ = Describe("Everyblock transformer", func() {
var fetcher mocks.Fetcher var fetcher mocks.Fetcher
var repository mocks.TotalSupplyRepository var repository mocks.ERC20TokenRepository
var transformer every_block.Transformer var transformer every_block.Transformer
var blockChain *fakes.MockBlockChain var blockChain *fakes.MockBlockChain
var initialSupply = "27647235749155415536952630" var initialSupply = "27647235749155415536952630"
@ -53,8 +53,8 @@ var _ = Describe("Everyblock transformer", func() {
blockChain.SetLastBlock(&defaultLastBlock) blockChain.SetLastBlock(&defaultLastBlock)
fetcher = mocks.Fetcher{BlockChain: blockChain} fetcher = mocks.Fetcher{BlockChain: blockChain}
fetcher.SetSupply(initialSupply) fetcher.SetSupply(initialSupply)
repository = mocks.TotalSupplyRepository{} repository = mocks.ERC20TokenRepository{}
repository.SetMissingBlocks([]int64{config.FirstBlock}) repository.SetMissingSupplyBlocks([]int64{config.FirstBlock})
//setting the mock repository to return the first block as the missing blocks //setting the mock repository to return the first block as the missing blocks
transformer = every_block.Transformer{ transformer = every_block.Transformer{
@ -89,8 +89,9 @@ var _ = Describe("Everyblock transformer", func() {
config.FirstBlock + 1, config.FirstBlock + 1,
config.FirstBlock + 2, config.FirstBlock + 2,
} }
repository.SetMissingBlocks(missingBlocks) repository.SetMissingSupplyBlocks(missingBlocks)
transformer.Execute() err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(len(fetcher.FetchedBlocks)).To(Equal(3)) Expect(len(fetcher.FetchedBlocks)).To(Equal(3))
Expect(fetcher.FetchedBlocks).To(ConsistOf(config.FirstBlock, config.FirstBlock+1, config.FirstBlock+2)) Expect(fetcher.FetchedBlocks).To(ConsistOf(config.FirstBlock, config.FirstBlock+1, config.FirstBlock+2))
@ -104,7 +105,7 @@ var _ = Describe("Everyblock transformer", func() {
}) })
It("uses the set contract configuration", func() { It("uses the set contract configuration", func() {
repository.SetMissingBlocks([]int64{testContractConfig.FirstBlock}) repository.SetMissingSupplyBlocks([]int64{testContractConfig.FirstBlock})
transformer.SetConfiguration(testContractConfig) transformer.SetConfiguration(testContractConfig)
err := transformer.Execute() err := transformer.Execute()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -143,7 +144,7 @@ var _ = Describe("Everyblock transformer", func() {
It("returns an error if the call to get missing blocks fails", func() { It("returns an error if the call to get missing blocks fails", func() {
failureRepository := mocks.FailureRepository{} failureRepository := mocks.FailureRepository{}
failureRepository.SetMissingBlocksFail(true) failureRepository.SetMissingSupplyBlocksFail(true)
transformer = every_block.Transformer{ transformer = every_block.Transformer{
Fetcher: &fetcher, Fetcher: &fetcher,
Repository: &failureRepository, Repository: &failureRepository,
@ -171,8 +172,8 @@ var _ = Describe("Everyblock transformer", func() {
It("returns an error if the call to save the token_supply fails", func() { It("returns an error if the call to save the token_supply fails", func() {
failureRepository := mocks.FailureRepository{} failureRepository := mocks.FailureRepository{}
failureRepository.SetMissingBlocks([]int64{config.FirstBlock}) failureRepository.SetMissingSupplyBlocks([]int64{config.FirstBlock})
failureRepository.SetCreateFail(true) failureRepository.SetCreateSupplyFail(true)
transformer = every_block.Transformer{ transformer = every_block.Transformer{
Fetcher: &fetcher, Fetcher: &fetcher,

View File

@ -15,11 +15,14 @@
package mocks package mocks
import ( import (
"errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/vulcanize/vulcanizedb/examples/erc20_watcher/every_block" "github.com/vulcanize/vulcanizedb/examples/erc20_watcher/every_block"
"github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes" "github.com/vulcanize/vulcanizedb/pkg/fakes"
"reflect"
) )
type Fetcher struct { type Fetcher struct {
@ -28,6 +31,8 @@ type Fetcher struct {
FetchedBlocks []int64 FetchedBlocks []int64
BlockChain core.BlockChain BlockChain core.BlockChain
supply big.Int supply big.Int
balance map[string]*big.Int
allowance map[string]map[string]*big.Int
} }
func (f *Fetcher) SetSupply(supply string) { func (f *Fetcher) SetSupply(supply string) {
@ -38,69 +43,203 @@ func (f Fetcher) GetBlockChain() core.BlockChain {
return f.BlockChain return f.BlockChain
} }
func (f *Fetcher) FetchSupplyOf(contractAbi string, contractAddress string, blockNumber int64) (big.Int, error) { func (f *Fetcher) FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error) {
f.Abi = contractAbi f.Abi = contractAbi
f.ContractAddress = contractAddress f.ContractAddress = contractAddress
f.FetchedBlocks = append(f.FetchedBlocks, blockNumber) f.FetchedBlocks = append(f.FetchedBlocks, blockNumber)
accumulator := big.NewInt(1) accumulator := big.NewInt(1)
f.supply.Add(&f.supply, accumulator)
return f.supply, nil if method == "totalSupply" {
f.supply.Add(&f.supply, accumulator)
return f.supply, nil
}
if method == "balanceOf" {
rfl := reflect.ValueOf(methodArgs).Field(0)
tokenHolderAddr := rfl.Interface().(string)
pnt := f.balance[tokenHolderAddr]
f.balance[tokenHolderAddr].Add(pnt, accumulator)
return *f.balance[tokenHolderAddr], nil
}
if method == "allowance" {
rfl1 := reflect.ValueOf(methodArgs).Field(0)
rfl2 := reflect.ValueOf(methodArgs).Field(1)
tokenHolderAddr := rfl1.Interface().(string)
spenderAddr := rfl2.Interface().(string)
pnt := f.allowance[tokenHolderAddr][spenderAddr]
f.allowance[tokenHolderAddr][spenderAddr].Add(pnt, accumulator)
return *f.allowance[tokenHolderAddr][spenderAddr], nil
}
return *big.NewInt(0), errors.New("invalid method argument")
} }
type TotalSupplyRepository struct { func (f *Fetcher) FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error) {
TotalSuppliesCreated []every_block.TokenSupply //TODO: this
MissingBlockNumbers []int64 return false, nil
StartingBlock int64
EndingBlock int64
} }
func (fr *TotalSupplyRepository) Create(supply every_block.TokenSupply) error { func (f *Fetcher) FetchAddress(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Address, error) {
//TODO: this
var adr common.Address
return adr, nil
}
type ERC20TokenRepository struct {
TotalSuppliesCreated []every_block.TokenSupply
MissingSupplyBlockNumbers []int64
TotalBalancesCreated []every_block.TokenBalance
MissingBalanceBlockNumbers []int64
TotalAllowancesCreated []every_block.TokenAllowance
MissingAllowanceBlockNumbers []int64
StartingBlock int64
EndingBlock int64
}
func (fr *ERC20TokenRepository) CreateSupply(supply every_block.TokenSupply) error {
fr.TotalSuppliesCreated = append(fr.TotalSuppliesCreated, supply) fr.TotalSuppliesCreated = append(fr.TotalSuppliesCreated, supply)
return nil return nil
} }
func (fr *TotalSupplyRepository) MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) { func (fr *ERC20TokenRepository) CreateBalance(balance every_block.TokenBalance) error {
fr.StartingBlock = startingBlock fr.TotalBalancesCreated = append(fr.TotalBalancesCreated, balance)
fr.EndingBlock = highestBlock return nil
return fr.MissingBlockNumbers, nil
} }
func (fr *TotalSupplyRepository) SetMissingBlocks(missingBlocks []int64) { func (fr *ERC20TokenRepository) CreateAllowance(allowance every_block.TokenAllowance) error {
fr.MissingBlockNumbers = missingBlocks fr.TotalAllowancesCreated = append(fr.TotalAllowancesCreated, allowance)
return nil
}
func (fr *ERC20TokenRepository) MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) {
fr.StartingBlock = startingBlock
fr.EndingBlock = highestBlock
return fr.MissingSupplyBlockNumbers, nil
}
func (fr *ERC20TokenRepository) MissingBalanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress string) ([]int64, error) {
fr.StartingBlock = startingBlock
fr.EndingBlock = highestBlock
return fr.MissingBalanceBlockNumbers, nil
}
func (fr *ERC20TokenRepository) MissingAllowanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress, spenderAddress string) ([]int64, error) {
fr.StartingBlock = startingBlock
fr.EndingBlock = highestBlock
return fr.MissingAllowanceBlockNumbers, nil
}
func (fr *ERC20TokenRepository) SetMissingSupplyBlocks(missingBlocks []int64) {
fr.MissingSupplyBlockNumbers = missingBlocks
}
func (fr *ERC20TokenRepository) SetMissingBalanceBlocks(missingBlocks []int64) {
fr.MissingBalanceBlockNumbers = missingBlocks
}
func (fr *ERC20TokenRepository) SetMissingAllowanceBlocks(missingBlocks []int64) {
fr.MissingAllowanceBlockNumbers = missingBlocks
} }
type FailureRepository struct { type FailureRepository struct {
createFail bool createSupplyFail bool
missingBlocksFail bool createBalanceFail bool
missingBlocksNumbers []int64 createAllowanceFail bool
missingSupplyBlocksFail bool
missingBalanceBlocksFail bool
missingAllowanceBlocksFail bool
missingSupplyBlocksNumbers []int64
missingBalanceBlocksNumbers []int64
missingAllowanceBlocksNumbers []int64
} }
func (fr *FailureRepository) Create(supply every_block.TokenSupply) error { func (fr *FailureRepository) CreateSupply(supply every_block.TokenSupply) error {
if fr.createFail { if fr.createSupplyFail {
return fakes.FakeError return fakes.FakeError
} else { } else {
return nil return nil
} }
} }
func (fr *FailureRepository) MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) { func (fr *FailureRepository) CreateBalance(balance every_block.TokenBalance) error {
if fr.missingBlocksFail { if fr.createBalanceFail {
return []int64{}, fakes.FakeError return fakes.FakeError
} else { } else {
return fr.missingBlocksNumbers, nil return nil
} }
} }
func (fr *FailureRepository) SetCreateFail(fail bool) { func (fr *FailureRepository) CreateAllowance(allowance every_block.TokenAllowance) error {
fr.createFail = fail if fr.createAllowanceFail {
return fakes.FakeError
} else {
return nil
}
} }
func (fr *FailureRepository) SetMissingBlocksFail(fail bool) { func (fr *FailureRepository) MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) {
fr.missingBlocksFail = fail if fr.missingSupplyBlocksFail {
return []int64{}, fakes.FakeError
} else {
return fr.missingSupplyBlocksNumbers, nil
}
} }
func (fr *FailureRepository) SetMissingBlocks(missingBlocks []int64) { func (fr *FailureRepository) MissingBalanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress string) ([]int64, error) {
fr.missingBlocksNumbers = missingBlocks if fr.missingBalanceBlocksFail {
return []int64{}, fakes.FakeError
} else {
return fr.missingBalanceBlocksNumbers, nil
}
}
func (fr *FailureRepository) MissingAllowanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress, spenderAddress string) ([]int64, error) {
if fr.missingAllowanceBlocksFail {
return []int64{}, fakes.FakeError
} else {
return fr.missingAllowanceBlocksNumbers, nil
}
}
func (fr *FailureRepository) SetCreateSupplyFail(fail bool) {
fr.createSupplyFail = fail
}
func (fr *FailureRepository) SetCreateBalanceFail(fail bool) {
fr.createBalanceFail = fail
}
func (fr *FailureRepository) SetCreateAllowanceFail(fail bool) {
fr.createAllowanceFail = fail
}
func (fr *FailureRepository) SetMissingSupplyBlocksFail(fail bool) {
fr.missingSupplyBlocksFail = fail
}
func (fr *FailureRepository) SetMissingBalanceBlocksFail(fail bool) {
fr.missingBalanceBlocksFail = fail
}
func (fr *FailureRepository) SetMissingAllowanceBlocksFail(fail bool) {
fr.missingAllowanceBlocksFail = fail
}
func (fr *FailureRepository) SetMissingSupplyBlocks(missingBlocks []int64) {
fr.missingSupplyBlocksNumbers = missingBlocks
}
func (fr *FailureRepository) SetMissingBalanceBlocks(missingBlocks []int64) {
fr.missingBalanceBlocksNumbers = missingBlocks
}
func (fr *FailureRepository) SetMissingAllowanceBlocks(missingBlocks []int64) {
fr.missingAllowanceBlocksNumbers = missingBlocks
} }

View File

@ -80,8 +80,15 @@ var _ = Describe("Reading contracts", func() {
contract := testing.SampleContract() contract := testing.SampleContract()
var balance = new(big.Int) var balance = new(big.Int)
args := common.HexToHash("0xd26114cd6ee289accf82350c8d8487fedb8a0c07")
err = blockChain.FetchContractData(contract.Abi, "0xd26114cd6ee289accf82350c8d8487fedb8a0c07", "balanceOf", args, &balance, 5167471) arg := common.HexToHash("0xd26114cd6ee289accf82350c8d8487fedb8a0c07")
hashArgs := []common.Hash{arg}
balanceOfArgs := make([]interface{}, len(hashArgs))
for i, s := range hashArgs {
balanceOfArgs[i] = s
}
err = blockChain.FetchContractData(contract.Abi, "0xd26114cd6ee289accf82350c8d8487fedb8a0c07", "balanceOf", balanceOfArgs, &balance, 5167471)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
expected := new(big.Int) expected := new(big.Int)
expected.SetString("10897295492887612977137", 10) expected.SetString("10897295492887612977137", 10)

View File

@ -12,5 +12,5 @@ type BlockChain interface {
} }
type ContractDataFetcher interface { type ContractDataFetcher interface {
FetchContractData(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) error FetchContractData(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error
} }

View File

@ -13,7 +13,7 @@ type MockBlockChain struct {
fetchContractDataPassedAbi string fetchContractDataPassedAbi string
fetchContractDataPassedAddress string fetchContractDataPassedAddress string
fetchContractDataPassedMethod string fetchContractDataPassedMethod string
fetchContractDataPassedMethodArg interface{} fetchContractDataPassedMethodArgs []interface{}
fetchContractDataPassedResult interface{} fetchContractDataPassedResult interface{}
fetchContractDataPassedBlockNumber int64 fetchContractDataPassedBlockNumber int64
getBlockByNumberErr error getBlockByNumberErr error
@ -43,11 +43,11 @@ func (blockChain *MockBlockChain) GetHeaderByNumber(blockNumber int64) (core.Hea
return core.Header{BlockNumber: blockNumber}, nil return core.Header{BlockNumber: blockNumber}, nil
} }
func (blockChain *MockBlockChain) FetchContractData(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) error { func (blockChain *MockBlockChain) FetchContractData(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error {
blockChain.fetchContractDataPassedAbi = abiJSON blockChain.fetchContractDataPassedAbi = abiJSON
blockChain.fetchContractDataPassedAddress = address blockChain.fetchContractDataPassedAddress = address
blockChain.fetchContractDataPassedMethod = method blockChain.fetchContractDataPassedMethod = method
blockChain.fetchContractDataPassedMethodArg = methodArg blockChain.fetchContractDataPassedMethodArgs = methodArgs
blockChain.fetchContractDataPassedResult = result blockChain.fetchContractDataPassedResult = result
blockChain.fetchContractDataPassedBlockNumber = blockNumber blockChain.fetchContractDataPassedBlockNumber = blockNumber
return blockChain.fetchContractDataErr return blockChain.fetchContractDataErr
@ -74,7 +74,7 @@ func (blockChain *MockBlockChain) GetBlockByNumber(blockNumber int64) (core.Bloc
} }
// TODO: handle methodArg being nil (can't match nil to nil in Gomega) // TODO: handle methodArg being nil (can't match nil to nil in Gomega)
func (blockChain *MockBlockChain) AssertFetchContractDataCalledWith(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) { func (blockChain *MockBlockChain) AssertFetchContractDataCalledWith(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) {
Expect(blockChain.fetchContractDataPassedAbi).To(Equal(abiJSON)) Expect(blockChain.fetchContractDataPassedAbi).To(Equal(abiJSON))
Expect(blockChain.fetchContractDataPassedAddress).To(Equal(address)) Expect(blockChain.fetchContractDataPassedAddress).To(Equal(address))
Expect(blockChain.fetchContractDataPassedMethod).To(Equal(method)) Expect(blockChain.fetchContractDataPassedMethod).To(Equal(method))

View File

@ -14,14 +14,14 @@ var (
ErrInvalidStateAttribute = errors.New("invalid state attribute") ErrInvalidStateAttribute = errors.New("invalid state attribute")
) )
func (blockChain *BlockChain) FetchContractData(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) error { func (blockChain *BlockChain) FetchContractData(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error {
parsed, err := ParseAbi(abiJSON) parsed, err := ParseAbi(abiJSON)
if err != nil { if err != nil {
return err return err
} }
var input []byte var input []byte
if methodArg != nil { if methodArgs != nil {
input, err = parsed.Pack(method, methodArg) input, err = parsed.Pack(method, methodArgs...)
} else { } else {
input, err = parsed.Pack(method) input, err = parsed.Pack(method)
} }