Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: qbft issues with block rewards and beneficary #1547

Merged
merged 1 commit into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2536,7 +2536,6 @@ func MakeChain(ctx *cli.Context, stack *node.Node, useExist bool) (chain *core.B
qbftConfig := setBFTConfig(config.QBFT.BFTConfig)
qbftConfig.BlockReward = config.QBFT.BlockReward
qbftConfig.BeneficiaryMode = config.QBFT.BeneficiaryMode // beneficiary mode: list, besu, validators
qbftConfig.BeneficiaryList = config.QBFT.BeneficiaryList // list mode
qbftConfig.MiningBeneficiary = config.QBFT.MiningBeneficiary // auto (undefined mode) and besu mode
qbftConfig.TestQBFTBlock = big.NewInt(0)
qbftConfig.Transitions = config.Transitions
Expand Down
18 changes: 7 additions & 11 deletions consensus/istanbul/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ func (p *ProposerPolicy) ClearRegistry() {

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

var DefaultConfig = &Config{
Expand Down Expand Up @@ -201,13 +200,10 @@ func (c Config) GetConfig(blockNumber *big.Int) Config {
if transition.EmptyBlockPeriodSeconds != nil {
newConfig.EmptyBlockPeriod = *transition.EmptyBlockPeriodSeconds
}
if transition.BeneficiaryMode != nil { // besu mode
if transition.BeneficiaryMode != nil {
newConfig.BeneficiaryMode = transition.BeneficiaryMode
}
if transition.BeneficiaryList != nil { // list mode
newConfig.BeneficiaryList = transition.BeneficiaryList
}
if transition.BlockReward != nil { // besu mode
if transition.BlockReward != nil {
newConfig.BlockReward = transition.BlockReward
}
if transition.MiningBeneficiary != nil {
Expand Down
49 changes: 12 additions & 37 deletions consensus/istanbul/qbft/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"math/big"
"strings"
"time"

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

// AccumulateRewards credits the beneficiary of the given block with a reward.
func (e *Engine) accumulateRewards(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header, uncles []*types.Header, cfg istanbul.Config) {
blockReward := cfg.BlockReward
if blockReward == nil {
return // no reward
}
// Accumulate the rewards for a beneficiary
reward := big.Int(*blockReward)
if cfg.BeneficiaryMode == nil || *cfg.BeneficiaryMode == "" {
if cfg.MiningBeneficiary != nil {
state.AddBalance(*cfg.MiningBeneficiary, &reward) // implicit besu compatible mode
}
return
}
switch strings.ToLower(*cfg.BeneficiaryMode) {
case "fixed":
if cfg.MiningBeneficiary != nil {
state.AddBalance(*cfg.MiningBeneficiary, &reward)
}
case "list":
for _, b := range cfg.BeneficiaryList {
state.AddBalance(b, &reward)
}
case "validators":
genesis := chain.GetHeaderByNumber(0)
if err := e.VerifyHeader(chain, genesis, nil, nil); err != nil {
log.Error("BFT: invalid genesis block", "err", err)
return
}
list, err := e.validatorsList(genesis, cfg)
if err != nil {
log.Error("get validators list", "err", err)
return
}
for _, b := range list {
state.AddBalance(b, &reward)
func (e *Engine) accumulateRewards(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header) {

blockReward := chain.Config().GetBlockReward(header.Number)
if blockReward.Cmp(big.NewInt(0)) > 0 {
coinbase := header.Coinbase
if (coinbase == common.Address{}) {
coinbase = e.signer
}
rewardAccount, _ := chain.Config().GetRewardAccount(header.Number, coinbase)
log.Trace("QBFT: accumulate rewards to", "rewardAccount", rewardAccount, "blockReward", blockReward)

state.AddBalance(rewardAccount, &blockReward)
}
}
129 changes: 0 additions & 129 deletions consensus/istanbul/qbft/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,14 @@ package qbftengine

import (
"bytes"
"fmt"
"math/big"
"reflect"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/istanbul"
istanbulcommon "github.com/ethereum/go-ethereum/consensus/istanbul/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestPrepareExtra(t *testing.T) {
Expand Down Expand Up @@ -167,124 +159,3 @@ func TestWriteValidatorVote(t *testing.T) {
t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
}
}

func TestAccumulateRewards(t *testing.T) {
addr := common.HexToAddress("0xed9d02e382b34818e88b88a309c7fe71e65f419d")
fixedMode := "fixed"
listMode := "list"
validatorsMode := "validators"
emptyMode := ""
dummyMode := "dummy"
m := []struct {
addr common.Address
miningBeneficiary *common.Address
balance *big.Int
blockReward *math.HexOrDecimal256
mode *string
list []common.Address
expectedBalance *big.Int
}{
{ // off
addr: addr,
miningBeneficiary: nil,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: nil,
list: nil,
expectedBalance: big.NewInt(1),
},
{ // auto/default
addr: addr,
miningBeneficiary: &addr,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: nil,
list: nil,
expectedBalance: big.NewInt(2),
},
{ // failing
addr: addr,
miningBeneficiary: nil,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: &fixedMode,
list: nil,
expectedBalance: big.NewInt(1),
},
{
addr: addr,
miningBeneficiary: &addr,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: &fixedMode,
list: nil,
expectedBalance: big.NewInt(2),
},
{ // failing
addr: addr,
miningBeneficiary: nil,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: &listMode,
list: nil,
expectedBalance: big.NewInt(1),
},
{
addr: addr,
miningBeneficiary: nil,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: &listMode,
list: []common.Address{addr},
expectedBalance: big.NewInt(2),
},
{
addr: addr,
miningBeneficiary: nil,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: &validatorsMode,
expectedBalance: big.NewInt(1),
},
{
addr: addr,
miningBeneficiary: nil,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: &emptyMode,
expectedBalance: big.NewInt(1),
},
{
addr: addr,
miningBeneficiary: nil,
balance: big.NewInt(1),
blockReward: math.NewHexOrDecimal256(1),
mode: &dummyMode,
expectedBalance: big.NewInt(1),
},
}
var e *Engine
chain := &core.BlockChain{}
db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), nil)
state, err := state.New(common.Hash{}, db, nil)
require.NoError(t, err)

header := &types.Header{
Number: big.NewInt(1),
}
for idx, te := range m {
if te.mode == &validatorsMode {
continue // skip, it's not testable yet
}
state.SetBalance(te.addr, te.balance)
cfg := istanbul.Config{
BlockReward: te.blockReward,
BeneficiaryMode: te.mode,
BeneficiaryList: te.list,
MiningBeneficiary: te.miningBeneficiary,
}
e.accumulateRewards(chain, state, header, nil, cfg)
balance := state.GetBalance(te.addr)
assert.Equal(t, te.expectedBalance, balance, fmt.Sprintf("index: %d", idx), te)
}
}
10 changes: 8 additions & 2 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,16 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if !isPrivate {
st.gas = leftoverGas
}
// End Quorum

// Quorum with gas enabled we can specify if it goes to coinbase(ie validators) or a fixed beneficiary
// Note the rewards here are only for transitions, any additional block rewards must go
rewardAccount, err := st.evm.ChainConfig().GetRewardAccount(st.evm.Context.BlockNumber, st.evm.Context.Coinbase)
if err != nil {
return nil, err
}

st.refundGas()
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
st.state.AddBalance(rewardAccount, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))

if isPrivate {
return &ExecutionResult{
Expand Down
5 changes: 4 additions & 1 deletion eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,11 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, co
if chainConfig.QBFT.ValidatorContractAddress != (common.Address{}) {
config.Istanbul.ValidatorContract = chainConfig.QBFT.ValidatorContractAddress
}
config.Istanbul.Validators = chainConfig.QBFT.Validators
config.Istanbul.BlockReward = chainConfig.QBFT.BlockReward
config.Istanbul.BeneficiaryMode = chainConfig.QBFT.BeneficiaryMode
config.Istanbul.MiningBeneficiary = chainConfig.QBFT.MiningBeneficiary
config.Istanbul.ValidatorSelectionMode = chainConfig.QBFT.ValidatorSelectionMode
config.Istanbul.Validators = chainConfig.QBFT.Validators

return istanbulBackend.New(&config.Istanbul, stack.GetNodeKey(), db)
}
Expand Down
53 changes: 51 additions & 2 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,6 @@ type QBFTConfig struct {
*BFTConfig
BlockReward *math.HexOrDecimal256 `json:"blockReward,omitempty"` // Reward from start, works only on QBFT consensus protocol
BeneficiaryMode *string `json:"beneficiaryMode,omitempty"` // Mode for setting the beneficiary, either: list, besu, validators (beneficiary list is the list of validators)
BeneficiaryList []common.Address `json:"beneficiaryList,omitempty"` // List of wallet addresses that have benefit at every new block (list mode)
MiningBeneficiary *common.Address `json:"miningBeneficiary,omitempty"` // Wallet address that benefits at every new block (besu mode)
ValidatorSelectionMode *string `json:"validatorselectionmode,omitempty"` // Select model for validators
Validators []common.Address `json:"validators"` // Validators list
Expand Down Expand Up @@ -469,7 +468,6 @@ type Transition struct {
TransactionSizeLimit uint64 `json:"transactionSizeLimit,omitempty"` // Modify TransactionSizeLimit
BlockReward *math.HexOrDecimal256 `json:"blockReward,omitempty"` // validation rewards
BeneficiaryMode *string `json:"beneficiaryMode,omitempty"` // Mode for setting the beneficiary, either: list, besu, validators (beneficiary list is the list of validators)
BeneficiaryList []common.Address `json:"beneficiaryList,omitempty"` // List of wallet addresses that have benefit at every new block (list mode)
MiningBeneficiary *common.Address `json:"miningBeneficiary,omitempty"` // Wallet address that benefits at every new block (besu mode)
}

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

func (c *ChainConfig) GetRewardAccount(num *big.Int, coinbase common.Address) (common.Address, error) {
beneficiaryMode := "validator"
miningBeneficiary := common.Address{}

if c.QBFT != nil && c.QBFT.MiningBeneficiary != nil {
miningBeneficiary = *c.QBFT.MiningBeneficiary
beneficiaryMode = "fixed"
}

if c.QBFT != nil && c.QBFT.BeneficiaryMode != nil {
beneficiaryMode = *c.QBFT.BeneficiaryMode
}

c.GetTransitionValue(num, func(transition Transition) {
if transition.BeneficiaryMode != nil && (*transition.BeneficiaryMode == "validators" || *transition.BeneficiaryMode == "validator") {
beneficiaryMode = "validator"
}
if transition.MiningBeneficiary != nil && (transition.BeneficiaryMode == nil || *transition.BeneficiaryMode == "fixed") {
miningBeneficiary = *transition.MiningBeneficiary
beneficiaryMode = "fixed"
}
})

switch strings.ToLower(beneficiaryMode) {
case "fixed":
log.Trace("fixed beneficiary mode", "miningBeneficiary", miningBeneficiary)
return miningBeneficiary, nil
case "validator":
log.Trace("validator beneficiary mode", "coinbase", coinbase)
return coinbase, nil
}

return common.Address{}, errors.New("BeneficiaryMode must be coinbase|fixed")
}

func (c *ChainConfig) GetBlockReward(num *big.Int) big.Int {
blockReward := *math.NewHexOrDecimal256(0)

if c.QBFT != nil && c.QBFT.BlockReward != nil {
blockReward = *c.QBFT.BlockReward
}

c.GetTransitionValue(num, func(transition Transition) {
if transition.BlockReward != nil {
blockReward = *transition.BlockReward
}
})

return big.Int(blockReward)
}

// Quorum
// gets value at or after a transition
func (c *ChainConfig) GetTransitionValue(num *big.Int, callback func(transition Transition)) {
Expand Down
10 changes: 5 additions & 5 deletions params/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,10 +332,10 @@ func TestCheckTransitionsData(t *testing.T) {
var ibftTransitionsConfig, qbftTransitionsConfig, invalidTransition, invalidBlockOrder []Transition
var emptyBlockPeriodSeconds uint64 = 10

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}
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}
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}
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}
tranI0 := Transition{big.NewInt(0), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}
tranQ5 := Transition{big.NewInt(5), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}
tranI10 := Transition{big.NewInt(10), IBFT, 30000, 5, nil, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}
tranQ8 := Transition{big.NewInt(8), QBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}

ibftTransitionsConfig = append(ibftTransitionsConfig, tranI0, tranI10)
qbftTransitionsConfig = append(qbftTransitionsConfig, tranQ5, tranQ8)
Expand Down Expand Up @@ -395,7 +395,7 @@ func TestCheckTransitionsData(t *testing.T) {
wantErr: ErrBlockOrder,
},
{
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}}},
stored: &ChainConfig{Transitions: []Transition{{nil, IBFT, 30000, 5, &emptyBlockPeriodSeconds, 10, 50, common.Address{}, nil, "", nil, nil, nil, nil, 0, nil, 0, nil, nil, nil}}},
wantErr: ErrBlockNumberMissing,
},
{
Expand Down