Merge pull request #116 from 8thlight/add_block_fields

Add block fields
This commit is contained in:
Matt K 2017-12-28 12:04:02 -06:00 committed by GitHub
commit 7a09839c23
14 changed files with 301 additions and 24 deletions

View File

@ -0,0 +1,2 @@
ALTER TABLE blocks
DROP COLUMN block_miner;

View File

@ -0,0 +1,2 @@
ALTER TABLE blocks
ADD COLUMN block_miner VARCHAR(42);

View File

@ -0,0 +1,2 @@
ALTER TABLE blocks
DROP COLUMN block_extra_data;

View File

@ -0,0 +1,2 @@
ALTER TABLE blocks
ADD COLUMN block_extra_data VARCHAR;

View File

@ -0,0 +1,3 @@
ALTER TABLE blocks
DROP COLUMN block_reward,
DROP COLUMN block_uncles_reward;

View File

@ -0,0 +1,3 @@
ALTER TABLE blocks
ADD COLUMN block_reward NUMERIC,
ADD COLUMN block_uncles_reward NUMERIC;

View File

@ -51,7 +51,11 @@ CREATE TABLE blocks (
block_size bigint,
uncle_hash character varying(66),
node_id integer NOT NULL,
is_final boolean
is_final boolean,
block_miner character varying(42),
block_extra_data character varying,
block_reward numeric,
block_uncles_reward numeric
);

View File

@ -0,0 +1,34 @@
package integration
import (
"log"
cfg "github.com/8thlight/vulcanizedb/pkg/config"
"github.com/8thlight/vulcanizedb/pkg/geth"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Rewards calculations", func() {
It("calculates a block reward for a real block", func() {
config, err := cfg.NewConfig("infura")
if err != nil {
log.Fatalln(err)
}
blockchain := geth.NewGethBlockchain(config.Client.IPCPath)
block := blockchain.GetBlockByNumber(1071819)
Expect(block.Reward).To(Equal(5.31355))
})
It("calculates an uncle reward for a real block", func() {
config, err := cfg.NewConfig("infura")
if err != nil {
log.Fatalln(err)
}
blockchain := geth.NewGethBlockchain(config.Client.IPCPath)
block := blockchain.GetBlockByNumber(1071819)
Expect(block.UnclesReward).To(Equal(6.875))
})
})

View File

