From 9030cff2bd809dd8c3b91eef0f9a4110c247c3ed Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Thu, 21 Mar 2019 14:52:24 -0500 Subject: [PATCH] keep track of who is receiving uncle rewards --- .../00045_create_uncle_rewards_table.sql | 15 +++++ pkg/core/block.go | 35 ++++++----- .../postgres/repositories/block_repository.go | 30 ++++++++++ .../repositories/block_repository_test.go | 4 +- .../postgres/repositories/uncle_rewards.go | 1 + .../repositories/uncle_rewards_test.go | 1 + pkg/geth/converters/common/block_converter.go | 6 +- pkg/geth/converters/common/block_rewards.go | 60 ++++++++++++------- 8 files changed, 109 insertions(+), 43 deletions(-) create mode 100644 db/migrations/00045_create_uncle_rewards_table.sql create mode 100644 pkg/datastore/postgres/repositories/uncle_rewards.go create mode 100644 pkg/datastore/postgres/repositories/uncle_rewards_test.go diff --git a/db/migrations/00045_create_uncle_rewards_table.sql b/db/migrations/00045_create_uncle_rewards_table.sql new file mode 100644 index 00000000..4c40561f --- /dev/null +++ b/db/migrations/00045_create_uncle_rewards_table.sql @@ -0,0 +1,15 @@ +-- +goose Up +CREATE TABLE public.uncle_rewards ( + id SERIAL PRIMARY KEY, + block_id INTEGER, + block_hash VARCHAR(66) NOT NULL, + uncle_hash VARCHAR(66) NOT NULL, + uncle_reward NUMERIC NOT NULL, + miner_address VARCHAR(66) NOT NULL, + CONSTRAINT block_id_fk FOREIGN KEY (block_id) + REFERENCES blocks (id) + ON DELETE CASCADE +); + +-- +goose Down +DROP TABLE public.uncle_rewards; diff --git a/pkg/core/block.go b/pkg/core/block.go index 279b8baf..e7546f19 100644 --- a/pkg/core/block.go +++ b/pkg/core/block.go @@ -16,21 +16,24 @@ package core +import "math/big" + type Block struct { - Reward float64 `db:"reward"` - Difficulty int64 `db:"difficulty"` - ExtraData string `db:"extra_data"` - GasLimit uint64 `db:"gaslimit"` - GasUsed uint64 `db:"gasused"` - Hash string `db:"hash"` - IsFinal bool `db:"is_final"` - Miner string `db:"miner"` - Nonce string `db:"nonce"` - Number int64 `db:"number"` - ParentHash string `db:"parenthash"` - Size string `db:"size"` - Time int64 `db:"time"` - Transactions []TransactionModel - UncleHash string `db:"uncle_hash"` - UnclesReward float64 `db:"uncles_reward"` + Reward string `db:"reward"` + Difficulty int64 `db:"difficulty"` + ExtraData string `db:"extra_data"` + GasLimit uint64 `db:"gaslimit"` + GasUsed uint64 `db:"gasused"` + Hash string `db:"hash"` + IsFinal bool `db:"is_final"` + Miner string `db:"miner"` + Nonce string `db:"nonce"` + Number int64 `db:"number"` + ParentHash string `db:"parenthash"` + Size string `db:"size"` + Time int64 `db:"time"` + Transactions []TransactionModel + UncleHash string `db:"uncle_hash"` + UnclesReward string `db:"uncles_reward"` + MappedUncleRewards map[string]map[string]*big.Int } diff --git a/pkg/datastore/postgres/repositories/block_repository.go b/pkg/datastore/postgres/repositories/block_repository.go index cdbed4a9..9fe2c0df 100644 --- a/pkg/datastore/postgres/repositories/block_repository.go +++ b/pkg/datastore/postgres/repositories/block_repository.go @@ -20,6 +20,7 @@ import ( "database/sql" "errors" log "github.com/sirupsen/logrus" + "math/big" "github.com/jmoiron/sqlx" @@ -139,6 +140,13 @@ func (blockRepository BlockRepository) insertBlock(block core.Block) (int64, err } return 0, postgres.ErrDBInsertFailed(insertBlockErr) } + if len(block.MappedUncleRewards) > 0 { + insertUncleErr := blockRepository.createUncleRewards(tx, blockId, block.Hash, block.MappedUncleRewards) + if insertUncleErr != nil { + tx.Rollback() + return 0, postgres.ErrDBInsertFailed(insertUncleErr) + } + } if len(block.Transactions) > 0 { insertTxErr := blockRepository.createTransactions(tx, blockId, block.Transactions) if insertTxErr != nil { @@ -160,6 +168,28 @@ func (blockRepository BlockRepository) insertBlock(block core.Block) (int64, err return blockId, nil } +func (blockRepository BlockRepository) createUncleRewards(tx *sqlx.Tx, blockId int64, blockHash string, mappedUncleRewards map[string]map[string]*big.Int) error { + for miner, uncleRewards := range mappedUncleRewards { + for uncleHash, reward := range uncleRewards { + err := blockRepository.createUncleReward(tx, blockId, blockHash, miner, uncleHash, reward.String()) + if err != nil { + return err + } + } + } + return nil +} + +func (blockRepository BlockRepository) createUncleReward(tx *sqlx.Tx, blockId int64, blockHash, miner, uncleHash, amount string) error { + _, err := tx.Exec( + `INSERT INTO uncle_rewards + (block_id, block_hash, uncle_hash, uncle_reward, miner_address) + VALUES ($1, $2, $3, $4, $5) + RETURNING id`, + blockId, blockHash, miner, uncleHash, amount) + return err +} + func (blockRepository BlockRepository) createTransactions(tx *sqlx.Tx, blockId int64, transactions []core.TransactionModel) error { for _, transaction := range transactions { err := blockRepository.createTransaction(tx, blockId, transaction) diff --git a/pkg/datastore/postgres/repositories/block_repository_test.go b/pkg/datastore/postgres/repositories/block_repository_test.go index 64b40fcb..fb126d00 100644 --- a/pkg/datastore/postgres/repositories/block_repository_test.go +++ b/pkg/datastore/postgres/repositories/block_repository_test.go @@ -84,8 +84,8 @@ var _ = Describe("Saving blocks", func() { uncleHash := "x789" blockSize := string("1000") difficulty := int64(10) - blockReward := float64(5.132) - unclesReward := float64(3.580) + blockReward := "5132000000000000000" + unclesReward := "3580000000000000000" block := core.Block{ Reward: blockReward, Difficulty: difficulty, diff --git a/pkg/datastore/postgres/repositories/uncle_rewards.go b/pkg/datastore/postgres/repositories/uncle_rewards.go new file mode 100644 index 00000000..3f43206c --- /dev/null +++ b/pkg/datastore/postgres/repositories/uncle_rewards.go @@ -0,0 +1 @@ +package repositories diff --git a/pkg/datastore/postgres/repositories/uncle_rewards_test.go b/pkg/datastore/postgres/repositories/uncle_rewards_test.go new file mode 100644 index 00000000..3f43206c --- /dev/null +++ b/pkg/datastore/postgres/repositories/uncle_rewards_test.go @@ -0,0 +1 @@ +package repositories diff --git a/pkg/geth/converters/common/block_converter.go b/pkg/geth/converters/common/block_converter.go index c12d095b..5b36c271 100644 --- a/pkg/geth/converters/common/block_converter.go +++ b/pkg/geth/converters/common/block_converter.go @@ -52,7 +52,9 @@ func (bc BlockConverter) ToCoreBlock(gethBlock *types.Block) (core.Block, error) Transactions: transactions, UncleHash: gethBlock.UncleHash().Hex(), } - coreBlock.Reward = CalcBlockReward(coreBlock, gethBlock.Uncles()) - coreBlock.UnclesReward = CalcUnclesReward(coreBlock, gethBlock.Uncles()) + coreBlock.Reward = CalcBlockReward(coreBlock, gethBlock.Uncles()).String() + uncleRewards, mappedUncleRewards := CalcUnclesReward(coreBlock, gethBlock.Uncles()) + coreBlock.UnclesReward = uncleRewards.String() + coreBlock.MappedUncleRewards = mappedUncleRewards return coreBlock, nil } diff --git a/pkg/geth/converters/common/block_rewards.go b/pkg/geth/converters/common/block_rewards.go index fcf1a2c7..57f8df28 100644 --- a/pkg/geth/converters/common/block_rewards.go +++ b/pkg/geth/converters/common/block_rewards.go @@ -17,54 +17,68 @@ package common import ( + "math/big" + "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" "github.com/vulcanize/vulcanizedb/pkg/core" ) -func CalcUnclesReward(block core.Block, uncles []*types.Header) float64 { - var unclesReward float64 +// (U_n + 8 - B_n) * R / 8 +// Returns a map of miner addresses to a map of the uncles they mined (hashes) to the rewards received for that uncle +func CalcUnclesReward(block core.Block, uncles []*types.Header) (*big.Int, map[string]map[string]*big.Int) { + uncleRewards := new(big.Int) + mappedUncleRewards := make(map[string]map[string]*big.Int) for _, uncle := range uncles { - blockNumber := block.Number - staticBlockReward := float64(staticRewardByBlockNumber(blockNumber)) - unclesReward += (1.0 + float64(uncle.Number.Int64()-block.Number)/8.0) * staticBlockReward + staticBlockReward := staticRewardByBlockNumber(block.Number) + rewardDiv8 := staticBlockReward.Div(staticBlockReward, big.NewInt(8)) + uncleBlock := big.NewInt(uncle.Number.Int64()) + uncleBlockPlus8 := uncleBlock.Add(uncleBlock, big.NewInt(8)) + mainBlock := big.NewInt(block.Number) + uncleBlockPlus8MinusMainBlock := uncleBlockPlus8.Sub(uncleBlockPlus8, mainBlock) + thisUncleReward := rewardDiv8.Mul(rewardDiv8, uncleBlockPlus8MinusMainBlock) + uncleRewards = uncleRewards.Add(uncleRewards, thisUncleReward) + mappedUncleRewards[uncle.Coinbase.Hex()][uncle.Hash().Hex()].Add(mappedUncleRewards[uncle.Coinbase.Hex()][uncle.Hash().Hex()], thisUncleReward) } - return unclesReward + return uncleRewards, mappedUncleRewards } -func CalcBlockReward(block core.Block, uncles []*types.Header) float64 { - blockNumber := block.Number - staticBlockReward := staticRewardByBlockNumber(blockNumber) +func CalcBlockReward(block core.Block, uncles []*types.Header) *big.Int { + staticBlockReward := staticRewardByBlockNumber(block.Number) transactionFees := calcTransactionFees(block) uncleInclusionRewards := calcUncleInclusionRewards(block, uncles) - return transactionFees + uncleInclusionRewards + staticBlockReward + tmp := transactionFees.Add(transactionFees, uncleInclusionRewards) + return tmp.Add(tmp, staticBlockReward) } -func calcTransactionFees(block core.Block) float64 { - var transactionFees float64 +func calcTransactionFees(block core.Block) *big.Int { + transactionFees := new(big.Int) for _, transaction := range block.Transactions { receipt := transaction.Receipt - transactionFees += float64(uint64(transaction.GasPrice) * receipt.GasUsed) + gasPrice := big.NewInt(transaction.GasPrice) + gasUsed := big.NewInt(int64(receipt.GasUsed)) + transactionFee := gasPrice.Mul(gasPrice, gasUsed) + transactionFees = transactionFees.Add(transactionFees, transactionFee) } - return transactionFees / params.Ether + return transactionFees } -func calcUncleInclusionRewards(block core.Block, uncles []*types.Header) float64 { - var uncleInclusionRewards float64 - staticBlockReward := staticRewardByBlockNumber(block.Number) +func calcUncleInclusionRewards(block core.Block, uncles []*types.Header) *big.Int { + uncleInclusionRewards := new(big.Int) for range uncles { - uncleInclusionRewards += staticBlockReward * 1 / 32 + staticBlockReward := staticRewardByBlockNumber(block.Number) + staticBlockReward.Div(staticBlockReward, big.NewInt(32)) + uncleInclusionRewards.Add(uncleInclusionRewards, staticBlockReward) } return uncleInclusionRewards } -func staticRewardByBlockNumber(blockNumber int64) float64 { - var staticBlockReward float64 +func staticRewardByBlockNumber(blockNumber int64) *big.Int { + staticBlockReward := new(big.Int) //https://blog.ethereum.org/2017/10/12/byzantium-hf-announcement/ if blockNumber >= 4370000 { - staticBlockReward = 3 + staticBlockReward.SetString("3000000000000000000", 10) } else { - staticBlockReward = 5 + staticBlockReward.SetString("5000000000000000000", 10) } return staticBlockReward }