From 921bde1089fbd01dcb50c8949b45534cbb286ee8 Mon Sep 17 00:00:00 2001 From: Matt K <1036969+mkrump@users.noreply.github.com> Date: Thu, 7 Dec 2017 13:32:16 -0600 Subject: [PATCH] Update BlockChain to record NodeInfo (#95) --- Gopkg.lock | 62 +++++++++++++----- cmd/populate_blocks/main.go | 2 +- cmd/run/main.go | 5 +- cmd/show_contract_summary/main.go | 2 +- cmd/subscribe_contract/main.go | 4 +- cmd/utils.go | 5 +- .../1512504078_add_nodes_table.down.sql | 1 + .../1512504078_add_nodes_table.up.sql | 6 ++ .../1512507280_add_node_fk_to_blocks.down.sql | 2 + .../1512507280_add_node_fk_to_blocks.up.sql | 5 ++ db/schema.sql | 64 ++++++++++++++++++- integration_test/geth_blockchain_test.go | 11 ++++ pkg/core/blockchain.go | 1 + pkg/core/node_info.go | 6 ++ pkg/fakes/blockchain.go | 6 ++ pkg/geth/geth_blockchain.go | 11 +++- pkg/geth/node/node.go | 32 ++++++++++ pkg/repositories/in_memory_test.go | 3 +- pkg/repositories/postgres.go | 52 +++++++++++---- pkg/repositories/postgres_test.go | 21 ++++-- pkg/repositories/testing/helpers.go | 20 +++++- 21 files changed, 276 insertions(+), 45 deletions(-) create mode 100644 db/migrations/1512504078_add_nodes_table.down.sql create mode 100644 db/migrations/1512504078_add_nodes_table.up.sql create mode 100644 db/migrations/1512507280_add_node_fk_to_blocks.down.sql create mode 100644 db/migrations/1512507280_add_node_fk_to_blocks.up.sql create mode 100644 pkg/core/node_info.go create mode 100644 pkg/geth/node/node.go diff --git a/Gopkg.lock b/Gopkg.lock index f997d50f..e4193fc7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -13,23 +13,29 @@ packages = ["."] revision = "4748e29d5718c2df4028a6543edf86fd8cc0f881" +[[projects]] + branch = "master" + name = "github.com/aristanetworks/goarista" + packages = ["monotime"] + revision = "8d0e8f607a4080e7df3532e645440ed0900c64a4" + [[projects]] branch = "master" name = "github.com/btcsuite/btcd" packages = ["btcec"] - revision = "c7588cbf7690cd9f047a28efa2dcd8f2435a4e5e" + revision = "2e60448ffcc6bf78332d1fe590260095f554dd78" [[projects]] name = "github.com/ethereum/go-ethereum" - packages = [".","accounts/abi","common","common/hexutil","common/math","core/types","crypto","crypto/secp256k1","crypto/sha3","ethclient","log","params","rlp","rpc","trie"] - revision = "1db4ecdc0b9e828ff65777fb466fc7c1d04e0de9" - version = "v1.7.2" + packages = [".","accounts/abi","common","common/hexutil","common/math","common/mclock","core/types","crypto","crypto/ecies","crypto/secp256k1","crypto/sha3","ethclient","event","log","metrics","p2p","p2p/discover","p2p/discv5","p2p/nat","p2p/netutil","params","rlp","rpc","trie"] + revision = "4bb3c89d44e372e6a9ab85a8be0c9345265c763a" + version = "v1.7.3" [[projects]] name = "github.com/go-stack/stack" packages = ["."] - revision = "817915b46b97fd7bb80e8ab6b69f01a53ac3eebf" - version = "v1.6.0" + revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" + version = "v1.7.0" [[projects]] branch = "master" @@ -37,23 +43,41 @@ packages = ["proto"] revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845" +[[projects]] + branch = "master" + name = "github.com/golang/snappy" + packages = ["."] + revision = "553a641470496b2327abcac10b36396bd98e45c9" + [[projects]] branch = "master" name = "github.com/howeyc/gopass" packages = ["."] revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8" +[[projects]] + branch = "master" + name = "github.com/huin/goupnp" + packages = [".","dcps/internetgateway1","dcps/internetgateway2","httpu","scpd","soap","ssdp"] + revision = "dceda08e705b2acee36aab47d765ed801f64cfc7" + +[[projects]] + name = "github.com/jackpal/go-nat-pmp" + packages = ["."] + revision = "c9cfead9f2a36ddf3daa40ba269aa7f4bbba6b62" + version = "v1.0.1" + [[projects]] branch = "master" name = "github.com/jmoiron/sqlx" packages = [".","reflectx"] - revision = "3379e5993990b1f927fc8db926485e6f6becf2d2" + revision = "99f3ad6d85ae53d0fecf788ab62d0e9734b3c117" [[projects]] branch = "master" name = "github.com/lib/pq" packages = [".","oid"] - revision = "b609790bd85edf8e9ab7e0f8912750a786177bcf" + revision = "83612a56d3dd153a94a629cd64925371c9adad78" [[projects]] name = "github.com/mattn/go-colorable" @@ -112,8 +136,8 @@ [[projects]] branch = "master" name = "github.com/rcrowley/go-metrics" - packages = ["."] - revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c" + packages = [".","exp"] + revision = "e181e095bae94582363434144c61a9653aff6e50" [[projects]] name = "github.com/rs/cors" @@ -121,29 +145,35 @@ revision = "7af7a1e09ba336d2ea14b1ce73bf693c6837dbf6" version = "v1.2" +[[projects]] + branch = "master" + name = "github.com/syndtr/goleveldb" + packages = ["leveldb","leveldb/cache","leveldb/comparer","leveldb/errors","leveldb/filter","leveldb/iterator","leveldb/journal","leveldb/memdb","leveldb/opt","leveldb/storage","leveldb/table","leveldb/util"] + revision = "adf24ef3f94bd13ec4163060b21a5678f22b429b" + [[projects]] branch = "master" name = "golang.org/x/crypto" packages = ["ssh/terminal"] - revision = "bd6f299fb381e4c3393d1c4b1f0b94f5e77650c8" + revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122" [[projects]] branch = "master" name = "golang.org/x/net" packages = ["context","html","html/atom","html/charset","websocket"] - revision = "1087133bc4af3073e18add999345c6ae75918503" + revision = "faacc1b5e36e3ff02cbec9661c69ac63dd5a83ad" [[projects]] branch = "master" name = "golang.org/x/sys" packages = ["unix","windows"] - revision = "8dbc5d05d6edcc104950cc299a1ce6641235bc86" + revision = "a0f4589a76f1f83070cb9e5613809e1d07b97c13" [[projects]] branch = "master" name = "golang.org/x/text" packages = ["encoding","encoding/charmap","encoding/htmlindex","encoding/internal","encoding/internal/identifier","encoding/japanese","encoding/korean","encoding/simplifiedchinese","encoding/traditionalchinese","encoding/unicode","internal/gen","internal/tag","internal/utf8internal","language","runes","transform","unicode/cldr"] - revision = "c01e4764d870b77f8abe5096ee19ad20d80e8075" + revision = "be25de41fadfae372d6470bda81ca6beb55ef551" [[projects]] name = "gopkg.in/fatih/set.v0" @@ -173,11 +203,11 @@ branch = "v2" name = "gopkg.in/yaml.v2" packages = ["."] - revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f" + revision = "287cf08546ab5e7e37d55a84f7ed3fd1db036de5" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "90af18ee127c0b2099f47adc5d33fb6ce9b98630278ec66648ef3e1c5ad9063f" + inputs-digest = "9b993b03db46de97fde5cfe8022a60d1654172dcb7d63c2c4b876308ffd1f73e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/populate_blocks/main.go b/cmd/populate_blocks/main.go index bdeff355..82d7e41a 100644 --- a/cmd/populate_blocks/main.go +++ b/cmd/populate_blocks/main.go @@ -16,7 +16,7 @@ func main() { flag.Parse() config := cmd.LoadConfig(*environment) blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - repository := cmd.LoadPostgres(config.Database) + repository := cmd.LoadPostgres(config.Database, blockchain.Node()) numberOfBlocksCreated := history.PopulateBlocks(blockchain, repository, int64(*startingBlockNumber)) fmt.Printf("Populated %d blocks", numberOfBlocksCreated) } diff --git a/cmd/run/main.go b/cmd/run/main.go index 2a10fd4f..cf4853a2 100644 --- a/cmd/run/main.go +++ b/cmd/run/main.go @@ -16,10 +16,11 @@ func main() { environment := flag.String("environment", "", "Environment name") flag.Parse() config := cmd.LoadConfig(*environment) - repository := cmd.LoadPostgres(config.Database) fmt.Printf("Creating Geth Blockchain to: %s\n", config.Client.IPCPath) + blockchain := geth.NewGethBlockchain(config.Client.IPCPath) + repository := cmd.LoadPostgres(config.Database, blockchain.Node()) listener := blockchain_listener.NewBlockchainListener( - geth.NewGethBlockchain(config.Client.IPCPath), + blockchain, []core.BlockchainObserver{ observers.BlockchainLoggingObserver{}, observers.NewBlockchainDbObserver(repository), diff --git a/cmd/show_contract_summary/main.go b/cmd/show_contract_summary/main.go index 6a6baeb5..1d4730b6 100644 --- a/cmd/show_contract_summary/main.go +++ b/cmd/show_contract_summary/main.go @@ -21,7 +21,7 @@ func main() { flag.Parse() config := cmd.LoadConfig(*environment) blockchain := geth.NewGethBlockchain(config.Client.IPCPath) - repository := cmd.LoadPostgres(config.Database) + repository := cmd.LoadPostgres(config.Database, blockchain.Node()) blockNumber := requestedBlockNumber(_blockNumber) contractSummary, err := contract_summary.NewSummary(blockchain, repository, *contractHash, blockNumber) diff --git a/cmd/subscribe_contract/main.go b/cmd/subscribe_contract/main.go index 5b74e58d..c372d1dc 100644 --- a/cmd/subscribe_contract/main.go +++ b/cmd/subscribe_contract/main.go @@ -5,6 +5,7 @@ import ( "github.com/8thlight/vulcanizedb/cmd" "github.com/8thlight/vulcanizedb/pkg/core" + "github.com/8thlight/vulcanizedb/pkg/geth" ) func main() { @@ -15,7 +16,8 @@ func main() { contractAbiString := cmd.GetAbi(*abiFilepath, *contractHash) config := cmd.LoadConfig(*environment) - repository := cmd.LoadPostgres(config.Database) + blockchain := geth.NewGethBlockchain(config.Client.IPCPath) + repository := cmd.LoadPostgres(config.Database, blockchain.Node()) watchedContract := core.Contract{ Abi: contractAbiString, Hash: *contractHash, diff --git a/cmd/utils.go b/cmd/utils.go index fc388ade..cd23dfa4 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/8thlight/vulcanizedb/pkg/config" + "github.com/8thlight/vulcanizedb/pkg/core" "github.com/8thlight/vulcanizedb/pkg/geth" "github.com/8thlight/vulcanizedb/pkg/repositories" ) @@ -20,8 +21,8 @@ func LoadConfig(environment string) config.Config { return cfg } -func LoadPostgres(database config.Database) repositories.Postgres { - repository, err := repositories.NewPostgres(database) +func LoadPostgres(database config.Database, node core.Node) repositories.Postgres { + repository, err := repositories.NewPostgres(database, node) if err != nil { log.Fatalf("Error loading postgres\n%v", err) } diff --git a/db/migrations/1512504078_add_nodes_table.down.sql b/db/migrations/1512504078_add_nodes_table.down.sql new file mode 100644 index 00000000..c095b945 --- /dev/null +++ b/db/migrations/1512504078_add_nodes_table.down.sql @@ -0,0 +1 @@ +DROP TABLE nodes; \ No newline at end of file diff --git a/db/migrations/1512504078_add_nodes_table.up.sql b/db/migrations/1512504078_add_nodes_table.up.sql new file mode 100644 index 00000000..06fa91d9 --- /dev/null +++ b/db/migrations/1512504078_add_nodes_table.up.sql @@ -0,0 +1,6 @@ +CREATE TABLE nodes ( + id SERIAL PRIMARY KEY, + genesis_block VARCHAR(66), + network_id NUMERIC, + CONSTRAINT node_uc UNIQUE (genesis_block, network_id) +); \ No newline at end of file diff --git a/db/migrations/1512507280_add_node_fk_to_blocks.down.sql b/db/migrations/1512507280_add_node_fk_to_blocks.down.sql new file mode 100644 index 00000000..97e87635 --- /dev/null +++ b/db/migrations/1512507280_add_node_fk_to_blocks.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE blocks + DROP COLUMN node_id; \ No newline at end of file diff --git a/db/migrations/1512507280_add_node_fk_to_blocks.up.sql b/db/migrations/1512507280_add_node_fk_to_blocks.up.sql new file mode 100644 index 00000000..6e03577c --- /dev/null +++ b/db/migrations/1512507280_add_node_fk_to_blocks.up.sql @@ -0,0 +1,5 @@ +ALTER TABLE blocks + ADD COLUMN node_id INTEGER NOT NULL, + ADD CONSTRAINT node_fk +FOREIGN KEY (node_id) +REFERENCES nodes (id); \ No newline at end of file diff --git a/db/schema.sql b/db/schema.sql index e90957a0..503eeceb 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -49,7 +49,8 @@ CREATE TABLE blocks ( block_nonce character varying(20), block_parenthash character varying(66), block_size bigint, - uncle_hash character varying(66) + uncle_hash character varying(66), + node_id integer NOT NULL ); @@ -72,6 +73,36 @@ CREATE SEQUENCE blocks_id_seq ALTER SEQUENCE blocks_id_seq OWNED BY blocks.id; +-- +-- Name: nodes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE nodes ( + id integer NOT NULL, + genesis_block character varying(66), + network_id numeric +); + + +-- +-- Name: nodes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE nodes_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: nodes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE nodes_id_seq OWNED BY nodes.id; + + -- -- Name: schema_migrations; Type: TABLE; Schema: public; Owner: - -- @@ -155,6 +186,13 @@ ALTER SEQUENCE watched_contracts_contract_id_seq OWNED BY watched_contracts.cont ALTER TABLE ONLY blocks ALTER COLUMN id SET DEFAULT nextval('blocks_id_seq'::regclass); +-- +-- Name: nodes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY nodes ALTER COLUMN id SET DEFAULT nextval('nodes_id_seq'::regclass); + + -- -- Name: transactions id; Type: DEFAULT; Schema: public; Owner: - -- @@ -185,6 +223,22 @@ ALTER TABLE ONLY watched_contracts ADD CONSTRAINT contract_hash_uc UNIQUE (contract_hash); +-- +-- Name: nodes node_uc; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY nodes + ADD CONSTRAINT node_uc UNIQUE (genesis_block, network_id); + + +-- +-- Name: nodes nodes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY nodes + ADD CONSTRAINT nodes_pkey PRIMARY KEY (id); + + -- -- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -224,6 +278,14 @@ ALTER TABLE ONLY transactions ADD CONSTRAINT fk_test FOREIGN KEY (block_id) REFERENCES blocks(id); +-- +-- Name: blocks node_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY blocks + ADD CONSTRAINT node_fk FOREIGN KEY (node_id) REFERENCES nodes(id); + + -- -- PostgreSQL database dump complete -- diff --git a/integration_test/geth_blockchain_test.go b/integration_test/geth_blockchain_test.go index 8a9336c9..e77e15b8 100644 --- a/integration_test/geth_blockchain_test.go +++ b/integration_test/geth_blockchain_test.go @@ -54,4 +54,15 @@ var _ = Describe("Reading from the Geth blockchain", func() { close(done) }, 15) + It("retrieves the node info", func(done Done) { + node := blockchain.Node() + devNetworkGenesisBlock := "0xe5be92145a301820111f91866566e3e99ee344d155569e4556a39bc71238f3bc" + devNetworkNodeId := float64(1) + + Expect(node.GenesisBlock).To(Equal(devNetworkGenesisBlock)) + Expect(node.NetworkId).To(Equal(devNetworkNodeId)) + + close(done) + }, 15) + }) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 1738bd28..2bb550d0 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -4,6 +4,7 @@ import "math/big" type Blockchain interface { GetBlockByNumber(blockNumber int64) Block + Node() Node SubscribeToBlocks(blocks chan Block) StartListening() StopListening() diff --git a/pkg/core/node_info.go b/pkg/core/node_info.go new file mode 100644 index 00000000..79ae55fb --- /dev/null +++ b/pkg/core/node_info.go @@ -0,0 +1,6 @@ +package core + +type Node struct { + GenesisBlock string + NetworkId float64 +} diff --git a/pkg/fakes/blockchain.go b/pkg/fakes/blockchain.go index 42a9fad1..e9b90e3b 100644 --- a/pkg/fakes/blockchain.go +++ b/pkg/fakes/blockchain.go @@ -13,6 +13,11 @@ type Blockchain struct { contractAttributes map[string]map[string]string blocksChannel chan core.Block WasToldToStop bool + node core.Node +} + +func (blockchain *Blockchain) Node() core.Node { + return blockchain.node } func (blockchain *Blockchain) GetAttribute(contract core.Contract, attributeName string, blockNumber *big.Int) (interface{}, error) { @@ -29,6 +34,7 @@ func NewBlockchain() *Blockchain { return &Blockchain{ blocks: make(map[int64]core.Block), contractAttributes: make(map[string]map[string]string), + node: core.Node{GenesisBlock: "GENESIS"}, } } diff --git a/pkg/geth/geth_blockchain.go b/pkg/geth/geth_blockchain.go index d2ba8012..6a01496b 100644 --- a/pkg/geth/geth_blockchain.go +++ b/pkg/geth/geth_blockchain.go @@ -6,9 +6,11 @@ import ( "math/big" "github.com/8thlight/vulcanizedb/pkg/core" + "github.com/8thlight/vulcanizedb/pkg/geth/node" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -17,6 +19,11 @@ type GethBlockchain struct { readGethHeaders chan *types.Header outputBlocks chan core.Block newHeadSubscription ethereum.Subscription + node core.Node +} + +func (blockchain *GethBlockchain) Node() core.Node { + return blockchain.node } func (blockchain *GethBlockchain) GetBlockByNumber(blockNumber int64) core.Block { @@ -26,7 +33,9 @@ func (blockchain *GethBlockchain) GetBlockByNumber(blockNumber int64) core.Block func NewGethBlockchain(ipcPath string) *GethBlockchain { blockchain := GethBlockchain{} - client, _ := ethclient.Dial(ipcPath) + rpcClient, _ := rpc.Dial(ipcPath) + client := ethclient.NewClient(rpcClient) + blockchain.node = node.Retrieve(rpcClient) blockchain.client = client return &blockchain } diff --git a/pkg/geth/node/node.go b/pkg/geth/node/node.go new file mode 100644 index 00000000..38fcfe6d --- /dev/null +++ b/pkg/geth/node/node.go @@ -0,0 +1,32 @@ +package node + +import ( + "context" + + "github.com/8thlight/vulcanizedb/pkg/core" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" +) + +func Retrieve(client *rpc.Client) core.Node { + var info p2p.NodeInfo + node := core.Node{} + client.CallContext(context.Background(), &info, "admin_nodeInfo") + for protocolName, protocol := range info.Protocols { + if protocolName == "eth" { + protocolMap, _ := protocol.(map[string]interface{}) + node.GenesisBlock = getAttribute(protocolMap, "genesis").(string) + node.NetworkId = getAttribute(protocolMap, "network").(float64) + } + } + return node +} + +func getAttribute(protocolMap map[string]interface{}, protocol string) interface{} { + for key, val := range protocolMap { + if key == protocol { + return val + } + } + return nil +} diff --git a/pkg/repositories/in_memory_test.go b/pkg/repositories/in_memory_test.go index d991eef7..333a5d68 100644 --- a/pkg/repositories/in_memory_test.go +++ b/pkg/repositories/in_memory_test.go @@ -1,6 +1,7 @@ package repositories_test import ( + "github.com/8thlight/vulcanizedb/pkg/core" "github.com/8thlight/vulcanizedb/pkg/repositories" "github.com/8thlight/vulcanizedb/pkg/repositories/testing" _ "github.com/lib/pq" @@ -9,7 +10,7 @@ import ( var _ = Describe("In memory repository", func() { - testing.AssertRepositoryBehavior(func() repositories.Repository { + testing.AssertRepositoryBehavior(func(core.Node) repositories.Repository { return repositories.NewInMemory() }) diff --git a/pkg/repositories/postgres.go b/pkg/repositories/postgres.go index 81a1cd5e..743979b2 100644 --- a/pkg/repositories/postgres.go +++ b/pkg/repositories/postgres.go @@ -14,21 +14,46 @@ import ( ) type Postgres struct { - Db *sqlx.DB + Db *sqlx.DB + node core.Node + nodeId int64 } var ( ErrDBInsertFailed = errors.New("postgres: insert failed") ErrDBConnectionFailed = errors.New("postgres: db connection failed") + ErrUnableToSetNode = errors.New("postgres: unable to set node") ) -func NewPostgres(databaseConfig config.Database) (Postgres, error) { +func NewPostgres(databaseConfig config.Database, node core.Node) (Postgres, error) { connectString := config.DbConnectionString(databaseConfig) db, err := sqlx.Connect("postgres", connectString) if err != nil { return Postgres{}, ErrDBConnectionFailed } - return Postgres{Db: db}, nil + pg := Postgres{Db: db, node: node} + err = pg.CreateNode(&node) + if err != nil { + return Postgres{}, ErrUnableToSetNode + } + return pg, nil +} + +func (repository *Postgres) CreateNode(node *core.Node) error { + var nodeId int64 + err := repository.Db.QueryRow( + `INSERT INTO nodes (genesis_block, network_id) + VALUES ($1, $2) + ON CONFLICT (genesis_block, network_id) + DO UPDATE + SET genesis_block = $1, network_id = $2 + RETURNING id`, + node.GenesisBlock, node.NetworkId).Scan(&nodeId) + if err != nil { + return ErrUnableToSetNode + } + repository.nodeId = nodeId + return nil } func (repository Postgres) CreateContract(contract core.Contract) error { @@ -53,7 +78,8 @@ func (repository Postgres) CreateContract(contract core.Contract) error { func (repository Postgres) ContractExists(contractHash string) bool { var exists bool repository.Db.QueryRow( - `SELECT exists(SELECT 1 FROM watched_contracts WHERE contract_hash=$1) FROM watched_contracts`, contractHash).Scan(&exists) + `SELECT exists(SELECT 1 FROM watched_contracts WHERE contract_hash=$1) + FROM watched_contracts`, contractHash).Scan(&exists) return exists } @@ -92,7 +118,9 @@ func (repository Postgres) MissingBlockNumbers(startingBlockNumber int64, highes func (repository Postgres) FindBlockByNumber(blockNumber int64) *core.Block { blockRows, _ := repository.Db.Query( - `SELECT id, block_number, block_gaslimit, block_gasused, block_time, block_difficulty, block_hash, block_nonce, block_parenthash, block_size, uncle_hash FROM blocks`) + `SELECT id, block_number, block_gaslimit, block_gasused, block_time, block_difficulty, block_hash, block_nonce, block_parenthash, block_size, uncle_hash + FROM blocks + WHERE node_id = $1`, repository.nodeId) var savedBlocks []core.Block for blockRows.Next() { savedBlock := repository.loadBlock(blockRows) @@ -116,10 +144,10 @@ func (repository Postgres) CreateBlock(block core.Block) error { var blockId int64 err := tx.QueryRow( `INSERT INTO blocks - (block_number, block_gaslimit, block_gasused, block_time, block_difficulty, block_hash, block_nonce, block_parenthash, block_size, uncle_hash) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) - RETURNING id `, - block.Number, block.GasLimit, block.GasUsed, block.Time, block.Difficulty, block.Hash, block.Nonce, block.ParentHash, block.Size, block.UncleHash). + (node_id, block_number, block_gaslimit, block_gasused, block_time, block_difficulty, block_hash, block_nonce, block_parenthash, block_size, uncle_hash) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) + RETURNING id `, + repository.nodeId, block.Number, block.GasLimit, block.GasUsed, block.Time, block.Difficulty, block.Hash, block.Nonce, block.ParentHash, block.Size, block.UncleHash). Scan(&blockId) if err != nil { tx.Rollback() @@ -138,8 +166,8 @@ func (repository Postgres) createTransactions(tx *sql.Tx, blockId int64, transac for _, transaction := range transactions { _, err := tx.Exec( `INSERT INTO transactions - (block_id, tx_hash, tx_nonce, tx_to, tx_from, tx_gaslimit, tx_gasprice, tx_value) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, + (block_id, tx_hash, tx_nonce, tx_to, tx_from, tx_gaslimit, tx_gasprice, tx_value) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, blockId, transaction.Hash, transaction.Nonce, transaction.To, transaction.From, transaction.GasLimit, transaction.GasPrice, transaction.Value) if err != nil { return err @@ -204,7 +232,7 @@ func (repository Postgres) loadTransactions(transactionRows *sql.Rows) []core.Tr } func (repository Postgres) addTransactions(contract core.Contract) core.Contract { - transactionRows, _ := repository.Db.Query(`SELECT tx_hash, tx_nonce, tx_to, tx_from, tx_gaslimit, tx_gasprice, tx_value FROM transactions WHERE tx_to = $1 ORDER BY block_id desc`, contract.Hash) + transactionRows, _ := repository.Db.Query(`SELECT tx_hash, tx_nonce, tx_to, tx_from, tx_gaslimit, tx_gasprice, tx_value FROM transactions WHERE tx_to = $1 ORDER BY block_id DESC`, contract.Hash) transactions := repository.loadTransactions(transactionRows) savedContract := core.Contract{Hash: contract.Hash, Transactions: transactions, Abi: contract.Abi} return savedContract diff --git a/pkg/repositories/postgres_test.go b/pkg/repositories/postgres_test.go index e50c8728..f5d7895f 100644 --- a/pkg/repositories/postgres_test.go +++ b/pkg/repositories/postgres_test.go @@ -24,9 +24,9 @@ var _ = Describe("Postgres repository", func() { Expect(db).ShouldNot(BeNil()) }) - testing.AssertRepositoryBehavior(func() repositories.Repository { + testing.AssertRepositoryBehavior(func(node core.Node) repositories.Repository { cfg, _ := config.NewConfig("private") - repository, _ := repositories.NewPostgres(cfg.Database) + repository, _ := repositories.NewPostgres(cfg.Database, node) testing.ClearData(repository) return repository }) @@ -40,7 +40,8 @@ var _ = Describe("Postgres repository", func() { Transactions: []core.Transaction{}, } cfg, _ := config.NewConfig("private") - repository, _ := repositories.NewPostgres(cfg.Database) + node := core.Node{GenesisBlock: "GENESIS", NetworkId: 1} + repository, _ := repositories.NewPostgres(cfg.Database, node) err := repository.CreateBlock(badBlock) savedBlock := repository.FindBlockByNumber(123) @@ -51,10 +52,19 @@ var _ = Describe("Postgres repository", func() { It("throws error when can't connect to the database", func() { invalidDatabase := config.Database{} - _, err := repositories.NewPostgres(invalidDatabase) + node := core.Node{GenesisBlock: "GENESIS", NetworkId: 1} + _, err := repositories.NewPostgres(invalidDatabase, node) Expect(err).To(Equal(repositories.ErrDBConnectionFailed)) }) + It("throws error when can't create node", func() { + cfg, _ := config.NewConfig("private") + badHash := fmt.Sprintf("x %s", strings.Repeat("1", 100)) + node := core.Node{GenesisBlock: badHash, NetworkId: 1} + _, err := repositories.NewPostgres(cfg.Database, node) + Expect(err).To(Equal(repositories.ErrUnableToSetNode)) + }) + It("does not commit block or transactions if transaction is invalid", func() { //badHash violates db To field length badHash := fmt.Sprintf("x %s", strings.Repeat("1", 100)) @@ -64,7 +74,8 @@ var _ = Describe("Postgres repository", func() { Transactions: []core.Transaction{badTransaction}, } cfg, _ := config.NewConfig("private") - repository, _ := repositories.NewPostgres(cfg.Database) + node := core.Node{GenesisBlock: "GENESIS", NetworkId: 1} + repository, _ := repositories.NewPostgres(cfg.Database, node) err := repository.CreateBlock(block) savedBlock := repository.FindBlockByNumber(123) diff --git a/pkg/repositories/testing/helpers.go b/pkg/repositories/testing/helpers.go index 7d0b7fb2..6b93ffb6 100644 --- a/pkg/repositories/testing/helpers.go +++ b/pkg/repositories/testing/helpers.go @@ -13,11 +13,12 @@ func ClearData(postgres repositories.Postgres) { postgres.Db.MustExec("DELETE FROM blocks") } -func AssertRepositoryBehavior(buildRepository func() repositories.Repository) { +func AssertRepositoryBehavior(buildRepository func(node core.Node) repositories.Repository) { var repository repositories.Repository BeforeEach(func() { - repository = buildRepository() + node := core.Node{GenesisBlock: "GENESIS", NetworkId: 1} + repository = buildRepository(node) }) Describe("Saving blocks", func() { @@ -34,6 +35,21 @@ func AssertRepositoryBehavior(buildRepository func() repositories.Repository) { Expect(repository.BlockCount()).To(Equal(1)) }) + It("associates blocks to a node", func() { + block := core.Block{ + Number: 123, + } + repository.CreateBlock(block) + nodeTwo := core.Node{ + GenesisBlock: "0x456", + NetworkId: 1, + } + repositoryTwo := buildRepository(nodeTwo) + + foundBlock := repositoryTwo.FindBlockByNumber(123) + Expect(foundBlock).To(BeNil()) + }) + It("saves the attributes of the block", func() { blockNumber := int64(123) gasLimit := int64(1000000)