diff --git a/db/migrations/1514476024_add_block_reward_uncle_reward.down.sql b/db/migrations/1514476024_add_block_reward_uncle_reward.down.sql new file mode 100644 index 00000000..ec96263f --- /dev/null +++ b/db/migrations/1514476024_add_block_reward_uncle_reward.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE blocks + DROP COLUMN block_reward, + DROP COLUMN block_uncles_reward; diff --git a/db/migrations/1514476024_add_block_reward_uncle_reward.up.sql b/db/migrations/1514476024_add_block_reward_uncle_reward.up.sql new file mode 100644 index 00000000..447e53a6 --- /dev/null +++ b/db/migrations/1514476024_add_block_reward_uncle_reward.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE blocks + ADD COLUMN block_reward NUMERIC, + ADD COLUMN block_uncles_reward NUMERIC; diff --git a/db/schema.sql b/db/schema.sql index e4bbce3f..b3eae30a 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -53,7 +53,9 @@ CREATE TABLE blocks ( node_id integer NOT NULL, is_final boolean, block_miner character varying(42), - block_extra_data character varying + block_extra_data character varying, + block_reward numeric, + block_uncles_reward numeric ); diff --git a/integration_test/block_rewards_test.go b/integration_test/block_rewards_test.go index d183ff92..b5ee5351 100644 --- a/integration_test/block_rewards_test.go +++ b/integration_test/block_rewards_test.go @@ -9,28 +9,26 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Reading contracts", func() { +var _ = Describe("Rewards calculations", func() { - Describe("Block and Uncle rewards", 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.BlockReward).To(Equal(5.31355)) - }) + 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.UncleReward).To(Equal(6.875)) - }) + 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)) }) }) diff --git a/pkg/core/block.go b/pkg/core/block.go index 5d78fe1d..d14f43b7 100644 --- a/pkg/core/block.go +++ b/pkg/core/block.go @@ -1,7 +1,7 @@ package core type Block struct { - BlockReward float64 + Reward float64 Difficulty int64 ExtraData string GasLimit int64 @@ -16,5 +16,5 @@ type Block struct { Time int64 Transactions []Transaction UncleHash string - UncleReward float64 + UnclesReward float64 } diff --git a/pkg/geth/geth_block_rewards.go b/pkg/geth/geth_block_rewards.go index 8e4ad79e..f491041d 100644 --- a/pkg/geth/geth_block_rewards.go +++ b/pkg/geth/geth_block_rewards.go @@ -7,24 +7,27 @@ import ( "github.com/ethereum/go-ethereum/params" ) -func UncleReward(gethBlock *types.Block, client GethClient) float64 { - var uncleReward float64 +func CalcUnclesReward(gethBlock *types.Block) float64 { + var unclesReward float64 for _, uncle := range gethBlock.Uncles() { - staticBlockReward := float64(blockNumberStaticReward(gethBlock)) / float64(8) - uncleReward += float64(uncle.Number.Int64()-gethBlock.Number().Int64()+int64(8)) * staticBlockReward + blockNumber := gethBlock.Number().Int64() + staticBlockReward := float64(staticRewardByBlockNumber(blockNumber)) + unclesReward += (1.0 + float64(uncle.Number.Int64()-gethBlock.Number().Int64())/8.0) * staticBlockReward } - return uncleReward + return unclesReward } -func BlockReward(gethBlock *types.Block, client GethClient) float64 { - staticBlockReward := blockNumberStaticReward(gethBlock) +func CalcBlockReward(gethBlock *types.Block, client GethClient) float64 { + blockNumber := gethBlock.Number().Int64() + staticBlockReward := staticRewardByBlockNumber(blockNumber) transactionFees := calcTransactionFees(gethBlock, client) - uncleInclusionRewards := uncleInclusionRewards(gethBlock, staticBlockReward) + uncleInclusionRewards := calcUncleInclusionRewards(gethBlock) return transactionFees + uncleInclusionRewards + staticBlockReward } -func uncleInclusionRewards(gethBlock *types.Block, staticBlockReward float64) float64 { +func calcUncleInclusionRewards(gethBlock *types.Block) float64 { var uncleInclusionRewards float64 + staticBlockReward := staticRewardByBlockNumber(gethBlock.Number().Int64()) for range gethBlock.Uncles() { uncleInclusionRewards += staticBlockReward * 1 / 32 } @@ -43,9 +46,10 @@ func calcTransactionFees(gethBlock *types.Block, client GethClient) float64 { return transactionFees / params.Ether } -func blockNumberStaticReward(gethBlock *types.Block) float64 { +func staticRewardByBlockNumber(blockNumber int64) float64 { var staticBlockReward float64 - if gethBlock.Number().Int64() > 4269999 { + //https://blog.ethereum.org/2017/10/12/byzantium-hf-announcement/ + if blockNumber >= 4370000 { staticBlockReward = 3 } else { staticBlockReward = 5 diff --git a/pkg/geth/geth_block_to_core_block.go b/pkg/geth/geth_block_to_core_block.go index af307b7a..ece19b56 100644 --- a/pkg/geth/geth_block_to_core_block.go +++ b/pkg/geth/geth_block_to_core_block.go @@ -22,24 +22,24 @@ func GethBlockToCoreBlock(gethBlock *types.Block, client GethClient) core.Block transaction := gethTransToCoreTrans(gethTransaction, &from) transactions = append(transactions, transaction) } - blockReward := BlockReward(gethBlock, client) - uncleReward := UncleReward(gethBlock, client) + 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(), - ExtraData: hexutil.Encode(gethBlock.Extra()), + Miner: gethBlock.Coinbase().Hex(), Nonce: hexutil.Encode(gethBlock.Header().Nonce[:]), Number: gethBlock.Number().Int64(), - Miner: gethBlock.Coinbase().Hex(), ParentHash: gethBlock.ParentHash().Hex(), + Reward: blockReward, Size: gethBlock.Size().Int64(), Time: gethBlock.Time().Int64(), Transactions: transactions, - BlockReward: blockReward, UncleHash: gethBlock.UncleHash().Hex(), - UncleReward: uncleReward, + UnclesReward: uncleReward, } } diff --git a/pkg/geth/geth_block_to_core_block_test.go b/pkg/geth/geth_block_to_core_block_test.go index ccf7a85e..5d551a6a 100644 --- a/pkg/geth/geth_block_to_core_block_test.go +++ b/pkg/geth/geth_block_to_core_block_test.go @@ -83,104 +83,110 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() { Expect(gethBlock.IsFinal).To(BeFalse()) }) - Describe("the block and uncle rewards calculations", func() { - It("Calculates block rewards for a block", func() { - number := int64(1071819) - uncles := []*types.Header{{Number: big.NewInt(1071817)}, {Number: big.NewInt(1071818)}} - - nonce := uint64(226823) - to := common.HexToAddress("0x108fedb097c1dcfed441480170144d8e19bb217f") - amount := big.NewInt(1080900090000000000) - gasLimit := big.NewInt(90000) - gasPrice := big.NewInt(50000000000) - var payload []byte - transaction := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, payload) + 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.BlockReward(block, client)).To(Equal(5.31355)) + 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() { - number := int64(4370055) - var uncles []*types.Header + transactionOne := types.NewTransaction( + uint64(8072), + common.HexToAddress("0xebd17720aeb7ac5186c5dfa7bafeb0bb14c02551 "), + big.NewInt(0), + big.NewInt(500000), + big.NewInt(42000000000), + []byte{}, + ) - nonce := uint64(8072) - to := common.HexToAddress("0xebd17720aeb7ac5186c5dfa7bafeb0bb14c02551 ") - amount := big.NewInt(0) - gasLimit := big.NewInt(500000) - gasPrice := big.NewInt(42000000000) - var payload []byte - transactionOne := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, payload) - - nonce = uint64(8071) - to = common.HexToAddress("0x3cdab63d764c8c5048ed5e8f0a4e95534ba7e1ea") - amount = big.NewInt(0) - gasLimit = big.NewInt(500000) - gasPrice = big.NewInt(42000000000) - transactionTwo := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, payload) + transactionTwo := types.NewTransaction(uint64(8071), + common.HexToAddress("0x3cdab63d764c8c5048ed5e8f0a4e95534ba7e1ea"), + big.NewInt(0), + big.NewInt(500000), + big.NewInt(42000000000), + []byte{}) transactions := []*types.Transaction{transactionOne, transactionTwo} - txHashOne := transactionOne.Hash() - receiptOne := types.Receipt{TxHash: txHashOne, GasUsed: big.NewInt(297508)} - txHashTwo := transactionTwo.Hash() - receiptTwo := types.Receipt{TxHash: txHashTwo, GasUsed: big.NewInt(297508)} + 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.BlockReward(block, client)).To(Equal(3.024990672)) + Expect(geth.CalcBlockReward(block, client)).To(Equal(3.024990672)) }) - - It("Calculates uncle rewards for a block", func() { - number := int64(1071819) - uncles := []*types.Header{{Number: big.NewInt(1071816)}, {Number: big.NewInt(1071817)}} - - nonce := uint64(226823) - to := common.HexToAddress("0x108fedb097c1dcfed441480170144d8e19bb217f") - amount := big.NewInt(1080900090000000000) - gasLimit := big.NewInt(90000) - gasPrice := big.NewInt(50000000000) - var payload []byte - transaction := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, payload) - transactions := []*types.Transaction{transaction} - - txHash := transaction.Hash() - receipt := types.Receipt{TxHash: txHash, GasUsed: big.NewInt(21000)} - receipts := []*types.Receipt{&receipt} - - header := types.Header{ - Number: big.NewInt(number), - } - block := types.NewBlock(&header, transactions, uncles, []*types.Receipt{}) - - client := NewFakeClient() - client.AddReceipts(receipts) - - Expect(geth.UncleReward(block, client)).To(Equal(6.875)) - }) - }) - Describe("the converted transations", func() { + Describe("the converted transactions", func() { It("is empty", func() { header := types.Header{} block := types.NewBlock(&header, []*types.Transaction{}, []*types.Header{}, []*types.Receipt{}) diff --git a/pkg/repositories/postgres.go b/pkg/repositories/postgres.go index ed7446ae..c4b00e5b 100644 --- a/pkg/repositories/postgres.go +++ b/pkg/repositories/postgres.go @@ -200,7 +200,9 @@ func (repository Postgres) FindBlockByNumber(blockNumber int64) (core.Block, err uncle_hash, is_final, block_miner, - block_extra_data + 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) @@ -258,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, block_miner, block_extra_data) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) + (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, block.Miner, block.ExtraData). + 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() @@ -312,12 +314,14 @@ func (repository Postgres) loadBlock(blockRows *sql.Row) (core.Block, error) { 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, &blockMiner, &blockExtraData) + 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 } @@ -334,6 +338,7 @@ 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), @@ -348,6 +353,7 @@ func (repository Postgres) loadBlock(blockRows *sql.Row) (core.Block, error) { Time: int64(blockTime), Transactions: transactions, UncleHash: uncleHash, + UnclesReward: unclesReward, }, nil } diff --git a/pkg/repositories/testing/helpers.go b/pkg/repositories/testing/helpers.go index 7accb32b..ca7420db 100644 --- a/pkg/repositories/testing/helpers.go +++ b/pkg/repositories/testing/helpers.go @@ -67,25 +67,30 @@ func AssertRepositoryBehavior(buildRepository func(node core.Node) repositories. 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, - ExtraData: extraData, - Nonce: blockNonce, - Miner: miner, - 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)) @@ -98,6 +103,7 @@ func AssertRepositoryBehavior(buildRepository func(node core.Node) repositories. 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() {