Skip to content

Commit 7fd26e5

Browse files
committed
fix: qbft issues with block rewards and beneficary
- multiple issues were found with coinbase rewards when running qbft - fix up issues with MiningBeneficiary
1 parent 703f2a0 commit 7fd26e5

File tree

8 files changed

+87
-188
lines changed

8 files changed

+87
-188
lines changed

cmd/utils/flags.go

-1
Original file line numberDiff line numberDiff line change
@@ -2536,7 +2536,6 @@ func MakeChain(ctx *cli.Context, stack *node.Node, useExist bool) (chain *core.B
25362536
qbftConfig := setBFTConfig(config.QBFT.BFTConfig)
25372537
qbftConfig.BlockReward = config.QBFT.BlockReward
25382538
qbftConfig.BeneficiaryMode = config.QBFT.BeneficiaryMode // beneficiary mode: list, besu, validators
2539-
qbftConfig.BeneficiaryList = config.QBFT.BeneficiaryList // list mode
25402539
qbftConfig.MiningBeneficiary = config.QBFT.MiningBeneficiary // auto (undefined mode) and besu mode
25412540
qbftConfig.TestQBFTBlock = big.NewInt(0)
25422541
qbftConfig.Transitions = config.Transitions

consensus/istanbul/config.go

+7-11
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ func (p *ProposerPolicy) ClearRegistry() {
124124

125125
type Config struct {
126126
RequestTimeout uint64 `toml:",omitempty"` // The timeout for each Istanbul round in milliseconds.
127-
BlockReward *math.HexOrDecimal256 `toml:",omitempty"` // Reward
128127
BlockPeriod uint64 `toml:",omitempty"` // Default minimum difference between two consecutive block's timestamps in second
129128
EmptyBlockPeriod uint64 `toml:",omitempty"` // Default minimum difference between a block and empty block's timestamps in second
130129
ProposerPolicy *ProposerPolicy `toml:",omitempty"` // The policy for proposer selection
@@ -133,13 +132,13 @@ type Config struct {
133132
AllowedFutureBlockTime uint64 `toml:",omitempty"` // Max time (in seconds) from current time allowed for blocks, before they're considered future blocks
134133
TestQBFTBlock *big.Int `toml:",omitempty"` // Fork block at which block confirmations are done using qbft consensus instead of ibft
135134
BeneficiaryMode *string `toml:",omitempty"` // Mode for setting the beneficiary, either: list, besu, validators (beneficiary list is the list of validators)
136-
BeneficiaryList []common.Address `toml:",omitempty"` // List of wallet addresses that have benefit at every new block (list mode)
135+
BlockReward *math.HexOrDecimal256 `toml:",omitempty"` // Reward
137136
MiningBeneficiary *common.Address `toml:",omitempty"` // Wallet address that benefits at every new block (besu mode)
137+
ValidatorContract common.Address `toml:",omitempty"`
138+
Validators []common.Address `toml:",omitempty"`
139+
ValidatorSelectionMode *string `toml:",omitempty"`
140+
Client bind.ContractCaller `toml:",omitempty"`
138141
Transitions []params.Transition
139-
ValidatorContract common.Address `toml:",omitempty"`
140-
Validators []common.Address `toml:",omitempty"`
141-
ValidatorSelectionMode *string `toml:",omitempty"`
142-
Client bind.ContractCaller `toml:",omitempty"`
143142
}
144143

145144
var DefaultConfig = &Config{
@@ -201,13 +200,10 @@ func (c Config) GetConfig(blockNumber *big.Int) Config {
201200
if transition.EmptyBlockPeriodSeconds != nil {
202201
newConfig.EmptyBlockPeriod = *transition.EmptyBlockPeriodSeconds
203202
}
204-
if transition.BeneficiaryMode != nil { // besu mode
203+
if transition.BeneficiaryMode != nil {
205204
newConfig.BeneficiaryMode = transition.BeneficiaryMode
206205
}
207-
if transition.BeneficiaryList != nil { // list mode
208-
newConfig.BeneficiaryList = transition.BeneficiaryList
209-
}
210-
if transition.BlockReward != nil { // besu mode
206+
if transition.BlockReward != nil {
211207
newConfig.BlockReward = transition.BlockReward
212208
}
213209
if transition.MiningBeneficiary != nil {

consensus/istanbul/qbft/engine/engine.go

+12-37
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"fmt"
66
"math/big"
7-
"strings"
87
"time"
98

109
"github.com/ethereum/go-ethereum/accounts/abi/bind"
@@ -383,7 +382,7 @@ func WriteValidators(validators []common.Address) ApplyQBFTExtra {
383382
// consensus rules that happen at finalization (e.g. block rewards).
384383
func (e *Engine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
385384
// Accumulate any block and uncle rewards and commit the final state root
386-
e.accumulateRewards(chain, state, header, uncles, e.cfg.GetConfig(header.Number))
385+
e.accumulateRewards(chain, state, header)
387386
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
388387
header.UncleHash = nilUncleHash
389388
}
@@ -586,41 +585,17 @@ func (e *Engine) validatorsList(genesis *types.Header, config istanbul.Config) (
586585
}
587586

588587
// AccumulateRewards credits the beneficiary of the given block with a reward.
589-
func (e *Engine) accumulateRewards(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header, uncles []*types.Header, cfg istanbul.Config) {
590-
blockReward := cfg.BlockReward
591-
if blockReward == nil {
592-
return // no reward
593-
}
594-
// Accumulate the rewards for a beneficiary
595-
reward := big.Int(*blockReward)
596-
if cfg.BeneficiaryMode == nil || *cfg.BeneficiaryMode == "" {
597-
if cfg.MiningBeneficiary != nil {
598-
state.AddBalance(*cfg.MiningBeneficiary, &reward) // implicit besu compatible mode
599-
}
600-
return
601-
}
602-
switch strings.ToLower(*cfg.BeneficiaryMode) {
603-
case "fixed":
604-
if cfg.MiningBeneficiary != nil {
605-
state.AddBalance(*cfg.MiningBeneficiary, &reward)
606-
}
607-
case "list":
608-
for _, b := range cfg.BeneficiaryList {
609-
state.AddBalance(b, &reward)
610-
}
611-
case "validators":
612-
genesis := chain.GetHeaderByNumber(0)
613-
if err := e.VerifyHeader(chain, genesis, nil, nil); err != nil {
614-
log.Error("BFT: invalid genesis block", "err", err)
615-
return
616-
}
617-
list, err := e.validatorsList(genesis, cfg)
618-
if err != nil {
619-
log.Error("get validators list", "err", err)
620-
return
621-
}
622-
for _, b := range list {
623-
state.AddBalance(b, &reward)
588+
func (e *Engine) accumulateRewards(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header) {
589+
590+
blockReward := chain.Config().GetBlockReward(header.Number)
591+
if blockReward.Cmp(big.NewInt(0)) > 0 {
592+
coinbase := header.Coinbase
593+
if (coinbase == common.Address{}) {
594+
coinbase = e.signer
624595
}
596+
rewardAccount, _ := chain.Config().GetRewardAccount(header.Number, coinbase)
597+
log.Trace("QBFT: accumulate rewards to", "rewardAccount", rewardAccount, "blockReward", blockReward)
598+
599+
state.AddBalance(rewardAccount, &blockReward)
625600
}
626601
}

consensus/istanbul/qbft/engine/engine_test.go

-129
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,14 @@ package qbftengine
22

33
import (
44
"bytes"
5-
"fmt"
65
"math/big"
76
"reflect"
87
"testing"
98

109
"github.com/ethereum/go-ethereum/common"
1110
"github.com/ethereum/go-ethereum/common/hexutil"
12-
"github.com/ethereum/go-ethereum/common/math"
13-
"github.com/ethereum/go-ethereum/consensus/istanbul"
1411
istanbulcommon "github.com/ethereum/go-ethereum/consensus/istanbul/common"
15-
"github.com/ethereum/go-ethereum/core"
16-
"github.com/ethereum/go-ethereum/core/rawdb"
17-
"github.com/ethereum/go-ethereum/core/state"
1812
"github.com/ethereum/go-ethereum/core/types"
19-
"github.com/stretchr/testify/assert"
20-
"github.com/stretchr/testify/require"
2113
)
2214

2315
func TestPrepareExtra(t *testing.T) {
@@ -167,124 +159,3 @@ func TestWriteValidatorVote(t *testing.T) {
167159
t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
168160
}
169161
}
170-
171-
func TestAccumulateRewards(t *testing.T) {
172-
addr := common.HexToAddress("0xed9d02e382b34818e88b88a309c7fe71e65f419d")
173-
fixedMode := "fixed"
174-
listMode := "list"
175-
validatorsMode := "validators"
176-
emptyMode := ""
177-
dummyMode := "dummy"
178-
m := []struct {
179-
addr common.Address
180-
miningBeneficiary *common.Address
181-
balance *big.Int
182-
blockReward *math.HexOrDecimal256
183-
mode *string
184-
list []common.Address
185-
expectedBalance *big.Int
186-
}{
187-
{ // off
188-
addr: addr,
189-
miningBeneficiary: nil,
190-
balance: big.NewInt(1),
191-
blockReward: math.NewHexOrDecimal256(1),
192-
mode: nil,
193-
list: nil,
194-
expectedBalance: big.NewInt(1),
195-
},
196-
{ // auto/default
197-
addr: addr,
198-
miningBeneficiary: &addr,
199-
balance: big.NewInt(1),
200-
blockReward: math.NewHexOrDecimal256(1),
201-
mode: nil,
202-
list: nil,
203-
expectedBalance: big.NewInt(2),
204-
},
205-
{ // failing
206-
addr: addr,
207-
miningBeneficiary: nil,
208-
balance: big.NewInt(1),
209-
blockReward: math.NewHexOrDecimal256(1),
210-
mode: &fixedMode,
211-
list: nil,
212-
expectedBalance: big.NewInt(1),
213-
},
214-
{
215-
addr: addr,
216-
miningBeneficiary: &addr,
217-
balance: big.NewInt(1),
218-
blockReward: math.NewHexOrDecimal256(1),
219-
mode: &fixedMode,
220-
list: nil,
221-
expectedBalance: big.NewInt(2),
222-
},
223-
{ // failing
224-
addr: addr,
225-
miningBeneficiary: nil,
226-
balance: big.NewInt(1),
227-
blockReward: math.NewHexOrDecimal256(1),
228-
mode: &listMode,
229-
list: nil,
230-
expectedBalance: big.NewInt(1),
231-
},
232-
{
233-
addr: addr,
234-
miningBeneficiary: nil,
235-
balance: big.NewInt(1),
236-
blockReward: math.NewHexOrDecimal256(1),
237-
mode: &listMode,
238-
list: []common.Address{addr},
239-
expectedBalance: big.NewInt(2),
240-
},
241-
{
242-
addr: addr,
243-
miningBeneficiary: nil,
244-
balance: big.NewInt(1),
245-
blockReward: math.NewHexOrDecimal256(1),
246-
mode: &validatorsMode,
247-
expectedBalance: big.NewInt(1),
248-
},
249-
{
250-
addr: addr,
251-
miningBeneficiary: nil,
252-
balance: big.NewInt(1),
253-
blockReward: math.NewHexOrDecimal256(1),
254-
mode: &emptyMode,
255-
expectedBalance: big.NewInt(1),
256-
},
257-
{
258-
addr: addr,
259-
miningBeneficiary: nil,
260-
balance: big.NewInt(1),
261-
blockReward: math.NewHexOrDecimal256(1),
262-
mode: &dummyMode,
263-
expectedBalance: big.NewInt(1),
264-
},
265-
}
266-
var e *Engine
267-
chain := &core.BlockChain{}
268-
db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), nil)
269-
state, err := state.New(common.Hash{}, db, nil)
270-
require.NoError(t, err)
271-
272-
header := &types.Header{
273-
Number: big.NewInt(1),
274-
}
275-
for idx, te := range m {
276-
if te.mode == &validatorsMode {
277-
continue // skip, it's not testable yet
278-
}
279-
state.SetBalance(te.addr, te.balance)
280-
cfg := istanbul.Config{
281-
BlockReward: te.blockReward,
282-
BeneficiaryMode: te.mode,
283-
BeneficiaryList: te.list,
284-
MiningBeneficiary: te.miningBeneficiary,
285-
}
286-
e.accumulateRewards(chain, state, header, nil, cfg)
287-
balance := state.GetBalance(te.addr)
288-
assert.Equal(t, te.expectedBalance, balance, fmt.Sprintf("index: %d", idx), te)
289-
}
290-
}

core/state_transition.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,16 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
401401
if !isPrivate {
402402
st.gas = leftoverGas
403403
}
404-
// End Quorum
404+
405+
// Quorum with gas enabled we can specify if it goes to coinbase(ie validators) or a fixed beneficiary
406+
// Note the rewards here are only for transitions, any additional block rewards must go
407+
rewardAccount, err := st.evm.ChainConfig().GetRewardAccount(st.evm.Context.BlockNumber, st.evm.Context.Coinbase)
408+
if err != nil {
409+
return nil, err
410+
}
405411

406412
st.refundGas()
407-
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
413+
st.state.AddBalance(rewardAccount, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
408414

409415
if isPrivate {
410416
return &ExecutionResult{

eth/ethconfig/config.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,11 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, co
296296
if chainConfig.QBFT.ValidatorContractAddress != (common.Address{}) {
297297
config.Istanbul.ValidatorContract = chainConfig.QBFT.ValidatorContractAddress
298298
}
299-
config.Istanbul.Validators = chainConfig.QBFT.Validators
299+
config.Istanbul.BlockReward = chainConfig.QBFT.BlockReward
300+
config.Istanbul.BeneficiaryMode = chainConfig.QBFT.BeneficiaryMode
301+
config.Istanbul.MiningBeneficiary = chainConfig.QBFT.MiningBeneficiary
300302
config.Istanbul.ValidatorSelectionMode = chainConfig.QBFT.ValidatorSelectionMode
303+
config.Istanbul.Validators = chainConfig.QBFT.Validators
301304

302305
return istanbulBackend.New(&config.Istanbul, stack.GetNodeKey(), db)
303306
}

params/config.go

+51-2
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,6 @@ type QBFTConfig struct {
431431
*BFTConfig
432432
BlockReward *math.HexOrDecimal256 `json:"blockReward,omitempty"` // Reward from start, works only on QBFT consensus protocol
433433
BeneficiaryMode *string `json:"beneficiaryMode,omitempty"` // Mode for setting the beneficiary, either: list, besu, validators (beneficiary list is the list of validators)
434-
BeneficiaryList []common.Address `json:"beneficiaryList,omitempty"` // List of wallet addresses that have benefit at every new block (list mode)
435434
MiningBeneficiary *common.Address `json:"miningBeneficiary,omitempty"` // Wallet address that benefits at every new block (besu mode)
436435
ValidatorSelectionMode *string `json:"validatorselectionmode,omitempty"` // Select model for validators
437436
Validators []common.Address `json:"validators"` // Validators list
@@ -469,7 +468,6 @@ type Transition struct {
469468
TransactionSizeLimit uint64 `json:"transactionSizeLimit,omitempty"` // Modify TransactionSizeLimit
470469
BlockReward *math.HexOrDecimal256 `json:"blockReward,omitempty"` // validation rewards
471470
BeneficiaryMode *string `json:"beneficiaryMode,omitempty"` // Mode for setting the beneficiary, either: list, besu, validators (beneficiary list is the list of validators)
472-
BeneficiaryList []common.Address `json:"beneficiaryList,omitempty"` // List of wallet addresses that have benefit at every new block (list mode)
473471
MiningBeneficiary *common.Address `json:"miningBeneficiary,omitempty"` // Wallet address that benefits at every new block (besu mode)
474472
}
475473

@@ -639,6 +637,57 @@ func (c *ChainConfig) GetMaxCodeSize(num *big.Int) int {
639637
return maxCodeSize
640638
}
641639

640+
func (c *ChainConfig) GetRewardAccount(num *big.Int, coinbase common.Address) (common.Address, error) {
641+
beneficiaryMode := "validator"
642+
miningBeneficiary := common.Address{}
643+
644+
if c.QBFT.MiningBeneficiary != nil {
645+
miningBeneficiary = *c.QBFT.MiningBeneficiary
646+
beneficiaryMode = "fixed"
647+
}
648+
649+
if c.QBFT.BeneficiaryMode != nil {
650+
beneficiaryMode = *c.QBFT.BeneficiaryMode
651+
}
652+
653+
c.GetTransitionValue(num, func(transition Transition) {
654+
if transition.BeneficiaryMode != nil && (*transition.BeneficiaryMode == "validators" || *transition.BeneficiaryMode == "validator") {
655+
beneficiaryMode = "validator"
656+
}
657+
if transition.MiningBeneficiary != nil && (transition.BeneficiaryMode == nil || *transition.BeneficiaryMode == "fixed") {
658+
miningBeneficiary = *transition.MiningBeneficiary
659+
beneficiaryMode = "fixed"
660+
}
661+
})
662+
663+
switch strings.ToLower(beneficiaryMode) {
664+
case "fixed":
665+
log.Trace("fixed beneficiary mode", "miningBeneficiary", miningBeneficiary)
666+
return miningBeneficiary, nil
667+
case "validator":
668+
log.Trace("validator beneficiary mode", "coinbase", coinbase)
669+
return coinbase, nil
670+
}
671+
672+
return common.Address{}, errors.New("BeneficiaryMode must be coinbase|fixed")
673+
}
674+
675+
func (c *ChainConfig) GetBlockReward(num *big.Int) big.Int {
676+
blockReward := *math.NewHexOrDecimal256(0)
677+
678+
if c.QBFT.BlockReward != nil {
679+
blockReward = *c.QBFT.BlockReward
680+
}
681+
682+
c.GetTransitionValue(num, func(transition Transition) {
683+
if transition.BlockReward != nil {
684+
blockReward = *transition.BlockReward
685+
}
686+
})
687+
688+
return big.Int(blockReward)
689+
}
690+
642691
// Quorum
643692
// gets value at or after a transition
644693
func (c *ChainConfig) GetTransitionValue(num *big.Int, callback func(transition Transition)) {

params/config_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,10 @@ func TestCheckTransitionsData(t *testing.T) {
332332
var ibftTransitionsConfig, qbftTransitionsConfig, invalidTransition, invalidBlockOrder []Transition
333333
var emptyBlockPeriodSeconds uint64 = 10
334334

335-
tranI0 := Transition{big.NewInt(0), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil, nil}
336-
tranQ5 := Transition{big.NewInt(5), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil, nil}
337-
tranI10 := Transition{big.NewInt(10), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil, nil}
338-
tranQ8 := Transition{big.NewInt(8), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil, nil}
335+
tranI0 := Transition{big.NewInt(0), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}
336+
tranQ5 := Transition{big.NewInt(5), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}
337+
tranI10 := Transition{big.NewInt(10), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}
338+
tranQ8 := Transition{big.NewInt(8), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}
339339

340340
ibftTransitionsConfig = append(ibftTransitionsConfig, tranI0, tranI10)
341341
qbftTransitionsConfig = append(qbftTransitionsConfig, tranQ5, tranQ8)
@@ -395,7 +395,7 @@ func TestCheckTransitionsData(t *testing.T) {
395395
wantErr: ErrBlockOrder,
396396
},
397397
{
398-
stored: &ChainConfig{Transitions: []Transition{{nil, IBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil, nil}}},
398+
stored: &ChainConfig{Transitions: []Transition{{nil, IBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}}},
399399
wantErr: ErrBlockNumberMissing,
400400
},
401401
{

0 commit comments

Comments
 (0)