Merge pull request #17 from ShyftNetwork/dustin-accounts

Dustin accounts
This commit is contained in:
Tim Williams 2018-04-26 16:33:08 -04:00 committed by GitHub
commit bd6acd8059
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 265 additions and 153 deletions

View file

@ -54,25 +54,47 @@ func GetAllTransactions(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, txs) fmt.Fprintln(w, txs)
} }
// GetBalance gets balance // GetAccount gets balance
func GetBalance(w http.ResponseWriter, r *http.Request) { func GetAccount(w http.ResponseWriter, r *http.Request) {
// vars := mux.Vars(r) vars := mux.Vars(r)
// address := vars["address"] address := vars["address"]
//addressBytes := []byte(address) //addressBytes := []byte(address)
fmt.Println("ADDRESS FROM ROUTE", address)
w.Header().Set("Content-Type", "application/json; charset=UTF-8") connStr := "user=postgres dbname=shyftdb sslmode=disable"
w.WriteHeader(http.StatusOK) blockExplorerDb, err := sql.Open("postgres", connStr)
if err != nil {
//fmt.Fprintln(w, "Get Balances", addresses) return
} }
// GetBalances gets balances getAccountBalance := shyftdb.GetAccount(blockExplorerDb, address)
func GetBalances(w http.ResponseWriter, r *http.Request) {
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
//fmt.Fprintln(w, "Get Balances", addresses) fmt.Fprintln(w, getAccountBalance)
}
// GetAllAccounts gets balances
func GetAllAccounts(w http.ResponseWriter, r *http.Request) {
connStr := "user=postgres dbname=shyftdb sslmode=disable"
blockExplorerDb, err := sql.Open("postgres", connStr)
if err != nil {
return
}
allAccounts := shyftdb.GetAllAccounts(blockExplorerDb)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, allAccounts)
} }
//GetBlock returns block json //GetBlock returns block json
@ -98,23 +120,18 @@ func GetBlock(w http.ResponseWriter, r *http.Request) {
// GetAllBlocks response // GetAllBlocks response
func GetAllBlocks(w http.ResponseWriter, r *http.Request) { func GetAllBlocks(w http.ResponseWriter, r *http.Request) {
connStr := "user=postgres dbname=shyftdb sslmode=disable" connStr := "user=postgres dbname=shyftdb sslmode=disable"
blockExplorerDb, err := sql.Open("postgres", connStr) blockExplorerDb, err := sql.Open("postgres", connStr)
if err != nil { if err != nil {
return return
} }
block3 := shyftdb.GetAllBlocks(blockExplorerDb) block3 := shyftdb.GetAllBlocks(blockExplorerDb)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, block3) fmt.Fprintln(w, block3)
} }

View file

