Skip to content

Commit e39372d

Browse files
committed
core,consensus: reduce code duplication, add test for supply.Delta
1 parent 0298e1e commit e39372d

File tree

4 files changed

+150
-81
lines changed

4 files changed

+150
-81
lines changed

consensus/ethash/consensus.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
488488
// Finalize implements consensus.Engine, accumulating the block and uncle rewards.
489489
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
490490
// Accumulate any block and uncle rewards
491-
accumulateRewards(chain.Config(), state, header, uncles)
491+
applyRewards(chain.Config(), state, header, uncles)
492492
}
493493

494494
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
@@ -543,10 +543,19 @@ var (
543543
big32 = big.NewInt(32)
544544
)
545545

546-
// AccumulateRewards credits the coinbase of the given block with the mining
547-
// reward. The total reward consists of the static block reward and rewards for
548-
// included uncles. The coinbase of each uncle block is also rewarded.
549-
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
546+
// applyRewards credits the coinbase of the given block with the mining reward.
547+
func applyRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
548+
f := func(h *types.Header, amt *big.Int) {
549+
state.AddBalance(h.Coinbase, amt)
550+
}
551+
AccumulateRewards(config, header, uncles, f, f)
552+
}
553+
554+
// AccumulateRewards is a generic function that allows the caller to decide how
555+
// to apply rewards. The total reward consists of the static block reward and
556+
// rewards for included uncles. The coinbase of each uncle block is also
557+
// rewarded.
558+
func AccumulateRewards(config *params.ChainConfig, header *types.Header, uncles []*types.Header, accUncleReward, accTotalReward func(*types.Header, *big.Int)) {
550559
// Select the correct block reward based on chain progression
551560
blockReward := FrontierBlockReward
552561
if config.IsByzantium(header.Number) {
@@ -563,10 +572,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
563572
r.Sub(r, header.Number)
564573
r.Mul(r, blockReward)
565574
r.Div(r, big8)
566-
state.AddBalance(uncle.Coinbase, r)
575+
accUncleReward(uncle, r)
567576

568577
r.Div(blockReward, big32)
569578
reward.Add(reward, r)
570579
}
571-
state.AddBalance(header.Coinbase, reward)
580+
accTotalReward(header, reward)
572581
}

core/blockchain.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1401,12 +1401,26 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
14011401
if parent == nil {
14021402
log.Error("Failed to retrieve parent for supply delta", "err", err)
14031403
} else {
1404-
supplyDelta, err := supply.Delta(block, parent, bc.stateCache.TrieDB(), bc.chainConfig)
1404+
start := time.Now()
1405+
1406+
supplyDelta, err := supply.Delta(parent, block.Header(), bc.stateCache.TrieDB())
14051407
if err != nil {
14061408
log.Error("Failed to record Ether supply delta", "err", err)
14071409
} else {
14081410
rawdb.WriteSupplyDelta(bc.db, block.NumberU64(), block.Hash(), supplyDelta)
14091411
}
1412+
1413+
// Calculate the block coinbaseReward based on chain rules and progression.
1414+
coinbaseReward, unclesReward, burn, withdrawals := supply.Subsidy(block, bc.chainConfig)
1415+
1416+
// Calculate the difference between the "calculated" and "crawled" supply delta.
1417+
diff := new(big.Int).Set(supplyDelta)
1418+
diff.Sub(diff, coinbaseReward)
1419+
diff.Sub(diff, unclesReward)
1420+
diff.Sub(diff, withdrawals)
1421+
diff.Add(diff, burn)
1422+
1423+
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))
14101424
}
14111425
}
14121426
return nil