@ -1,10 +1,14 @@
package core
type Block struct {
Reward float64
Difficulty int64
ExtraData string
GasLimit int64
GasUsed int64
Hash string
IsFinal bool
Miner string
Nonce string
Number int64
ParentHash string
@ -12,5 +16,5 @@ type Block struct {
Time int64
Transactions []Transaction
UncleHash string
IsFinal bool
UnclesReward float64
}

View File

@ -0,0 +1,58 @@
package geth
import (
"context"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
func CalcUnclesReward(gethBlock *types.Block) float64 {
var unclesReward float64
for _, uncle := range gethBlock.Uncles() {
blockNumber := gethBlock.Number().Int64()
staticBlockReward := float64(staticRewardByBlockNumber(blockNumber))
unclesReward += (1.0 + float64(uncle.Number.Int64()-gethBlock.Number().Int64())/8.0) * staticBlockReward
}
return unclesReward
}
func CalcBlockReward(gethBlock *types.Block, client GethClient) float64 {
blockNumber := gethBlock.Number().Int64()
staticBlockReward := staticRewardByBlockNumber(blockNumber)
transactionFees := calcTransactionFees(gethBlock, client)
uncleInclusionRewards := calcUncleInclusionRewards(gethBlock)
return transactionFees + uncleInclusionRewards + staticBlockReward
}
func calcUncleInclusionRewards(gethBlock *types.Block) float64 {
var uncleInclusionRewards float64
staticBlockReward := staticRewardByBlockNumber(gethBlock.Number().Int64())
for range gethBlock.Uncles() {
uncleInclusionRewards += staticBlockReward * 1 / 32
}
return uncleInclusionRewards
}
func calcTransactionFees(gethBlock *types.Block, client GethClient) float64 {
var transactionFees float64
for _, transaction := range gethBlock.Transactions() {
receipt, err := client.TransactionReceipt(context.Background(), transaction.Hash())
if err != nil {
continue
}
transactionFees += float64(transaction.GasPrice().Int64() * receipt.GasUsed.Int64())
}
return transactionFees / params.Ether
}
func staticRewardByBlockNumber(blockNumber int64) float64 {
var staticBlockReward float64
//https://blog.ethereum.org/2017/10/12/byzantium-hf-announcement/
if blockNumber >= 4370000 {
staticBlockReward = 3
} else {
staticBlockReward = 5
}
return staticBlockReward
}

View File

@ -12,6 +12,7 @@ import (
type GethClient interface {
TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error)
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
}
func GethBlockToCoreBlock(gethBlock *types.Block, client GethClient) core.Block {
@ -21,18 +22,24 @@ func GethBlockToCoreBlock(gethBlock *types.Block, client GethClient) core.Block
transaction := gethTransToCoreTrans(gethTransaction, &from)
transactions = append(transactions, transaction)
}
blockReward := CalcBlockReward(gethBlock, client)
uncleReward := CalcUnclesReward(gethBlock)
return core.Block{
Difficulty: gethBlock.Difficulty().Int64(),
ExtraData: hexutil.Encode(gethBlock.Extra()),
GasLimit: gethBlock.GasLimit().Int64(),
GasUsed: gethBlock.GasUsed().Int64(),
Hash: gethBlock.Hash().Hex(),
Miner: gethBlock.Coinbase().Hex(),
Nonce: hexutil.Encode(gethBlock.Header().Nonce[:]),
Number: gethBlock.Number().Int64(),
ParentHash: gethBlock.ParentHash().Hex(),
Reward: blockReward,
Size: gethBlock.Size().Int64(),
Time: gethBlock.Time().Int64(),
Transactions: transactions,
UncleHash: gethBlock.UncleHash().Hex(),
UnclesReward: uncleReward,
}
}

View File

@ -13,7 +13,28 @@ import (
. "github.com/onsi/gomega"
)
type FakeGethClient struct{}
type FakeGethClient struct {
receipts map[string]*types.Receipt
}
func NewFakeClient() *FakeGethClient {
return &FakeGethClient{
receipts: make(map[string]*types.Receipt),
}
}
func (client *FakeGethClient) AddReceipts(receipts []*types.Receipt) {
for _, receipt := range receipts {
client.receipts[receipt.TxHash.Hex()] = receipt
}
}
func (client *FakeGethClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
if gasUsed, ok := client.receipts[txHash.Hex()]; ok {
return gasUsed, nil
}
return &types.Receipt{GasUsed: big.NewInt(0)}, nil
}
func (client *FakeGethClient) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) {
return common.HexToAddress("0x123"), nil
@ -21,10 +42,12 @@ func (client *FakeGethClient) TransactionSender(ctx context.Context, tx *types.T
var _ = Describe("Conversion of GethBlock to core.Block", func() {
It("converts basic Block metada", func() {
It("converts basic Block metadata", func() {
difficulty := big.NewInt(1)
gasLimit := int64(100000)
gasUsed := int64(100000)
miner := common.HexToAddress("0x0000000000000000000000000000000000000123")
extraData, _ := hexutil.Decode("0xe4b883e5bda9e7a59ee4bb99e9b1bc")
nonce := types.BlockNonce{10}
number := int64(1)
time := int64(140000000)
@ -33,6 +56,8 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
Difficulty: difficulty,
GasLimit: big.NewInt(gasLimit),
GasUsed: big.NewInt(gasUsed),
Extra: extraData,
Coinbase: miner,
Nonce: nonce,
Number: big.NewInt(number),
ParentHash: common.Hash{64},
@ -45,18 +70,123 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
Expect(gethBlock.Difficulty).To(Equal(difficulty.Int64()))
Expect(gethBlock.GasLimit).To(Equal(gasLimit))
Expect(gethBlock.Miner).To(Equal(miner.Hex()))
Expect(gethBlock.GasUsed).To(Equal(gasUsed))
Expect(gethBlock.Hash).To(Equal(block.Hash().Hex()))
Expect(gethBlock.Nonce).To(Equal(hexutil.Encode(header.Nonce[:])))
Expect(gethBlock.Number).To(Equal(number))
Expect(gethBlock.ParentHash).To(Equal(block.ParentHash().Hex()))
Expect(gethBlock.ExtraData).To(Equal(hexutil.Encode(block.Extra())))
Expect(gethBlock.Size).To(Equal(block.Size().Int64()))
Expect(gethBlock.Time).To(Equal(time))
Expect(gethBlock.UncleHash).To(Equal(block.UncleHash().Hex()))
Expect(gethBlock.IsFinal).To(BeFalse())
})
Describe("the converted transations", func() {
Describe("The block and uncle rewards calculations", func() {
It("calculates block rewards for a block", func() {
transaction := types.NewTransaction(
uint64(226823),
common.HexToAddress("0x108fedb097c1dcfed441480170144d8e19bb217f"),
big.NewInt(1080900090000000000),
big.NewInt(90000),
big.NewInt(50000000000),
[]byte{},
)
transactions := []*types.Transaction{transaction}
txHash := transaction.Hash()
receipt := types.Receipt{TxHash: txHash, GasUsed: big.NewInt(21000)}
receipts := []*types.Receipt{&receipt}
number := int64(1071819)
header := types.Header{
Number: big.NewInt(number),
}
uncles := []*types.Header{{Number: big.NewInt(1071817)}, {Number: big.NewInt(1071818)}}
block := types.NewBlock(&header, transactions, uncles, []*types.Receipt{})
client := NewFakeClient()
client.AddReceipts(receipts)
Expect(geth.CalcBlockReward(block, client)).To(Equal(5.31355))
})
It("calculates the uncles reward for a block", func() {
transaction := types.NewTransaction(
uint64(226823),
common.HexToAddress("0x108fedb097c1dcfed441480170144d8e19bb217f"),
big.NewInt(1080900090000000000),
big.NewInt(90000),
big.NewInt(50000000000),
[]byte{})
transactions := []*types.Transaction{transaction}
receipt := types.Receipt{
TxHash: transaction.Hash(),
GasUsed: big.NewInt(21000),
}
receipts := []*types.Receipt{&receipt}
header := types.Header{
Number: big.NewInt(int64(1071819)),
}
uncles := []*types.Header{
{Number: big.NewInt(1071816)},
{Number: big.NewInt(1071817)},
}
block := types.NewBlock(&header, transactions, uncles, []*types.Receipt{})
client := NewFakeClient()
client.AddReceipts(receipts)
Expect(geth.CalcUnclesReward(block)).To(Equal(6.875))
})
It("decreases the static block reward from 5 to 3 for blocks after block 4,269,999", func() {
transactionOne := types.NewTransaction(
uint64(8072),
common.HexToAddress("0xebd17720aeb7ac5186c5dfa7bafeb0bb14c02551 "),
big.NewInt(0),
big.NewInt(500000),
big.NewInt(42000000000),
[]byte{},
)
transactionTwo := types.NewTransaction(uint64(8071),
common.HexToAddress("0x3cdab63d764c8c5048ed5e8f0a4e95534ba7e1ea"),
big.NewInt(0),
big.NewInt(500000),
big.NewInt(42000000000),
[]byte{})
transactions := []*types.Transaction{transactionOne, transactionTwo}
receiptOne := types.Receipt{
TxHash: transactionOne.Hash(),
GasUsed: big.NewInt(297508),
}
receiptTwo := types.Receipt{
TxHash: transactionTwo.Hash(),
GasUsed: big.NewInt(297508),
}
receipts := []*types.Receipt{&receiptOne, &receiptTwo}
number := int64(4370055)
header := types.Header{
Number: big.NewInt(number),
}
var uncles []*types.Header
block := types.NewBlock(&header, transactions, uncles, receipts)
client := NewFakeClient()
client.AddReceipts(receipts)
Expect(geth.CalcBlockReward(block, client)).To(Equal(3.024990672))
})
})
Describe("the converted transactions", func() {
It("is empty", func() {
header := types.Header{}
block := types.NewBlock(&header, []*types.Transaction{}, []*types.Header{}, []*types.Receipt{})
@ -66,7 +196,7 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
Expect(len(coreBlock.Transactions)).To(Equal(0))
})
It("converts a single transations", func() {
It("converts a single transaction", func() {
nonce := uint64(10000)
header := types.Header{}
to := common.Address{1}
@ -76,7 +206,9 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
payload := []byte("1234")
gethTransaction := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, payload)
client := &FakeGethClient{}
client := NewFakeClient()
gethBlock := types.NewBlock(&header, []*types.Transaction{gethTransaction}, []*types.Header{}, []*types.Receipt{})
coreBlock := geth.GethBlockToCoreBlock(gethBlock, client)
@ -94,8 +226,8 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
It("has an empty to field when transaction creates a new contract", func() {
gethTransaction := types.NewContractCreation(uint64(10000), big.NewInt(10), big.NewInt(5000), big.NewInt(3), []byte("1234"))
gethBlock := types.NewBlock(&types.Header{}, []*types.Transaction{gethTransaction}, []*types.Header{}, []*types.Receipt{})
client := &FakeGethClient{}
client := NewFakeClient()
coreBlock := geth.GethBlockToCoreBlock(gethBlock, client)
coreTransaction := coreBlock.Transactions[0]

View File

@ -198,7 +198,11 @@ func (repository Postgres) FindBlockByNumber(blockNumber int64) (core.Block, err
block_parenthash,
block_size,
uncle_hash,
is_final
is_final,
block_miner,
block_extra_data,
block_reward,
block_uncles_reward
FROM blocks
WHERE node_id = $1 AND block_number = $2`, repository.nodeId, blockNumber)
savedBlock, err := repository.loadBlock(blockRows)
@ -256,10 +260,10 @@ func (repository Postgres) insertBlock(block core.Block) error {
tx, _ := repository.Db.BeginTx(context.Background(), nil)
err := tx.QueryRow(
`INSERT INTO blocks
(node_id, block_number, block_gaslimit, block_gasused, block_time, block_difficulty, block_hash, block_nonce, block_parenthash, block_size, uncle_hash, is_final)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
(node_id, block_number, block_gaslimit, block_gasused, block_time, block_difficulty, block_hash, block_nonce, block_parenthash, block_size, uncle_hash, is_final, block_miner, block_extra_data, block_reward, block_uncles_reward)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
RETURNING id `,
repository.nodeId, block.Number, block.GasLimit, block.GasUsed, block.Time, block.Difficulty, block.Hash, block.Nonce, block.ParentHash, block.Size, block.UncleHash, block.IsFinal).
repository.nodeId, block.Number, block.GasLimit, block.GasUsed, block.Time, block.Difficulty, block.Hash, block.Nonce, block.ParentHash, block.Size, block.UncleHash, block.IsFinal, block.Miner, block.ExtraData, block.Reward, block.UnclesReward).
Scan(&blockId)
if err != nil {
tx.Rollback()
@ -305,15 +309,19 @@ func (repository Postgres) loadBlock(blockRows *sql.Row) (core.Block, error) {
var blockHash string
var blockNonce string
var blockNumber int64
var blockMiner string
var blockExtraData string
var blockParentHash string
var blockSize int64
var blockTime float64
var blockReward float64
var difficulty int64
var gasLimit float64
var gasUsed float64
var uncleHash string
var unclesReward float64
var isFinal bool
err := blockRows.Scan(&blockId, &blockNumber, &gasLimit, &gasUsed, &blockTime, &difficulty, &blockHash, &blockNonce, &blockParentHash, &blockSize, &uncleHash, &isFinal)
err := blockRows.Scan(&blockId, &blockNumber, &gasLimit, &gasUsed, &blockTime, &difficulty, &blockHash, &blockNonce, &blockParentHash, &blockSize, &uncleHash, &isFinal, &blockMiner, &blockExtraData, &blockReward, &unclesReward)
if err != nil {
return core.Block{}, err
}
@ -330,10 +338,14 @@ func (repository Postgres) loadBlock(blockRows *sql.Row) (core.Block, error) {
ORDER BY tx_hash`, blockId)
transactions := repository.loadTransactions(transactionRows)
return core.Block{
Reward: blockReward,
Difficulty: difficulty,
ExtraData: blockExtraData,
GasLimit: int64(gasLimit),
GasUsed: int64(gasUsed),
Hash: blockHash,
IsFinal: isFinal,
Miner: blockMiner,
Nonce: blockNonce,
Number: blockNumber,
ParentHash: blockParentHash,
@ -341,7 +353,7 @@ func (repository Postgres) loadBlock(blockRows *sql.Row) (core.Block, error) {
Time: int64(blockTime),
Transactions: transactions,
UncleHash: uncleHash,
IsFinal: isFinal,
UnclesReward: unclesReward,
}, nil
}

View File

@ -61,37 +61,49 @@ func AssertRepositoryBehavior(buildRepository func(node core.Node) repositories.
blockHash := "x123"
blockParentHash := "x456"
blockNonce := "0x881db2ca900682e9a9"
miner := "x123"
extraData := "xextraData"
blockTime := int64(1508981640)
uncleHash := "x789"
blockSize := int64(1000)
difficulty := int64(10)
blockReward := float64(5.132)
unclesReward := float64(3.580)
block := core.Block{
Difficulty: difficulty,
GasLimit: gasLimit,
GasUsed: gasUsed,
Hash: blockHash,
Nonce: blockNonce,
Number: blockNumber,
ParentHash: blockParentHash,
Size: blockSize,
Time: blockTime,
UncleHash: uncleHash,
Reward: blockReward,
Difficulty: difficulty,
GasLimit: gasLimit,
GasUsed: gasUsed,
Hash: blockHash,
ExtraData: extraData,
Nonce: blockNonce,
Miner: miner,
Number: blockNumber,
ParentHash: blockParentHash,
Size: blockSize,
Time: blockTime,
UncleHash: uncleHash,
UnclesReward: unclesReward,
}
repository.CreateOrUpdateBlock(block)
savedBlock, err := repository.FindBlockByNumber(blockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(savedBlock.Reward).To(Equal(blockReward))
Expect(savedBlock.Difficulty).To(Equal(difficulty))
Expect(savedBlock.GasLimit).To(Equal(gasLimit))
Expect(savedBlock.GasUsed).To(Equal(gasUsed))
Expect(savedBlock.Hash).To(Equal(blockHash))
Expect(savedBlock.Nonce).To(Equal(blockNonce))
Expect(savedBlock.Miner).To(Equal(miner))
Expect(savedBlock.ExtraData).To(Equal(extraData))
Expect(savedBlock.Number).To(Equal(blockNumber))
Expect(savedBlock.ParentHash).To(Equal(blockParentHash))
Expect(savedBlock.Size).To(Equal(blockSize))
Expect(savedBlock.Time).To(Equal(blockTime))
Expect(savedBlock.UncleHash).To(Equal(uncleHash))
Expect(savedBlock.UnclesReward).To(Equal(unclesReward))
})
It("does not find a block when searching for a number that does not exist", func() {