Skip to content

Commit

Permalink
MinerPreference feature added to allow switching between Qi/Quai
Browse files Browse the repository at this point in the history
  • Loading branch information
Djadih committed Oct 10, 2024
1 parent c486917 commit ad116da
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 35 deletions.
7 changes: 7 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ var NodeFlags = []Flag{
PprofFlag,
InsecureUnlockAllowedFlag,
CoinbaseAddressFlag,
MinerPreferenceFlag,
EnvironmentFlag,
QuaiStatsURLFlag,
SendFullStatsFlag,
Expand Down Expand Up @@ -510,6 +511,12 @@ var (
Usage: "Input TOML string or path to TOML file" + generateEnvDoc(c_NodeFlagPrefix+"coinbases"),
}

MinerPreferenceFlag = Flag{
Name: c_NodeFlagPrefix + "miner-preference",
Value: 0.5,
Usage: "Indicates preference towards mining Quai or Qi (Neutral: 0.5, Qi only: 0, Quai only: 1)",
}

IndexAddressUtxos = Flag{
Name: c_NodeFlagPrefix + "index-address-utxos",
Value: false,
Expand Down
24 changes: 9 additions & 15 deletions core/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,19 @@ import (

// Miner creates blocks and searches for proof-of-work values.
type Miner struct {
worker *worker
primaryCoinbase common.Address
secondaryCoinbase common.Address
hc *HeaderChain
engine consensus.Engine
startCh chan []common.Address
logger *log.Logger
worker *worker
hc *HeaderChain
engine consensus.Engine
startCh chan []common.Address
logger *log.Logger
}

func New(hc *HeaderChain, txPool *TxPool, config *Config, db ethdb.Database, chainConfig *params.ChainConfig, engine consensus.Engine, isLocalBlock func(block *types.WorkObject) bool, processingState bool, logger *log.Logger) *Miner {
miner := &Miner{
hc: hc,
engine: engine,
startCh: make(chan []common.Address, 2),
worker: newWorker(config, chainConfig, db, engine, hc, txPool, isLocalBlock, true, processingState, logger),
primaryCoinbase: config.PrimaryCoinbase,
secondaryCoinbase: config.SecondaryCoinbase,
hc: hc,
engine: engine,
startCh: make(chan []common.Address, 2),
worker: newWorker(config, chainConfig, db, engine, hc, txPool, isLocalBlock, true, processingState, logger),
}

miner.SetExtra(miner.MakeExtraData(config.ExtraData))
Expand Down Expand Up @@ -131,12 +127,10 @@ func (miner *Miner) PendingBlockAndReceipts() (*types.WorkObject, types.Receipts
}

func (miner *Miner) SetPrimaryCoinbase(addr common.Address) {
miner.primaryCoinbase = addr
miner.worker.setPrimaryCoinbase(addr)
}

func (miner *Miner) SetSecondaryCoinbase(addr common.Address) {
miner.secondaryCoinbase = addr
miner.worker.setSecondaryCoinbase(addr)
}

Expand Down
4 changes: 2 additions & 2 deletions core/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func (sl *Slice) Append(header *types.WorkObject, domTerminus common.Hash, domOr
}
time5_1 = common.PrettyDuration(time.Since(start))
// Cache the subordinate's pending ETXs
pEtxs := types.PendingEtxs{header.ConvertToPEtxView(), subPendingEtxs}
pEtxs := types.PendingEtxs{Header: header.ConvertToPEtxView(), OutboundEtxs: subPendingEtxs}
time5_2 = common.PrettyDuration(time.Since(start))
// Add the pending etx given by the sub in the rollup
sl.AddPendingEtxs(pEtxs)
Expand All @@ -330,7 +330,7 @@ func (sl *Slice) Append(header *types.WorkObject, domTerminus common.Hash, domOr
}
}
// We also need to store the pendingEtxRollup to the dom
pEtxRollup := types.PendingEtxsRollup{header.ConvertToPEtxView(), crossPrimeRollup}
pEtxRollup := types.PendingEtxsRollup{Header: header.ConvertToPEtxView(), EtxsRollup: crossPrimeRollup}
sl.AddPendingEtxsRollup(pEtxRollup)
}
time5_3 = common.PrettyDuration(time.Since(start))
Expand Down
5 changes: 5 additions & 0 deletions core/types/wo.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,11 @@ func (wo *WorkObject) SetNumber(val *big.Int, nodeCtx int) {
}
}

func (wo *WorkObject) SetCoinbases(primary common.Address, secondary common.Address) {
wo.WorkObjectHeader().SetPrimaryCoinbase(primary)
wo.Header().SetSecondaryCoinbase(secondary)
}

////////////////////////////////////////////////////////////
/////////////////// Work Object Header Getters ///////////////
////////////////////////////////////////////////////////////
Expand Down
67 changes: 49 additions & 18 deletions core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"math"
"math/big"
"math/rand"
"runtime/debug"
"sort"
"strings"
Expand Down Expand Up @@ -102,6 +103,7 @@ func (env *environment) unclelist() []*types.WorkObjectHeader {
type Config struct {
PrimaryCoinbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account)
SecondaryCoinbase common.Address `toml:",omitempty"` // Public address for block mining rewards (second account)
MinerPreference float64 // Determines the relative preference of Qi or Quai [0, 1]
Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash).
NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages
ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner
Expand Down Expand Up @@ -271,6 +273,21 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, db ethdb.Databas
return worker
}