core/blockchain_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/ethereum/go-ethereum/consensus/ethash"
3434
"github.com/ethereum/go-ethereum/core/rawdb"
3535
"github.com/ethereum/go-ethereum/core/state"
36+
"github.com/ethereum/go-ethereum/core/supply"
3637
"github.com/ethereum/go-ethereum/core/types"
3738
"github.com/ethereum/go-ethereum/core/vm"
3839
"github.com/ethereum/go-ethereum/crypto"
@@ -4340,4 +4341,82 @@ func TestEIP3651(t *testing.T) {
43404341
if actual.Cmp(expected) != 0 {
43414342
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
43424343
}
4344+
4345+
}
4346+
4347+
func TestDelta(t *testing.T) {
4348+
var (
4349+
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
4350+
engine = beacon.NewFaker()
4351+
4352+
// A sender who makes transactions, has some funds
4353+
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
4354+
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
4355+
funds = big.NewInt(params.Ether)
4356+
gspec = &Genesis{
4357+
Config: params.AllEthashProtocolChanges,
4358+
Alloc: GenesisAlloc{
4359+
addr1: {Balance: funds},
4360+
// The address 0xAAAA self-destructs
4361+
aa: {
4362+
Code: []byte{
4363+
byte(vm.ADDRESS),
4364+
byte(vm.SELFDESTRUCT),
4365+
},
4366+
Nonce: 0,
4367+
Balance: big.NewInt(41),
4368+
},
4369+
},
4370+
}
4371+
)
4372+
4373+
gspec.Config.TerminalTotalDifficulty = common.Big0
4374+
gspec.Config.TerminalTotalDifficultyPassed = true
4375+
gspec.Config.ShanghaiTime = u64(0)
4376+
signer := types.LatestSigner(gspec.Config)
4377+
4378+
db, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
4379+
b.SetCoinbase(common.Address{1})
4380+
4381+
// One transaction to 0xAAAA
4382+
txdata := &types.DynamicFeeTx{
4383+
ChainID: gspec.Config.ChainID,
4384+
Nonce: 0,
4385+
To: &aa,
4386+
Value: common.Big1,
4387+
Gas: 50000,
4388+
GasFeeCap: newGwei(5),
4389+
GasTipCap: big.NewInt(2),
4390+
}
4391+
tx := types.NewTx(txdata)
4392+
tx, _ = types.SignTx(tx, signer, key1)
4393+
4394+
b.AddTx(tx)
4395+
b.AddWithdrawal(&types.Withdrawal{Amount: 1337})
4396+
})
4397+
4398+
var (
4399+
parent = gspec.ToBlock().Header()
4400+
block = blocks[0]
4401+
)
4402+
4403+
got, err := supply.Delta(parent, block.Header(), trie.NewDatabase(db))
4404+
if err != nil {
4405+
t.Fatalf("failed to calculate delta: %v", err)
4406+
}
4407+
4408+
// Calculate delta, w/o self-destructs
4409+
coinbaseReward, _, burn, withdrawals := supply.Subsidy(block, gspec.Config)
4410+
4411+
want := new(big.Int)
4412+
want.Add(want, coinbaseReward)
4413+
want.Add(want, withdrawals)
4414+
want.Sub(want, burn)
4415+
4416+
// Now account for self-destructed amount.
4417+
want.Sub(want, big.NewInt(42))
4418+
4419+
if want.Cmp(got) != 0 {
4420+
t.Fatalf("incorrect delta calculated: want %d, got %d", want, got)
4421+
}
43434422
}

core/supply/delta.go

Lines changed: 40 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -19,120 +19,87 @@ package supply
1919
import (
2020
"fmt"
2121
"math/big"
22-
"time"
2322

2423
"github.com/ethereum/go-ethereum/consensus/ethash"
2524
"github.com/ethereum/go-ethereum/core/types"
26-
"github.com/ethereum/go-ethereum/log"
2725
"github.com/ethereum/go-ethereum/params"
2826
"github.com/ethereum/go-ethereum/rlp"
2927
"github.com/ethereum/go-ethereum/trie"
3028
)
3129

