2017-11-03 13:01:35 +00:00
package repositories
import (
"database/sql"
2017-11-07 17:56:49 +00:00
"context"
"errors"
2017-11-09 14:00:02 +00:00
2017-11-09 14:51:14 +00:00
"github.com/8thlight/vulcanizedb/pkg/config"
2017-11-06 18:53:43 +00:00
"github.com/8thlight/vulcanizedb/pkg/core"
2017-11-03 13:01:35 +00:00
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
type Postgres struct {
Db * sqlx . DB
}
2017-11-09 19:59:12 +00:00
var (
2017-12-04 15:53:36 +00:00
ErrDBInsertFailed = errors . New ( "postgres: insert failed" )
ErrDBConnectionFailed = errors . New ( "postgres: db connection failed" )
2017-11-09 19:59:12 +00:00
)
2017-12-04 15:53:36 +00:00
func NewPostgres ( databaseConfig config . Database ) ( Postgres , error ) {
2017-11-09 14:51:14 +00:00
connectString := config . DbConnectionString ( databaseConfig )
db , err := sqlx . Connect ( "postgres" , connectString )
if err != nil {
2017-12-04 15:53:36 +00:00
return Postgres { } , ErrDBConnectionFailed
2017-11-09 14:51:14 +00:00
}
2017-12-04 15:53:36 +00:00
return Postgres { Db : db } , nil
2017-11-09 14:51:14 +00:00
}
2017-12-04 22:54:35 +00:00
func ( repository Postgres ) CreateContract ( contract core . Contract ) error {
2017-12-04 20:13:15 +00:00
abi := contract . Abi
var abiToInsert * string
if abi != "" {
abiToInsert = & abi
}
2017-11-09 19:59:12 +00:00
_ , err := repository . Db . Exec (
2017-12-04 20:13:15 +00:00
` INSERT INTO watched_contracts (contract_hash, contract_abi) VALUES ($1, $2) ` , contract . Hash , abiToInsert )
2017-11-09 19:59:12 +00:00
if err != nil {
return ErrDBInsertFailed
}
return nil
}
2017-12-04 22:54:35 +00:00
func ( repository Postgres ) ContractExists ( contractHash string ) bool {
2017-11-09 19:59:12 +00:00
var exists bool
2017-12-04 15:53:36 +00:00
repository . Db . QueryRow (
2017-11-13 21:41:32 +00:00
` SELECT exists(SELECT 1 FROM watched_contracts WHERE contract_hash=$1) FROM watched_contracts ` , contractHash ) . Scan ( & exists )
2017-11-09 19:59:12 +00:00
return exists
}
2017-11-07 17:56:49 +00:00
2017-12-04 22:54:35 +00:00
func ( repository Postgres ) FindContract ( contractHash string ) * core . Contract {
var savedContracts [ ] core . Contract
2017-11-13 15:58:36 +00:00
contractRows , _ := repository . Db . Query (
2017-12-04 20:13:15 +00:00
` SELECT contract_hash, contract_abi FROM watched_contracts WHERE contract_hash=$1 ` , contractHash )
2017-11-13 21:41:32 +00:00
savedContracts = repository . loadContract ( contractRows )
2017-11-13 15:58:36 +00:00
if len ( savedContracts ) > 0 {
return & savedContracts [ 0 ]
} else {
return nil
}
}
2017-11-06 20:36:12 +00:00
func ( repository Postgres ) MaxBlockNumber ( ) int64 {
var highestBlockNumber int64
repository . Db . Get ( & highestBlockNumber , ` SELECT MAX(block_number) FROM blocks ` )
return highestBlockNumber
}
func ( repository Postgres ) MissingBlockNumbers ( startingBlockNumber int64 , highestBlockNumber int64 ) [ ] int64 {
numbers := [ ] int64 { }
repository . Db . Select ( & numbers ,
` SELECT all_block_numbers
FROM (
SELECT generate_series ( $ 1 : : INT , $ 2 : : INT ) AS all_block_numbers ) series
LEFT JOIN blocks
ON block_number = all_block_numbers
WHERE block_number ISNULL ` ,
startingBlockNumber ,
highestBlockNumber )
return numbers
}
2017-11-03 13:01:35 +00:00
func ( repository Postgres ) FindBlockByNumber ( blockNumber int64 ) * core . Block {
2017-11-06 20:36:12 +00:00
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 ` )
2017-11-03 13:01:35 +00:00
var savedBlocks [ ] core . Block
for blockRows . Next ( ) {
savedBlock := repository . loadBlock ( blockRows )
savedBlocks = append ( savedBlocks , savedBlock )
}
if len ( savedBlocks ) > 0 {
return & savedBlocks [ 0 ]
} else {
return nil
}
}
func ( repository Postgres ) BlockCount ( ) int {
var count int
2017-11-13 21:41:32 +00:00
repository . Db . Get ( & count , ` SELECT COUNT(*) FROM blocks ` )
2017-11-03 13:01:35 +00:00
return count
}
2017-11-07 17:56:49 +00:00
func ( repository Postgres ) CreateBlock ( block core . Block ) error {
tx , _ := repository . Db . BeginTx ( context . Background ( ) , nil )
var blockId int64
err := tx . QueryRow (
2017-11-06 20:36:12 +00:00
` INSERT INTO blocks
( block_number , block_gaslimit , block_gasused , block_time , block_difficulty , block_hash , block_nonce , block_parenthash , block_size , uncle_hash )
2017-11-07 17:56:49 +00:00
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 ) .
Scan ( & blockId )
if err != nil {
tx . Rollback ( )
return ErrDBInsertFailed
}
err = repository . createTransactions ( tx , blockId , block . Transactions )
if err != nil {
tx . Rollback ( )
return ErrDBInsertFailed
}
tx . Commit ( )
return nil
2017-11-03 13:01:35 +00:00
}
2017-11-07 17:56:49 +00:00
func ( repository Postgres ) createTransactions ( tx * sql . Tx , blockId int64 , transactions [ ] core . Transaction ) error {
2017-11-03 13:01:35 +00:00
for _ , transaction := range transactions {
2017-11-07 17:56:49 +00:00
_ , err := tx . Exec (
2017-11-06 20:36:12 +00:00
` INSERT INTO transactions
2017-11-08 20:55:35 +00:00
( 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 )
2017-11-07 17:56:49 +00:00
if err != nil {
return err
}
2017-11-03 13:01:35 +00:00
}
2017-11-07 17:56:49 +00:00
return nil
2017-11-03 13:01:35 +00:00
}
func ( repository Postgres ) loadBlock ( blockRows * sql . Rows ) core . Block {
var blockId int64
var blockHash string
var blockNonce string
var blockNumber int64
var blockParentHash string
var blockSize int64
var blockTime float64
var difficulty int64
var gasLimit float64
var gasUsed float64
var uncleHash string
blockRows . Scan ( & blockId , & blockNumber , & gasLimit , & gasUsed , & blockTime , & difficulty , & blockHash , & blockNonce , & blockParentHash , & blockSize , & uncleHash )
2017-11-13 21:41:32 +00:00
transactionRows , _ := repository . Db . Query ( ` SELECT tx_hash, tx_nonce, tx_to, tx_from, tx_gaslimit, tx_gasprice, tx_value FROM transactions WHERE block_id = $1 ` , blockId )
transactions := repository . loadTransactions ( transactionRows )
2017-11-03 13:01:35 +00:00
return core . Block {
Difficulty : difficulty ,
GasLimit : int64 ( gasLimit ) ,
GasUsed : int64 ( gasUsed ) ,
Hash : blockHash ,
Nonce : blockNonce ,
Number : blockNumber ,
ParentHash : blockParentHash ,
Size : blockSize ,
Time : int64 ( blockTime ) ,
Transactions : transactions ,
UncleHash : uncleHash ,
}
}
2017-11-13 21:41:32 +00:00
func ( repository Postgres ) loadTransactions ( transactionRows * sql . Rows ) [ ] core . Transaction {
2017-11-03 13:01:35 +00:00
var transactions [ ] core . Transaction
for transactionRows . Next ( ) {
var hash string
var nonce uint64
var to string
2017-11-08 20:55:35 +00:00
var from string
2017-11-03 13:01:35 +00:00
var gasLimit int64
var gasPrice int64
var value int64
2017-11-08 20:55:35 +00:00
transactionRows . Scan ( & hash , & nonce , & to , & from , & gasLimit , & gasPrice , & value )
2017-11-03 13:01:35 +00:00
transaction := core . Transaction {
Hash : hash ,
Nonce : nonce ,
To : to ,
2017-11-08 20:55:35 +00:00
From : from ,
2017-11-03 13:01:35 +00:00
GasLimit : gasLimit ,
GasPrice : gasPrice ,
Value : value ,
}
transactions = append ( transactions , transaction )
}
return transactions
}
2017-11-13 21:41:32 +00:00
2017-12-04 22:54:35 +00:00
func ( repository Postgres ) loadContract ( contractRows * sql . Rows ) [ ] core . Contract {
var savedContracts [ ] core . Contract
2017-11-13 21:41:32 +00:00
for contractRows . Next ( ) {
var savedContractHash string
2017-12-04 20:13:15 +00:00
var savedContractAbi string
contractRows . Scan ( & savedContractHash , & savedContractAbi )
2017-11-14 15:14:34 +00:00
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 ` , savedContractHash )
2017-11-13 21:41:32 +00:00
transactions := repository . loadTransactions ( transactionRows )
2017-12-04 22:54:35 +00:00
savedContract := core . Contract { Hash : savedContractHash , Transactions : transactions , Abi : savedContractAbi }
2017-11-13 21:41:32 +00:00
savedContracts = append ( savedContracts , savedContract )
}
return savedContracts
}