@ -16,16 +16,16 @@ type Routes []Route
var routes = Routes{ var routes = Routes{
Route{ Route{
"GetBalance", "GetAccount",
"GET", "GET",
"/api/get_balance/", "/api/get_account/{address}",
GetBalance, GetAccount,
}, },
Route{ Route{
"GetBalances", "GetAllAccounts",
"GET", "GET",
"/api/get_balances/{addresses}", "/api/get_all_accounts",
GetBalances, GetAllAccounts,
}, },
Route{ Route{
"GetAllBlocks", "GetAllBlocks",

View file

@ -8,11 +8,15 @@ CREATE TABLE IF NOT EXISTS txs (
txHash text, txHash text,
to_addr text, to_addr text,
from_addr text, from_addr text,
blockhash text, blockhash text references blocks(hash),
amount numeric, amount numeric,
gasprice numeric, gasprice numeric,
gas numeric, gas numeric,
nonce numeric, nonce numeric,
data bytea, data bytea
block text references blocks(hash) );
CREATE TABLE IF NOT EXISTS accounts (
addr text primary key unique,
balance numeric
); );

View file

@ -1,2 +1,3 @@
DROP TABLE txs; DROP TABLE txs;
DROP TABLE blocks; DROP TABLE blocks;
DROP TABLE accounts;

View file

@ -1,19 +1,17 @@
package shyftdb package shyftdb
import ( import (
"bytes"
"encoding/gob"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/syndtr/goleveldb/leveldb"
"database/sql" "database/sql"
"log"
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
@ -32,8 +30,15 @@ type blockRes struct {
Blocks []SBlock Blocks []SBlock
} }
type txRes struct { type SAccounts struct {
TxEntry []ShyftTxEntryPretty Addr string
Balance string
}
type accountRes struct {
addr string
balance string
AllAccounts []SAccounts
} }
//ShyftTxEntry structure //ShyftTxEntry structure
@ -49,6 +54,10 @@ type ShyftTxEntry struct {
Data []byte Data []byte
} }
type txRes struct {
TxEntry []ShyftTxEntryPretty
}
type ShyftTxEntryPretty struct { type ShyftTxEntryPretty struct {
TxHash string TxHash string
To string To string
@ -62,17 +71,25 @@ type ShyftTxEntryPretty struct {
} }
type ShyftAccountEntry struct { type ShyftAccountEntry struct {
Balance *big.Int Balance string
Txs []string Txs []string
} }
type SendAndReceive struct {
To string
From string
Amount string
Address string
Balance string
}
//WriteBlock writes to block info to sql db //WriteBlock writes to block info to sql db
func WriteBlock(sqldb *sql.DB, block *types.Block) error { func WriteBlock(sqldb *sql.DB, block *types.Block) error {
coinbase := block.Header().Coinbase.String() coinbase := block.Header().Coinbase.String()
number := block.Header().Number.String() number := block.Header().Number.String()
sqlStatement := `INSERT INTO blocks(hash, coinbase, number) VALUES(($1), ($2), ($3)) RETURNING number` sqlStatement := `INSERT INTO blocks(hash, coinbase, number) VALUES(($1), ($2), ($3)) RETURNING number`
qerr := sqldb.QueryRow(sqlStatement, block.Header().Hash().Hex(), coinbase, number).Scan(&number) //.Scan(&fun) qerr := sqldb.QueryRow(sqlStatement, block.Header().Hash().Hex(), coinbase, number).Scan(&number)
if qerr != nil { if qerr != nil {
panic(qerr) panic(qerr)
} }
@ -80,7 +97,7 @@ func WriteBlock(sqldb *sql.DB, block *types.Block) error {
if block.Transactions().Len() > 0 { if block.Transactions().Len() > 0 {
for _, tx := range block.Transactions() { for _, tx := range block.Transactions() {
WriteTransactions(sqldb, tx, block.Header().Hash()) WriteTransactions(sqldb, tx, block.Header().Hash())
//tx_bytes[i] = tx.Hash().Bytes() WriteFromBalance(sqldb, tx)
} }
} }
return nil return nil
@ -130,68 +147,90 @@ func WriteTransactions(sqldb *sql.DB, tx *types.Transaction, blockHash common.Ha
return nil return nil
} }
//WriteAccountBalances(db, tx) //WriteFromBalance writes senders balance to accounts db
func WriteFromBalance(sqldb *sql.DB, tx *types.Transaction) error {
sendAndReceiveData, balanceRec, balanceSen := WriteBalanceHelper(sqldb, tx)
toAddr := sendAndReceiveData.To
fromAddr := sendAndReceiveData.From
amount := sendAndReceiveData.Amount
balanceReceiver := balanceRec
balanceSender := balanceSen
// func WriteFromBalance(db *leveldb.DB, tx *types.Transaction) { var response string
// key := append([]byte("acc-")[:], tx.From().Hash().Bytes()[:]...) sqlExistsStatement := `SELECT balance from accounts WHERE addr = ($1)`
// // The from (sender) addr must have balance. If it fails to retrieve there is a bigger issue. err := sqldb.QueryRow(sqlExistsStatement, toAddr).Scan(&response)
// retrievedData, err := db.Get(key, nil) switch {
// if err != nil { case err == sql.ErrNoRows:
// log.Crit("From MUST have eth and no record found", "err", err) fmt.Println("No rows error :)")
// }
// var decodedData ShyftAccountEntry
// d := gob.NewDecoder(bytes.NewBuffer(retrievedData))
// if err := d.Decode(&decodedData); err != nil {
// log.Crit("Failed to decode From data:", "err", err)
// }
// decodedData.Balance.Sub(decodedData.Balance, tx.Value())
// decodedData.Txs = append(decodedData.Txs, tx.Hash().String())
// // Encode updated data
// var encodedData bytes.Buffer
// encoder := gob.NewEncoder(&encodedData)
// if err := encoder.Encode(decodedData); err != nil {
// log.Crit("Faild to encode From Account data", "err", err)
// }
// if err := db.Put(key, encodedData.Bytes(), nil); err != nil {
// log.Crit("Could not write the From account data", "err", err)
// }
// }
func WriteToBalance(db *leveldb.DB, tx *types.Transaction) { sqlStatement := `INSERT INTO accounts(addr, balance) VALUES(($1), ($2)) RETURNING addr`
key := append([]byte("acc-")[:], tx.To().Hash().Bytes()[:]...) insertErr := sqldb.QueryRow(sqlStatement, toAddr, amount).Scan(&toAddr)
var txs []string if insertErr != nil {
panic(insertErr)
}
case err != nil:
log.Fatal(err)
default:
retrievedData, err := db.Get(key, nil) var newBalanceReceiver big.Int
var newBalanceSender big.Int
updateSQLStatement := `UPDATE accounts SET balance = ($2) WHERE addr = ($1)`
r := new(big.Int)
_, err := fmt.Sscan(balanceReceiver, r)
if err != nil { if err != nil {
accData := ShyftAccountEntry{ log.Println("error scanning value:", err)
Balance: tx.Value(),
Txs: append(txs, tx.Hash().String()),
} }
var encodedData bytes.Buffer
encoder := gob.NewEncoder(&encodedData) s := new(big.Int)
if err := encoder.Encode(accData); err != nil { _, error := fmt.Sscan(balanceSender, s)
log.Crit("Faild to encode To Account data", "err", err) if error != nil {
log.Println("error scanning value:", error)
} }
if err := db.Put(key, encodedData.Bytes(), nil); err != nil {
log.Crit("Could not write the TO account's first tx", "err", err) newBalanceReceiver.Add(r, tx.Value())
newBalanceSender.Sub(s, tx.Value())
_, err = sqldb.Exec(updateSQLStatement, toAddr, newBalanceReceiver.String())
if err != nil {
panic(err)
}
_, err = sqldb.Exec(updateSQLStatement, fromAddr, newBalanceSender.String())
if err != nil {
panic(err)
} }
} }
var decodedData ShyftAccountEntry return nil
d := gob.NewDecoder(bytes.NewBuffer(retrievedData))
if err := d.Decode(&decodedData); err != nil {
log.Crit("Failed to decode To account data:", "err", err)
} }
decodedData.Balance.Add(decodedData.Balance, tx.Value())
decodedData.Txs = append(decodedData.Txs, tx.Hash().String()) func WriteBalanceHelper(sqldb *sql.DB, tx *types.Transaction) (SendAndReceive, string, string) {
// Encode updated data sendAndReceiveData := SendAndReceive{
var encodedData bytes.Buffer To: tx.To().Hex(),
encoder := gob.NewEncoder(&encodedData) From: tx.From().Hex(),
if err := encoder.Encode(decodedData); err != nil { Amount: tx.Value().String(),
log.Crit("Faild to encode To Account data", "err", err)
} }
if err := db.Put(key, encodedData.Bytes(), nil); err != nil {
log.Crit("Could not write the To account data", "err", err) toAddr := sendAndReceiveData.To
fromAddr := sendAndReceiveData.From
getAccountBalanceReceiver := GetAccount(sqldb, toAddr)
getAccountBalanceSender:= GetAccount(sqldb, fromAddr)
var receiverBalance SendAndReceive
if err := json.Unmarshal([]byte(getAccountBalanceReceiver), &receiverBalance); err != nil {
log.Fatal(err)
} }
var senderBalance SendAndReceive
if err := json.Unmarshal([]byte(getAccountBalanceSender), &senderBalance); err != nil {
log.Fatal(err)
}
balanceReceiver := receiverBalance.Balance
balanceSender := senderBalance.Balance
return sendAndReceiveData, balanceReceiver, balanceSender
} }
// @NOTE: This function is extremely complex and requires heavy testing and knowdlege of edge cases: // @NOTE: This function is extremely complex and requires heavy testing and knowdlege of edge cases:
@ -200,57 +239,57 @@ func WriteToBalance(db *leveldb.DB, tx *types.Transaction) {
// @TODO: Calculate reward if there are uncles // @TODO: Calculate reward if there are uncles
// @TODO: Calculate mining reward (most likely retrieve higher up in the operations) // @TODO: Calculate mining reward (most likely retrieve higher up in the operations)
// @TODO: Calculate reorg // @TODO: Calculate reorg
func WriteMinerReward(db *leveldb.DB, block *types.Block) { // func WriteMinerReward(db *leveldb.DB, block *types.Block) {
var totalGas *big.Int // var totalGas *big.Int
var txs []string // var txs []string
key := append([]byte("acc-")[:], block.Coinbase().Hash().Bytes()[:]...) // key := append([]byte("acc-")[:], block.Coinbase().Hash().Bytes()[:]...)
for _, tx := range block.Transactions() { // for _, tx := range block.Transactions() {
totalGas.Add(totalGas, new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))) // totalGas.Add(totalGas, new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())))
} // }
retrievedData, err := db.Get(key, nil) // retrievedData, err := db.Get(key, nil)
if err != nil { // if err != nil {
// Assume time this account has had a tx // // Assume time this account has had a tx
// Balacne is exclusively minerreward + total gas from the block b/c no prior evm activity // // Balacne is exclusively minerreward + total gas from the block b/c no prior evm activity
// Txs would be empty because they have not had any transactions on the EVM // // Txs would be empty because they have not had any transactions on the EVM
// @TODO: Calc mining reward // // @TODO: Calc mining reward
//balance := totalGas.Add(totalGas, MINING_REWARD) // //balance := totalGas.Add(totalGas, MINING_REWARD)
balance := totalGas // balance := totalGas
accData := ShyftAccountEntry{ // accData := ShyftAccountEntry{
Balance: balance, // Balance: balance,
Txs: txs, // Txs: txs,
} // }
var encodedData bytes.Buffer // var encodedData bytes.Buffer
encoder := gob.NewEncoder(&encodedData) // encoder := gob.NewEncoder(&encodedData)
if err := encoder.Encode(accData); err != nil { // if err := encoder.Encode(accData); err != nil {
log.Crit("Faild to encode Miner Account data", "err", err) // log.Crit("Faild to encode Miner Account data", "err", err)
} // }
if err := db.Put(key, encodedData.Bytes(), nil); err != nil { // if err := db.Put(key, encodedData.Bytes(), nil); err != nil {
log.Crit("Could not write the miner's first tx", "err", err) // log.Crit("Could not write the miner's first tx", "err", err)
} // }
} else { // } else {
// The account has already have previous data stored due to activity in the EVM // // The account has already have previous data stored due to activity in the EVM
// Decode the data to update balance // // Decode the data to update balance
var decodedData ShyftAccountEntry // var decodedData ShyftAccountEntry
d := gob.NewDecoder(bytes.NewBuffer(retrievedData)) // d := gob.NewDecoder(bytes.NewBuffer(retrievedData))
if err := d.Decode(&decodedData); err != nil { // if err := d.Decode(&decodedData); err != nil {
log.Crit("Failed to decode miner data:", "err", err) // log.Crit("Failed to decode miner data:", "err", err)
} // }
// Write new balance // // Write new balance
// @TODO: Calc mining reward // // @TODO: Calc mining reward
// decodedData.Balance.Add(decodedData.Balance, totalGas.Add(totalGas, MINING_REWARD))) // // decodedData.Balance.Add(decodedData.Balance, totalGas.Add(totalGas, MINING_REWARD)))
decodedData.Balance.Add(decodedData.Balance, totalGas) // decodedData.Balance.Add(decodedData.Balance, totalGas)
// Encode the data to be written back to the db // // Encode the data to be written back to the db
var encodedData bytes.Buffer // var encodedData bytes.Buffer
encoder := gob.NewEncoder(&encodedData) // encoder := gob.NewEncoder(&encodedData)
if err := encoder.Encode(decodedData); err != nil { // if err := encoder.Encode(decodedData); err != nil {
log.Crit("Faild to encode Miner Account data", "err", err) // log.Crit("Faild to encode Miner Account data", "err", err)
} // }
// Write newly encoded data back to the db // // Write newly encoded data back to the db
if err := db.Put(key, encodedData.Bytes(), nil); err != nil { // if err := db.Put(key, encodedData.Bytes(), nil); err != nil {
log.Crit("Could not update miner account data", "err", err) // log.Crit("Could not update miner account data", "err", err)
} // }
} // }
} // }
/////////// ///////////
// Getters // Getters
@ -406,3 +445,55 @@ func GetTransaction(sqldb *sql.DB) string {
return string(json) return string(json)
} }
//GetAccount returns account balances
func GetAccount(sqldb *sql.DB, address string) string {
sqlStatement := `SELECT * FROM accounts WHERE addr=$1;`
row := sqldb.QueryRow(sqlStatement, address)
var addr string
var balance string
row.Scan(&addr, &balance)
account := SAccounts{
Addr: addr,
Balance: balance,
}
json, _ := json.Marshal(account)
return string(json)
}
//GetAllAccounts returns all accounts and balances
func GetAllAccounts(sqldb *sql.DB) string {
var array accountRes
var accountsArr string
accs, err := sqldb.Query(`
SELECT
addr,
balance
FROM accounts`)
if err != nil {
fmt.Println(err)
}
defer accs.Close()
////
for accs.Next() {
var addr string
var balance string
err = accs.Scan(
&addr,
&balance,
)
array.AllAccounts = append(array.AllAccounts, SAccounts{
Addr: addr,
Balance: balance,
})
accounts, _ := json.Marshal(array.AllAccounts)
accountsFmt := string(accounts)
accountsArr = accountsFmt
}
return accountsArr
}

View file

@ -2,18 +2,17 @@ var firstAccount = web3.eth.accounts[0]
var secondAccount = web3.eth.accounts[1] var secondAccount = web3.eth.accounts[1]
var thirdAccount = web3.eth.accounts[2] var thirdAccount = web3.eth.accounts[2]
for (var i = 0; i < 1; i++) { for (var i = 0; i < 10; i++) {
console.log('\t\t' + (i + 1) + ' - Transactions') console.log('\t\t' + (i + 1) + ' - Transactions')
web3.eth.sendTransaction({ web3.eth.sendTransaction({
from: web3.eth.accounts[0], from: web3.eth.accounts[2],
to: web3.eth.accounts[1], to: web3.eth.accounts[0],
value: 623, value: 5,
gas: 50000, gas: 50000,
gasPrice: 20 gasPrice: 20
}); });
web3.eth.sendTransaction({ web3.eth.sendTransaction({
from: web3.eth.accounts[0], from: web3.eth.accounts[1],
to: web3.eth.accounts[2], to: web3.eth.accounts[2],
value: 291, value: 291,
gas: 50000, gas: 50000,
@ -21,8 +20,8 @@ for (var i = 0; i < 1; i++) {
}); });
web3.eth.sendTransaction({ web3.eth.sendTransaction({
from: web3.eth.accounts[1], from: web3.eth.accounts[0],
to: web3.eth.accounts[3], to: web3.eth.accounts[1],
value: 53039, value: 53039,
gas: 50000, gas: 50000,
gasPrice: 20 gasPrice: 20