Get transactions (#45)

* Make transactions requests in parallel

* Update transaction error handling
This commit is contained in:
Matt K
2018-03-27 16:06:12 -05:00
committed by GitHub
parent 88210e436a
commit 8a9395819c
38 changed files with 2303 additions and 64 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ import "math/big"
type Blockchain interface {
ContractDataFetcher
GetBlockByNumber(blockNumber int64) Block
GetBlockByNumber(blockNumber int64) (Block, error)
GetLogs(contract Contract, startingBlockNumber *big.Int, endingBlockNumber *big.Int) ([]Log, error)
LastBlock() *big.Int
Node() Node
@@ -3,10 +3,17 @@ package postgres_test
import (
"testing"
"io/ioutil"
"log"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func init() {
log.SetOutput(ioutil.Discard)
}
func TestPostgres(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Postgres Suite")
-7
View File
@@ -4,9 +4,6 @@ import (
"fmt"
"strings"
"io/ioutil"
"log"
"math/big"
"github.com/jmoiron/sqlx"
@@ -20,10 +17,6 @@ import (
"github.com/vulcanize/vulcanizedb/test_config"
)
func init() {
log.SetOutput(ioutil.Discard)
}
var _ = Describe("Postgres DB", func() {
var sqlxdb *sqlx.DB
+8 -3
View File
@@ -14,6 +14,7 @@ type Blockchain struct {
WasToldToStop bool
node core.Node
ContractReturnValue []byte
err error
}
func (blockchain *Blockchain) FetchContractData(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) error {
@@ -42,12 +43,13 @@ func (blockchain *Blockchain) Node() core.Node {
return blockchain.node
}
func NewBlockchain() *Blockchain {
func NewBlockchain(err error) *Blockchain {
return &Blockchain{
blocks: make(map[int64]core.Block),
logs: make(map[string][]core.Log),
contractAttributes: make(map[string]map[string]string),
node: core.Node{GenesisBlock: "GENESIS", NetworkID: 1, ID: "x123", ClientName: "Geth"},
err: err,
}
}
@@ -61,8 +63,11 @@ func NewBlockchainWithBlocks(blocks []core.Block) *Blockchain {
}
}
func (blockchain *Blockchain) GetBlockByNumber(blockNumber int64) core.Block {
return blockchain.blocks[blockNumber]
func (blockchain *Blockchain) GetBlockByNumber(blockNumber int64) (core.Block, error) {
if blockchain.err != nil {
return core.Block{}, blockchain.err
}
return blockchain.blocks[blockNumber], nil
}
func (blockchain *Blockchain) AddBlock(block core.Block) {
+36 -19
View File
@@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"golang.org/x/net/context"
"golang.org/x/sync/errgroup"
)
type Client interface {
@@ -17,8 +18,11 @@ type Client interface {
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
}
func ToCoreBlock(gethBlock *types.Block, client Client) core.Block {
transactions := convertTransactionsToCore(gethBlock, client)
func ToCoreBlock(gethBlock *types.Block, client Client) (core.Block, error) {
transactions, err := convertTransactionsToCore(gethBlock, client)
if err != nil {
return core.Block{}, err
}
coreBlock := core.Block{
Difficulty: gethBlock.Difficulty().Int64(),
ExtraData: hexutil.Encode(gethBlock.Extra()),
@@ -36,35 +40,48 @@ func ToCoreBlock(gethBlock *types.Block, client Client) core.Block {
}
coreBlock.Reward = CalcBlockReward(coreBlock, gethBlock.Uncles())
coreBlock.UnclesReward = CalcUnclesReward(coreBlock, gethBlock.Uncles())
return coreBlock
return coreBlock, nil
}
func convertTransactionsToCore(gethBlock *types.Block, client Client) []core.Transaction {
transactions := make([]core.Transaction, 0)
for i, gethTransaction := range gethBlock.Transactions() {
from, err := client.TransactionSender(context.Background(), gethTransaction, gethBlock.Hash(), uint(i))
if err != nil {
log.Println(err)
}
transaction := transToCoreTrans(gethTransaction, &from)
transaction, err = appendReceiptToTransaction(client, transaction)
if err != nil {
log.Println(err)
}
transactions = append(transactions, transaction)
func convertTransactionsToCore(gethBlock *types.Block, client Client) ([]core.Transaction, error) {
var g errgroup.Group
coreTransactions := make([]core.Transaction, len(gethBlock.Transactions()))
for gethTransactionIndex, gethTransaction := range gethBlock.Transactions() {
//https://golang.org/doc/faq#closures_and_goroutines
transaction := gethTransaction
transactionIndex := uint(gethTransactionIndex)
g.Go(func() error {
from, err := client.TransactionSender(context.Background(), transaction, gethBlock.Hash(), transactionIndex)
if err != nil {
log.Println("transaction sender: ", err)
return err
}
coreTransaction := transToCoreTrans(transaction, &from)
coreTransaction, err = appendReceiptToTransaction(client, coreTransaction)
if err != nil {
log.Println("receipt: ", err)
return err
}
coreTransactions[transactionIndex] = coreTransaction
return nil
})
}
return transactions
if err := g.Wait(); err != nil {
log.Println("transactions: ", err)
return coreTransactions, err
}
return coreTransactions, nil
}
func appendReceiptToTransaction(client Client, transaction core.Transaction) (core.Transaction, error) {
gethReceipt, err := client.TransactionReceipt(context.Background(), common.HexToHash(transaction.Hash))
if err != nil {
log.Println(err)
return transaction, err
}
receipt := ReceiptToCoreReceipt(gethReceipt)
transaction.Receipt = receipt
return transaction, err
return transaction, nil
}
func transToCoreTrans(transaction *types.Transaction, from *common.Address) core.Transaction {
+93 -13
View File
@@ -5,6 +5,13 @@ import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
@@ -15,11 +22,25 @@ import (
type FakeGethClient struct {
receipts map[string]*types.Receipt
err error
}
func NewFakeClient() *FakeGethClient {
type TransActionReceiptError struct{}
func (tarErr TransActionReceiptError) Error() string {
return fmt.Sprintf("transaction receipt error")
}
type TransactionSenderError struct{}
func (tasErr TransactionSenderError) Error() string {
return fmt.Sprintf("transaction sender error")
}
func NewFakeClient(err error) *FakeGethClient {
return &FakeGethClient{
receipts: make(map[string]*types.Receipt),
err: err,
}
}
@@ -30,6 +51,9 @@ func (client *FakeGethClient) AddReceipts(receipts []*types.Receipt) {
}
func (client *FakeGethClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
if err, ok := client.err.(TransActionReceiptError); ok {
return &types.Receipt{}, err
}
if gasUsed, ok := client.receipts[txHash.Hex()]; ok {
return gasUsed, nil
}
@@ -37,6 +61,9 @@ func (client *FakeGethClient) TransactionReceipt(ctx context.Context, txHash com
}
func (client *FakeGethClient) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) {
if err, ok := client.err.(TransactionSenderError); ok {
return common.Address{}, err
}
return common.HexToAddress("0x123"), nil
}
@@ -66,8 +93,9 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
}
block := types.NewBlock(&header, []*types.Transaction{}, []*types.Header{}, []*types.Receipt{})
client := &FakeGethClient{}
gethBlock := geth.ToCoreBlock(block, client)
gethBlock, err := geth.ToCoreBlock(block, client)
Expect(err).ToNot(HaveOccurred())
Expect(gethBlock.Difficulty).To(Equal(difficulty.Int64()))
Expect(gethBlock.GasLimit).To(Equal(gasLimit))
Expect(gethBlock.Miner).To(Equal(miner.Hex()))
@@ -104,7 +132,7 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
}
receipts := []*types.Receipt{&receipt}
client := NewFakeClient()
client := NewFakeClient(nil)
client.AddReceipts(receipts)
number := int64(1071819)
@@ -113,8 +141,9 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
}
uncles := []*types.Header{{Number: big.NewInt(1071817)}, {Number: big.NewInt(1071818)}}
block := types.NewBlock(&header, transactions, uncles, []*types.Receipt{&receipt})
coreBlock := geth.ToCoreBlock(block, client)
coreBlock, err := geth.ToCoreBlock(block, client)
Expect(err).ToNot(HaveOccurred())
Expect(geth.CalcBlockReward(coreBlock, block.Uncles())).To(Equal(5.31355))
})
@@ -144,11 +173,12 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
}
block := types.NewBlock(&header, transactions, uncles, receipts)
client := NewFakeClient()
client := NewFakeClient(nil)
client.AddReceipts(receipts)
coreBlock := geth.ToCoreBlock(block, client)
coreBlock, err := geth.ToCoreBlock(block, client)
Expect(err).ToNot(HaveOccurred())
Expect(geth.CalcUnclesReward(coreBlock, block.Uncles())).To(Equal(6.875))
})
@@ -190,10 +220,11 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
var uncles []*types.Header
block := types.NewBlock(&header, transactions, uncles, receipts)
client := NewFakeClient()
client := NewFakeClient(nil)
client.AddReceipts(receipts)
coreBlock := geth.ToCoreBlock(block, client)
coreBlock, err := geth.ToCoreBlock(block, client)
Expect(err).ToNot(HaveOccurred())
Expect(geth.CalcBlockReward(coreBlock, block.Uncles())).To(Equal(3.024990672))
})
})
@@ -203,8 +234,9 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
header := types.Header{}
block := types.NewBlock(&header, []*types.Transaction{}, []*types.Header{}, []*types.Receipt{})
client := &FakeGethClient{}
coreBlock := geth.ToCoreBlock(block, client)
coreBlock, err := geth.ToCoreBlock(block, client)
Expect(err).ToNot(HaveOccurred())
Expect(len(coreBlock.Transactions)).To(Equal(0))
})
@@ -227,7 +259,7 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
TxHash: gethTransaction.Hash(),
}
client := NewFakeClient()
client := NewFakeClient(nil)
client.AddReceipts([]*types.Receipt{gethReceipt})
header := types.Header{}
@@ -237,8 +269,9 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
[]*types.Header{},
[]*types.Receipt{gethReceipt},
)
coreBlock := geth.ToCoreBlock(gethBlock, client)
coreBlock, err := geth.ToCoreBlock(gethBlock, client)
Expect(err).ToNot(HaveOccurred())
Expect(len(coreBlock.Transactions)).To(Equal(1))
coreTransaction := coreBlock.Transactions[0]
Expect(coreTransaction.Data).To(Equal("0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"))
@@ -271,7 +304,7 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
ContractAddress: common.HexToAddress("0x1023342345"),
}
client := NewFakeClient()
client := NewFakeClient(nil)
client.AddReceipts([]*types.Receipt{gethReceipt})
gethBlock := types.NewBlock(
@@ -281,8 +314,9 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
[]*types.Receipt{gethReceipt},
)
coreBlock := geth.ToCoreBlock(gethBlock, client)
coreBlock, err := geth.ToCoreBlock(gethBlock, client)
Expect(err).ToNot(HaveOccurred())
coreTransaction := coreBlock.Transactions[0]
Expect(coreTransaction.To).To(Equal(""))
@@ -292,4 +326,50 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
})
})
Describe("transaction error handling", func() {
var gethTransaction *types.Transaction
var gethReceipt *types.Receipt
var header *types.Header
var gethBlock *types.Block
BeforeEach(func() {
log.SetOutput(ioutil.Discard)
gethTransaction = types.NewTransaction(
uint64(0),
common.Address{},
big.NewInt(0),
uint64(0),
big.NewInt(0),
[]byte{},
)
gethReceipt = &types.Receipt{}
header = &types.Header{}
gethBlock = types.NewBlock(
header,
[]*types.Transaction{gethTransaction},
[]*types.Header{},
[]*types.Receipt{gethReceipt},
)
})
AfterEach(func() {
defer log.SetOutput(os.Stdout)
})
It("returns an error when transaction sender call fails", func() {
client := NewFakeClient(TransactionSenderError{})
client.AddReceipts([]*types.Receipt{})
_, err := geth.ToCoreBlock(gethBlock, client)
Expect(err).To(Equal(TransactionSenderError{}))
})
It("returns an error when transaction receipt call fails", func() {
client := NewFakeClient(TransActionReceiptError{})
client.AddReceipts([]*types.Receipt{})
_, err := geth.ToCoreBlock(gethBlock, client)
Expect(err).To(Equal(TransActionReceiptError{}))
})
})
})
+10 -3
View File
@@ -58,9 +58,16 @@ func (blockchain *Blockchain) Node() core.Node {
return blockchain.node
}
func (blockchain *Blockchain) GetBlockByNumber(blockNumber int64) core.Block {
gethBlock, _ := blockchain.client.BlockByNumber(context.Background(), big.NewInt(blockNumber))
return ToCoreBlock(gethBlock, blockchain.client)
func (blockchain *Blockchain) GetBlockByNumber(blockNumber int64) (core.Block, error) {
gethBlock, err := blockchain.client.BlockByNumber(context.Background(), big.NewInt(blockNumber))
if err != nil {
return core.Block{}, err
}
block, err := ToCoreBlock(gethBlock, blockchain.client)
if err != nil {
return core.Block{}, err
}
return block, nil
}
func (blockchain *Blockchain) LastBlock() *big.Int {
+6 -1
View File
@@ -9,6 +9,8 @@ import (
"strings"
"log"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p"
"github.com/vulcanize/vulcanizedb/pkg/core"
@@ -79,7 +81,10 @@ func MakeNode(wrapper ClientWrapper) core.Node {
func (client ClientWrapper) NetworkId() float64 {
var version string
client.CallContext(context.Background(), &version, "net_version")
err := client.CallContext(context.Background(), &version, "net_version")
if err != nil {
log.Println(err)
}
networkId, _ := strconv.ParseFloat(version, 64)
return networkId
}
+6
View File
@@ -4,9 +4,15 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"io/ioutil"
"log"
"testing"
)
func init() {
log.SetOutput(ioutil.Discard)
}
func TestHistory(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "History Suite")
+5 -1
View File
@@ -18,7 +18,11 @@ func PopulateMissingBlocks(blockchain core.Blockchain, blockRepository datastore
func RetrieveAndUpdateBlocks(blockchain core.Blockchain, blockRepository datastore.BlockRepository, blockNumbers []int64) int {
for _, blockNumber := range blockNumbers {
block := blockchain.GetBlockByNumber(blockNumber)
block, err := blockchain.GetBlockByNumber(blockNumber)
if err != nil {
log.Printf("failed to retrieve block number: %d\n", blockNumber)
return 0
}
blockRepository.CreateOrUpdateBlock(block)
}
return len(blockNumbers)
+10
View File
@@ -1,6 +1,8 @@
package history_test
import (
"errors"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
@@ -100,4 +102,12 @@ var _ = Describe("Populating blocks", func() {
Expect(blockRepository.CreateOrUpdateBlockCallCount).To(Equal(3))
})
It("does not call repository create block when there is an error", func() {
blockchain := fakes.NewBlockchain(errors.New("error getting block"))
blocks := history.MakeRange(1, 10)
history.RetrieveAndUpdateBlocks(blockchain, blockRepository, blocks)
Expect(blockRepository.BlockCount()).To(Equal(0))
Expect(blockRepository.CreateOrUpdateBlockCallCount).To(Equal(0))
})
})
-7
View File
@@ -3,9 +3,6 @@ package history_test
import (
"bytes"
"io/ioutil"
"log"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
@@ -14,10 +11,6 @@ import (
"github.com/vulcanize/vulcanizedb/pkg/history"
)
func init() {
log.SetOutput(ioutil.Discard)
}
var _ = Describe("Blocks validator", func() {
It("creates a ValidationWindow equal to (HEAD-windowSize, HEAD)", func() {