diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index ad36f21ca948..71dd827ccf51 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -488,7 +488,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H // Finalize implements consensus.Engine, accumulating the block and uncle rewards. func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { // Accumulate any block and uncle rewards - accumulateRewards(chain.Config(), state, header, uncles) + applyRewards(chain.Config(), state, header, uncles) } // FinalizeAndAssemble implements consensus.Engine, accumulating the block and @@ -543,10 +543,19 @@ var ( big32 = big.NewInt(32) ) -// AccumulateRewards credits the coinbase of the given block with the mining -// reward. The total reward consists of the static block reward and rewards for -// included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { +// applyRewards credits the coinbase of the given block with the mining reward. +func applyRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { + f := func(h *types.Header, amt *big.Int) { + state.AddBalance(h.Coinbase, amt) + } + AccumulateRewards(config, header, uncles, f, f) +} + +// AccumulateRewards is a generic function that allows the caller to decide how +// to apply rewards. The total reward consists of the static block reward and +// rewards for included uncles. The coinbase of each uncle block is also +// rewarded. +func AccumulateRewards(config *params.ChainConfig, header *types.Header, uncles []*types.Header, accUncleReward, accTotalReward func(*types.Header, *big.Int)) { // Select the correct block reward based on chain progression blockReward := FrontierBlockReward if config.IsByzantium(header.Number) { @@ -563,10 +572,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, big8) - state.AddBalance(uncle.Coinbase, r) + accUncleReward(uncle, r) r.Div(blockReward, big32) reward.Add(reward, r) } - state.AddBalance(header.Coinbase, reward) + accTotalReward(header, reward) } diff --git a/core/blockchain.go b/core/blockchain.go index 0ad380c61ef9..cb566fe8b7cb 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1401,12 +1401,26 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. if parent == nil { log.Error("Failed to retrieve parent for supply delta", "err", err) } else { - supplyDelta, err := supply.Delta(block, parent, bc.stateCache.TrieDB(), bc.chainConfig) + start := time.Now() + + supplyDelta, err := supply.Delta(parent, block.Header(), bc.stateCache.TrieDB()) if err != nil { log.Error("Failed to record Ether supply delta", "err", err) } else { rawdb.WriteSupplyDelta(bc.db, block.NumberU64(), block.Hash(), supplyDelta) } + + // Calculate the block coinbaseReward based on chain rules and progression. + coinbaseReward, unclesReward, burn, withdrawals := supply.Subsidy(block, bc.chainConfig) + + // Calculate the difference between the "calculated" and "crawled" supply delta. + diff := new(big.Int).Set(supplyDelta) + diff.Sub(diff, coinbaseReward) + diff.Sub(diff, unclesReward) + diff.Sub(diff, withdrawals) + diff.Add(diff, burn) + + log.Info("Calculated supply delta for block", "number", block.Number(), "hash", block.Hash(), "supplydelta", supplyDelta, "coinbasereward", coinbaseReward, "unclesreward", unclesReward, "burn", burn, "withdrawals", withdrawals, "diff", diff, "elapsed", time.Since(start)) } } return nil diff --git a/core/blockchain_test.go b/core/blockchain_test.go index e626dbb5f727..b8e3ff73b792 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/supply" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -4340,4 +4341,82 @@ func TestEIP3651(t *testing.T) { if actual.Cmp(expected) != 0 { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } + +} + +func TestDelta(t *testing.T) { + var ( + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + engine = beacon.NewFaker() + + // A sender who makes transactions, has some funds + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + funds = big.NewInt(params.Ether) + gspec = &Genesis{ + Config: params.AllEthashProtocolChanges, + Alloc: GenesisAlloc{ + addr1: {Balance: funds}, + // The address 0xAAAA self-destructs + aa: { + Code: []byte{ + byte(vm.ADDRESS), + byte(vm.SELFDESTRUCT), + }, + Nonce: 0, + Balance: big.NewInt(41), + }, + }, + } + ) + + gspec.Config.TerminalTotalDifficulty = common.Big0 + gspec.Config.TerminalTotalDifficultyPassed = true + gspec.Config.ShanghaiTime = u64(0) + signer := types.LatestSigner(gspec.Config) + + db, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { + b.SetCoinbase(common.Address{1}) + + // One transaction to 0xAAAA + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: 0, + To: &aa, + Value: common.Big1, + Gas: 50000, + GasFeeCap: newGwei(5), + GasTipCap: big.NewInt(2), + } + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + b.AddWithdrawal(&types.Withdrawal{Amount: 1337}) + }) + + var ( + parent = gspec.ToBlock().Header() + block = blocks[0] + ) + + got, err := supply.Delta(parent, block.Header(), trie.NewDatabase(db)) + if err != nil { + t.Fatalf("failed to calculate delta: %v", err) + } + + // Calculate delta, w/o self-destructs + coinbaseReward, _, burn, withdrawals := supply.Subsidy(block, gspec.Config) + + want := new(big.Int) + want.Add(want, coinbaseReward) + want.Add(want, withdrawals) + want.Sub(want, burn) + + // Now account for self-destructed amount. + want.Sub(want, big.NewInt(42)) + + if want.Cmp(got) != 0 { + t.Fatalf("incorrect delta calculated: want %d, got %d", want, got) + } } diff --git a/core/supply/delta.go b/core/supply/delta.go index 6a35a05afd97..bfd3a0653093 100644 --- a/core/supply/delta.go +++ b/core/supply/delta.go @@ -19,37 +19,31 @@ package supply import ( "fmt" "math/big" - "time" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) -// Delta calculates the Ether delta across two state tries. That is, the +// Delta calculates the ether delta across two state tries. That is, the // issuance minus the ether destroyed. -func Delta(block *types.Block, parent *types.Header, db *trie.Database, config *params.ChainConfig) (*big.Int, error) { - var ( - supplyDelta = new(big.Int) - start = time.Now() - ) - // Open the two tries. - if block.ParentHash() != parent.Hash() { - return nil, fmt.Errorf("parent hash mismatch: have %s, want %s", block.ParentHash().Hex(), parent.Hash().Hex()) - } - src, err := trie.New(trie.StateTrieID(parent.Root), db) +func Delta(src, dst *types.Header, db *trie.Database) (*big.Int, error) { + // Open src and dst tries. + srcTrie, err := trie.New(trie.StateTrieID(src.Root), db) if err != nil { return nil, fmt.Errorf("failed to open source trie: %v", err) } - dst, err := trie.New(trie.StateTrieID(block.Root()), db) + dstTrie, err := trie.New(trie.StateTrieID(dst.Root), db) if err != nil { return nil, fmt.Errorf("failed to open destination trie: %v", err) } + + delta := new(big.Int) + // Gather all the changes across from source to destination. - fwdDiffIt, _ := trie.NewDifferenceIterator(src.MustNodeIterator(nil), dst.MustNodeIterator(nil)) + fwdDiffIt, _ := trie.NewDifferenceIterator(srcTrie.MustNodeIterator(nil), dstTrie.MustNodeIterator(nil)) fwdIt := trie.NewIterator(fwdDiffIt) for fwdIt.Next() { @@ -57,82 +51,55 @@ func Delta(block *types.Block, parent *types.Header, db *trie.Database, config * if err := rlp.DecodeBytes(fwdIt.Value, acc); err != nil { panic(err) } - supplyDelta.Add(supplyDelta, acc.Balance) + delta.Add(delta, acc.Balance) } // Gather all the changes across from destination to source. - rewDiffIt, _ := trie.NewDifferenceIterator(dst.MustNodeIterator(nil), src.MustNodeIterator(nil)) - rewIt := trie.NewIterator(rewDiffIt) + revDiffIt, _ := trie.NewDifferenceIterator(dstTrie.MustNodeIterator(nil), srcTrie.MustNodeIterator(nil)) + revIt := trie.NewIterator(revDiffIt) - for rewIt.Next() { + for revIt.Next() { acc := new(types.StateAccount) - if err := rlp.DecodeBytes(rewIt.Value, acc); err != nil { + if err := rlp.DecodeBytes(revIt.Value, acc); err != nil { panic(err) } - supplyDelta.Sub(supplyDelta, acc.Balance) + delta.Sub(delta, acc.Balance) } - // Calculate the block fixedReward based on chain rules and progression. - fixedReward, unclesReward, burn, withdrawals := Subsidy(block, config) - - // Calculate the difference between the "calculated" and "crawled" supply - // delta. - diff := new(big.Int).Set(supplyDelta) - diff.Sub(diff, fixedReward) - diff.Sub(diff, unclesReward) - diff.Add(diff, burn) - log.Info("Calculated supply delta for block", "number", block.Number(), "hash", block.Hash(), "supplydelta", supplyDelta, "fixedreward", fixedReward, "unclesreward", unclesReward, "burn", burn, "withdrawals", withdrawals, "diff", diff, "elapsed", time.Since(start)) - return supplyDelta, nil + return delta, nil } -// Subsidy calculates the block mining (fixed) and uncle subsidy as well as the -// 1559 burn solely based on header fields. This method is a very accurate -// approximation of the true supply delta, but cannot take into account Ether -// burns via selfdestructs, so it will always be ever so slightly off. -func Subsidy(block *types.Block, config *params.ChainConfig) (fixedReward *big.Int, unclesReward *big.Int, burn *big.Int, withdrawals *big.Int) { - // Calculate the block rewards based on chain rules and progression. - fixedReward = new(big.Int) - unclesReward = new(big.Int) - withdrawals = new(big.Int) - - // Select the correct block reward based on chain progression. - if config.Ethash != nil { - if block.Difficulty().BitLen() != 0 { - fixedReward = ethash.FrontierBlockReward - if config.IsByzantium(block.Number()) { - fixedReward = ethash.ByzantiumBlockReward - } - if config.IsConstantinople(block.Number()) { - fixedReward = ethash.ConstantinopleBlockReward - } +// Subsidy calculates the coinbase subsidy and uncle subsidy as well as the +// EIP-1559 burn. This method is a very accurate approximation of the true +// supply delta, but cannot take into account ether burns via selfdestructs, so +// it will always be slightly off. +func Subsidy(block *types.Block, config *params.ChainConfig) (*big.Int, *big.Int, *big.Int, *big.Int) { + var ( + coinbaseReward = new(big.Int) + unclesReward = new(big.Int) + withdrawals = new(big.Int) + ) + // If block is ethash, calculate the coinbase and uncle rewards. + if config.Ethash != nil && block.Difficulty().BitLen() != 0 { + accCoinbase := func(h *types.Header, amt *big.Int) { + coinbaseReward.Add(coinbaseReward, amt) } - // Accumulate the rewards for included uncles. - var ( - big8 = big.NewInt(8) - big32 = big.NewInt(32) - r = new(big.Int) - ) - for _, uncle := range block.Uncles() { - // Add the reward for the side blocks. - r.Add(uncle.Number, big8) - r.Sub(r, block.Number()) - r.Mul(r, fixedReward) - r.Div(r, big8) - unclesReward.Add(unclesReward, r) - - // Add the reward for accumulating the side blocks. - r.Div(fixedReward, big32) - unclesReward.Add(unclesReward, r) + accUncles := func(h *types.Header, amt *big.Int) { + unclesReward.Add(unclesReward, amt) } + ethash.AccumulateRewards(config, block.Header(), block.Uncles(), accCoinbase, accUncles) } // Calculate the burn based on chain rules and progression. - burn = new(big.Int) + burn := new(big.Int) if block.BaseFee() != nil { burn = new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee()) } - + // Sum up withdrawals. for _, w := range block.Withdrawals() { - withdrawals.Add(withdrawals, big.NewInt(int64(w.Amount))) + withdrawals.Add(withdrawals, newGwei(w.Amount)) } + return coinbaseReward, unclesReward, burn, withdrawals +} - return fixedReward, unclesReward, burn, withdrawals +func newGwei(n uint64) *big.Int { + return new(big.Int).Mul(big.NewInt(int64(n)), big.NewInt(params.GWei)) }