32-
// Delta calculates the Ether delta across two state tries. That is, the
30+
// Delta calculates the ether delta across two state tries. That is, the
3331
// issuance minus the ether destroyed.
34-
func Delta(block *types.Block, parent *types.Header, db *trie.Database, config *params.ChainConfig) (*big.Int, error) {
35-
var (
36-
supplyDelta = new(big.Int)
37-
start = time.Now()
38-
)
39-
// Open the two tries.
40-
if block.ParentHash() != parent.Hash() {
41-
return nil, fmt.Errorf("parent hash mismatch: have %s, want %s", block.ParentHash().Hex(), parent.Hash().Hex())
42-
}
43-
src, err := trie.New(trie.StateTrieID(parent.Root), db)
32+
func Delta(src, dst *types.Header, db *trie.Database) (*big.Int, error) {
33+
// Open src and dst tries.
34+
srcTrie, err := trie.New(trie.StateTrieID(src.Root), db)
4435
if err != nil {
4536
return nil, fmt.Errorf("failed to open source trie: %v", err)
4637
}
47-
dst, err := trie.New(trie.StateTrieID(block.Root()), db)
38+
dstTrie, err := trie.New(trie.StateTrieID(dst.Root), db)
4839
if err != nil {
4940
return nil, fmt.Errorf("failed to open destination trie: %v", err)
5041
}
42+
43+
delta := new(big.Int)
44+
5145
// Gather all the changes across from source to destination.
52-
fwdDiffIt, _ := trie.NewDifferenceIterator(src.MustNodeIterator(nil), dst.MustNodeIterator(nil))
46+
fwdDiffIt, _ := trie.NewDifferenceIterator(srcTrie.MustNodeIterator(nil), dstTrie.MustNodeIterator(nil))
5347
fwdIt := trie.NewIterator(fwdDiffIt)
5448

5549
for fwdIt.Next() {
5650
acc := new(types.StateAccount)
5751
if err := rlp.DecodeBytes(fwdIt.Value, acc); err != nil {
5852
panic(err)
5953
}
60-
supplyDelta.Add(supplyDelta, acc.Balance)
54+
delta.Add(delta, acc.Balance)
6155
}
6256
// Gather all the changes across from destination to source.
63-
rewDiffIt, _ := trie.NewDifferenceIterator(dst.MustNodeIterator(nil), src.MustNodeIterator(nil))
64-
rewIt := trie.NewIterator(rewDiffIt)
57+
revDiffIt, _ := trie.NewDifferenceIterator(dstTrie.MustNodeIterator(nil), srcTrie.MustNodeIterator(nil))
58+
revIt := trie.NewIterator(revDiffIt)
6559

66-
for rewIt.Next() {
60+
for revIt.Next() {
6761
acc := new(types.StateAccount)
68-
if err := rlp.DecodeBytes(rewIt.Value, acc); err != nil {
62+
if err := rlp.DecodeBytes(revIt.Value, acc); err != nil {
6963
panic(err)
7064
}
71-
supplyDelta.Sub(supplyDelta, acc.Balance)
65+
delta.Sub(delta, acc.Balance)
7266
}
73-
// Calculate the block fixedReward based on chain rules and progression.
74-
fixedReward, unclesReward, burn, withdrawals := Subsidy(block, config)
75-
76-
// Calculate the difference between the "calculated" and "crawled" supply
77-
// delta.
78-
diff := new(big.Int).Set(supplyDelta)
79-
diff.Sub(diff, fixedReward)
80-
diff.Sub(diff, unclesReward)
81-
diff.Add(diff, burn)
8267

83-
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))
84-
return supplyDelta, nil
68+
return delta, nil
8569
}
8670

87-
// Subsidy calculates the block mining (fixed) and uncle subsidy as well as the
88-
// 1559 burn solely based on header fields. This method is a very accurate
89-
// approximation of the true supply delta, but cannot take into account Ether
90-
// burns via selfdestructs, so it will always be ever so slightly off.
91-
func Subsidy(block *types.Block, config *params.ChainConfig) (fixedReward *big.Int, unclesReward *big.Int, burn *big.Int, withdrawals *big.Int) {
92-
// Calculate the block rewards based on chain rules and progression.
93-
fixedReward = new(big.Int)
94-
unclesReward = new(big.Int)
95-
withdrawals = new(big.Int)
96-
97-
// Select the correct block reward based on chain progression.
98-
if config.Ethash != nil {
99-
if block.Difficulty().BitLen() != 0 {
100-
fixedReward = ethash.FrontierBlockReward
101-
if config.IsByzantium(block.Number()) {
102-
fixedReward = ethash.ByzantiumBlockReward
103-
}
104-
if config.IsConstantinople(block.Number()) {
105-
fixedReward = ethash.ConstantinopleBlockReward
106-
}
71+
// Subsidy calculates the coinbase subsidy and uncle subsidy as well as the
72+
// EIP-1559 burn. This method is a very accurate approximation of the true
73+
// supply delta, but cannot take into account ether burns via selfdestructs, so
74+
// it will always be slightly off.
75+
func Subsidy(block *types.Block, config *params.ChainConfig) (*big.Int, *big.Int, *big.Int, *big.Int) {
76+
var (
77+
coinbaseReward = new(big.Int)
78+
unclesReward = new(big.Int)
79+
withdrawals = new(big.Int)
80+
)
81+
// If block is ethash, calculate the coinbase and uncle rewards.
82+
if config.Ethash != nil && block.Difficulty().BitLen() != 0 {
83+
accCoinbase := func(h *types.Header, amt *big.Int) {
84+
coinbaseReward.Add(coinbaseReward, amt)
10785
}
108-
// Accumulate the rewards for included uncles.
109-
var (
110-
big8 = big.NewInt(8)
111-
big32 = big.NewInt(32)
112-
r = new(big.Int)
113-
)
114-
for _, uncle := range block.Uncles() {
115-
// Add the reward for the side blocks.
116-
r.Add(uncle.Number, big8)
117-
r.Sub(r, block.Number())
118-
r.Mul(r, fixedReward)
119-
r.Div(r, big8)
120-
unclesReward.Add(unclesReward, r)
121-
122-
// Add the reward for accumulating the side blocks.
123-
r.Div(fixedReward, big32)
124-
unclesReward.Add(unclesReward, r)
86+
accUncles := func(h *types.Header, amt *big.Int) {
87+
unclesReward.Add(unclesReward, amt)
12588
}
89+
ethash.AccumulateRewards(config, block.Header(), block.Uncles(), accCoinbase, accUncles)
12690
}
12791
// Calculate the burn based on chain rules and progression.
128-
burn = new(big.Int)
92+
burn := new(big.Int)
12993
if block.BaseFee() != nil {
13094
burn = new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee())
13195
}
132-
96+
// Sum up withdrawals.
13397
for _, w := range block.Withdrawals() {
134-
withdrawals.Add(withdrawals, big.NewInt(int64(w.Amount)))
98+
withdrawals.Add(withdrawals, newGwei(w.Amount))
13599
}
100+
return coinbaseReward, unclesReward, burn, withdrawals
101+
}
136102

137-
return fixedReward, unclesReward, burn, withdrawals
103+
func newGwei(n uint64) *big.Int {
104+
return new(big.Int).Mul(big.NewInt(int64(n)), big.NewInt(params.GWei))
138105
}

0 commit comments

Comments
 (0)