func (w *worker) setCoinbases(primaryAddress common.Address, secondaryAddress common.Address) {
w.mu.Lock()
defer w.mu.Unlock()

// Use the MinerPreference to bias the decision
biasedFloat := math.Pow(rand.Float64(), (1-w.config.MinerPreference)*2)
if biasedFloat < 0.5 {
w.primaryCoinbase = primaryAddress
w.secondaryCoinbase = secondaryAddress
} else {
w.primaryCoinbase = primaryAddress
w.secondaryCoinbase = secondaryAddress
}
}

// setPrimaryCoinbase sets the coinbase used to initialize the block primary coinbase field.
func (w *worker) setPrimaryCoinbase(addr common.Address) {
w.mu.Lock()
Expand All @@ -285,6 +302,20 @@ func (w *worker) setSecondaryCoinbase(addr common.Address) {
w.secondaryCoinbase = addr
}

func (w *worker) GetPrimaryCoinbase() common.Address {
w.mu.RLock()
defer w.mu.RUnlock()

return w.primaryCoinbase
}

func (w *worker) GetSecondaryCoinbase() common.Address {
w.mu.RLock()
defer w.mu.RUnlock()

return w.secondaryCoinbase
}

func (w *worker) setGasCeil(ceil uint64) {
w.mu.Lock()
defer w.mu.Unlock()
Expand Down Expand Up @@ -484,30 +515,27 @@ func (w *worker) GeneratePendingHeader(block *types.WorkObject, fill bool, txs t
)

start := time.Now()
w.setCoinbases(w.GetPrimaryCoinbase(), w.GetSecondaryCoinbase())
// Set the primary coinbase if the worker is running or it's required
var primaryCoinbase common.Address
if w.hc.NodeCtx() == common.ZONE_CTX && w.primaryCoinbase.Equal(common.Address{}) && w.hc.ProcessingState() {
if w.hc.NodeCtx() == common.ZONE_CTX && w.GetPrimaryCoinbase().Equal(common.Address{}) && w.hc.ProcessingState() {
w.logger.Warn("Refusing to mine without primaryCoinbase")
return nil, errors.New("etherbase not found")
} else if w.primaryCoinbase.Equal(common.Address{}) {
w.primaryCoinbase = common.Zero
} else if w.GetPrimaryCoinbase().Equal(common.Address{}) {
w.setPrimaryCoinbase(common.Zero)
}
primaryCoinbase = w.primaryCoinbase // Use the preset address as the fee recipient

// Set the secondary coinbase if the worker is running or it's required
var secondaryCoinbase common.Address
if w.hc.NodeCtx() == common.ZONE_CTX && w.primaryCoinbase.Equal(common.Address{}) && w.hc.ProcessingState() {
if w.hc.NodeCtx() == common.ZONE_CTX && w.GetPrimaryCoinbase().Equal(common.Address{}) && w.hc.ProcessingState() {
w.logger.Warn("Refusing to mine without primaryCoinbase")
return nil, errors.New("etherbase not found")
} else if w.primaryCoinbase.Equal(common.Address{}) {
w.primaryCoinbase = common.Zero
} else if w.GetPrimaryCoinbase().Equal(common.Address{}) {
w.setPrimaryCoinbase(common.Zero)
}
secondaryCoinbase = w.secondaryCoinbase // Use the preset address as the fee recipient

work, err := w.prepareWork(&generateParams{
timestamp: uint64(timestamp),
primaryCoinbase: primaryCoinbase,
secondaryCoinbase: secondaryCoinbase,
primaryCoinbase: w.GetPrimaryCoinbase(),
secondaryCoinbase: w.GetSecondaryCoinbase(),
}, block)
if err != nil {
return nil, err
Expand Down Expand Up @@ -577,18 +605,22 @@ func (w *worker) GeneratePendingHeader(block *types.WorkObject, fill bool, txs t
if bytes.Equal(work.wo.PrimaryCoinbase().Bytes(), quaiCoinbase.Bytes()) {
coinbaseReward := misc.CalculateReward(work.wo.WorkObjectHeader())
blockReward := new(big.Int).Add(coinbaseReward, work.quaiFees)
primaryCoinbase := w.GetPrimaryCoinbase()
coinbaseEtx := types.NewTx(&types.ExternalTx{To: &primaryCoinbase, Gas: params.TxGas, Value: blockReward, EtxType: types.CoinbaseType, OriginatingTxHash: origin, ETXIndex: uint16(len(work.etxs)), Sender: primaryCoinbase, Data: []byte{defaultCoinbaseLockup}})
work.etxs = append(work.etxs, coinbaseEtx)
if work.utxoFees.Cmp(big.NewInt(0)) != 0 {
coinbaseEtx := types.NewTx(&types.ExternalTx{To: &secondaryCoinbase, Gas: params.TxGas, Value: work.utxoFees, EtxType: types.CoinbaseType, OriginatingTxHash: origin, ETXIndex: uint16(len(work.etxs)), Sender: secondaryCoinbase, Data: []byte{defaultCoinbaseLockup}})
secondaryCoinbase := w.GetSecondaryCoinbase()
coinbaseEtx := types.NewTx(&types.ExternalTx{To: &secondaryCoinbase, Gas: params.TxGas, Value: work.utxoFees, EtxType: types.CoinbaseType, OriginatingTxHash: origin, ETXIndex: uint16(len(work.etxs)), Sender: w.secondaryCoinbase, Data: []byte{defaultCoinbaseLockup}})
work.etxs = append(work.etxs, coinbaseEtx)
}
} else if bytes.Equal(work.wo.PrimaryCoinbase().Bytes(), qiCoinbase.Bytes()) {
coinbaseReward := misc.CalculateReward(work.wo.WorkObjectHeader())
blockReward := new(big.Int).Add(coinbaseReward, work.utxoFees)
primaryCoinbase := w.GetPrimaryCoinbase()
coinbaseEtx := types.NewTx(&types.ExternalTx{To: &primaryCoinbase, Gas: params.TxGas, Value: blockReward, EtxType: types.CoinbaseType, OriginatingTxHash: origin, ETXIndex: uint16(len(work.etxs)), Sender: primaryCoinbase, Data: []byte{defaultCoinbaseLockup}})
work.etxs = append(work.etxs, coinbaseEtx)
if work.quaiFees.Cmp(big.NewInt(0)) != 0 {
secondaryCoinbase := w.GetSecondaryCoinbase()
coinbaseEtx := types.NewTx(&types.ExternalTx{To: &secondaryCoinbase, Gas: params.TxGas, Value: work.quaiFees, EtxType: types.CoinbaseType, OriginatingTxHash: origin, ETXIndex: uint16(len(work.etxs)), Sender: secondaryCoinbase, Data: []byte{defaultCoinbaseLockup}})
work.etxs = append(work.etxs, coinbaseEtx)
}
Expand Down Expand Up @@ -1533,16 +1565,15 @@ func (w *worker) prepareWork(genParams *generateParams, wo *types.WorkObject) (*
newWo.Header().SetExtra(w.extra)
newWo.Header().SetStateLimit(misc.CalcStateLimit(parent, w.config.GasCeil))
if w.isRunning() {
if w.primaryCoinbase.Equal(common.Zero) {
if w.GetPrimaryCoinbase().Equal(common.Zero) {
w.logger.Error("Refusing to mine without primary coinbase")
return nil, errors.New("refusing to mine without primary coinbase")
}
if w.secondaryCoinbase.Equal(common.Zero) {
if w.GetSecondaryCoinbase().Equal(common.Zero) {
w.logger.Error("Refusing to mine without secondary coinbase")
return nil, errors.New("refusing to mine without secondary coinbase")
}
newWo.WorkObjectHeader().SetPrimaryCoinbase(w.primaryCoinbase)
newWo.Header().SetSecondaryCoinbase(w.secondaryCoinbase)
newWo.SetCoinbases(w.GetPrimaryCoinbase(), w.GetSecondaryCoinbase())
}

// Get the latest transactions to be broadcasted from the pool
Expand All @@ -1568,7 +1599,7 @@ func (w *worker) prepareWork(genParams *generateParams, wo *types.WorkObject) (*
proposedWoHeader := types.NewWorkObjectHeader(newWo.Hash(), newWo.ParentHash(nodeCtx), newWo.Number(nodeCtx), newWo.Difficulty(), newWo.WorkObjectHeader().PrimeTerminusNumber(), newWo.TxHash(), newWo.Nonce(), newWo.Lock(), newWo.Time(), newWo.Location(), newWo.PrimaryCoinbase())
proposedWoBody := types.NewWoBody(newWo.Header(), nil, nil, nil, nil, nil)
proposedWo := types.NewWorkObject(proposedWoHeader, proposedWoBody, nil)
env, err := w.makeEnv(parent, proposedWo, w.primaryCoinbase, w.secondaryCoinbase)
env, err := w.makeEnv(parent, proposedWo, w.GetPrimaryCoinbase(), w.GetSecondaryCoinbase())
if err != nil {
w.logger.WithField("err", err).Error("Failed to create sealing context")
return nil, err
Expand Down

0 comments on commit ad116da

Please sign in to comment.