Dev 312 update miner logic to fit new rules (#150)

* [DEV-312] Take in account subnetwork's GAS limit, when adding
transactions to block. Try to do that optimally.

* [DEV-312] Fixed GAS overusage calculation

* [DEV-312] Prepare to merge with master

* [DEV-312] Fix after merging
This commit is contained in:
Evgeny Khirin 2019-01-07 18:07:49 +02:00 committed by Ori Newman
parent d33d633e77
commit 6feea90946

View File

@ -80,12 +80,6 @@ type txPrioItem struct {
fee uint64
priority float64
feePerKB uint64
// dependsOn holds a map of transaction hashes which this one depends
// on. It will only be set when the transaction references other
// transactions in the source pool and hence must come after them in
// a block.
dependsOn map[daghash.Hash]struct{}
}
// txPriorityQueueLessFunc describes a function that can be used as a compare
@ -265,19 +259,6 @@ func createCoinbaseTx(params *dagconfig.Params, coinbaseScript []byte, nextBlock
return util.NewTx(tx), nil
}
// logSkippedDeps logs any dependencies which are also skipped as a result of
// skipping a transaction while generating a block template at the trace level.
func logSkippedDeps(tx *util.Tx, deps map[daghash.Hash]*txPrioItem) {
if deps == nil {
return
}
for _, item := range deps {
log.Tracef("Skipping tx %s since it depends on %s\n",
item.tx.Hash(), tx.Hash())
}
}
// MinimumMedianTime returns the minimum allowed timestamp for a block building
// on the end of the provided best chain. In particular, it is one second after
// the median timestamp of the last several blocks per the chain consensus
@ -443,13 +424,6 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
blockTxns = append(blockTxns, coinbaseTx)
blockUtxos := blockdag.NewDiffUTXOSet(g.dag.UTXOSet(), blockdag.NewUTXODiff())
// dependers is used to track transactions which depend on another
// transaction in the source pool. This, in conjunction with the
// dependsOn map kept with each dependent transaction helps quickly
// determine which dependent transactions are now eligible for inclusion
// in the block once each transaction has been included.
dependers := make(map[daghash.Hash]map[daghash.Hash]*txPrioItem)
// Create slices to hold the fees and number of signature operations
// for each of the selected transactions and add an entry for the
// coinbase. This allows the code below to simply append details about
@ -464,7 +438,6 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
log.Debugf("Considering %d transactions for inclusion to new block",
len(sourceTxns))
mempoolLoop:
for _, txDesc := range sourceTxns {
// A block can't have more than one coinbase or contain
// non-finalized transactions.
@ -480,63 +453,18 @@ mempoolLoop:
continue
}
// Setup dependencies for any transactions which reference
// other transactions in the mempool so they can be properly
// ordered below.
prioItem := &txPrioItem{tx: tx}
for _, txIn := range tx.MsgTx().TxIn {
originHash := &txIn.PreviousOutPoint.Hash
_, ok := blockUtxos.Get(txIn.PreviousOutPoint)
if !ok {
if !g.txSource.HaveTransaction(originHash) {
log.Tracef("Skipping tx %s because it "+
"references unspent output %s "+
"which is not available",
tx.Hash(), txIn.PreviousOutPoint)
continue mempoolLoop
}
// The transaction is referencing another
// transaction in the source pool, so setup an
// ordering dependency.
deps, exists := dependers[*originHash]
if !exists {
deps = make(map[daghash.Hash]*txPrioItem)
dependers[*originHash] = deps
}
deps[*prioItem.tx.Hash()] = prioItem
if prioItem.dependsOn == nil {
prioItem.dependsOn = make(
map[daghash.Hash]struct{})
}
prioItem.dependsOn[*originHash] = struct{}{}
// Skip the check below. We already know the
// referenced transaction is available.
continue
}
}
// Calculate the final transaction priority using the input
// value age sum as well as the adjusted transaction size. The
// formula is: sum(inputValue * inputAge) / adjustedTxSize
prioItem := &txPrioItem{tx: tx}
prioItem.priority = CalcPriority(tx.MsgTx(), blockUtxos,
nextBlockHeight)
// Calculate the fee in Satoshi/kB.
prioItem.feePerKB = txDesc.FeePerKB
prioItem.fee = txDesc.Fee
// Add the transaction to the priority queue to mark it ready
// for inclusion in the block unless it has dependencies.
if prioItem.dependsOn == nil {
heap.Push(priorityQueue, prioItem)
}
}
log.Tracef("Priority queue len %d, dependers len %d",
priorityQueue.Len(), len(dependers))
// The starting block size is the size of the block header plus the max
// possible transaction count size, plus the size of the coinbase
// transaction.
@ -544,6 +472,9 @@ mempoolLoop:
blockSigOps := numCoinbaseSigOps
totalFees := uint64(0)
// Create map of GAS usage per subnetwork
gasUsageMap := make(map[uint64]uint64)
// Choose which transactions make it into the block.
for priorityQueue.Len() > 0 {
// Grab the highest priority (or highest fee per kilobyte
@ -551,8 +482,25 @@ mempoolLoop:
prioItem := heap.Pop(priorityQueue).(*txPrioItem)
tx := prioItem.tx
// Grab any transactions which depend on this one.
deps := dependers[*tx.Hash()]
if tx.MsgTx().SubNetworkID > wire.SubNetworkDAGCoin {
subnetwork := tx.MsgTx().SubNetworkID
gasUsage, ok := gasUsageMap[subnetwork]
if !ok {
gasUsage = 0
}
gasLimit, err := g.dag.GasLimit(subnetwork)
if err != nil {
log.Errorf("Cannot get GAS limit for subnetwork %v", subnetwork)
continue
}
txGas := tx.MsgTx().Gas
if gasLimit-gasUsage < txGas {
log.Tracef("Transaction %v (GAS=%v) ignored because gas overusage (GASUsage=%v) in subnetwork %v (GASLimit=%v)",
tx.MsgTx().TxHash, txGas, gasUsage, subnetwork, gasLimit)
continue
}
gasUsageMap[subnetwork] = gasUsage + txGas
}
// Enforce maximum block size. Also check for overflow.
txSize := uint32(tx.MsgTx().SerializeSize())
@ -562,7 +510,6 @@ mempoolLoop:
log.Tracef("Skipping tx %s because it would exceed "+
"the max block size", tx.Hash())
logSkippedDeps(tx, deps)
continue
}
@ -573,7 +520,6 @@ mempoolLoop:
blockSigOps+numSigOps > blockdag.MaxSigOpsPerBlock {
log.Tracef("Skipping tx %s because it would exceed "+
"the maximum sigops per block", tx.Hash())
logSkippedDeps(tx, deps)
continue
}
numP2SHSigOps, err := blockdag.CountP2SHSigOps(tx, false,
@ -581,7 +527,6 @@ mempoolLoop:
if err != nil {
log.Tracef("Skipping tx %s due to error in "+
"GetSigOpCost: %v", tx.Hash(), err)
logSkippedDeps(tx, deps)
continue
}
numSigOps += int64(numP2SHSigOps)
@ -589,7 +534,6 @@ mempoolLoop:
blockSigOps+numSigOps > blockdag.MaxSigOpsPerBlock {
log.Tracef("Skipping tx %s because it would "+
"exceed the maximum sigops per block", tx.Hash())
logSkippedDeps(tx, deps)
continue
}
@ -604,7 +548,6 @@ mempoolLoop:
"minBlockSize %d", tx.Hash(), prioItem.feePerKB,
g.policy.TxMinFreeFee, blockPlusTxSize,
g.policy.BlockMinSize)
logSkippedDeps(tx, deps)
continue
}
@ -644,7 +587,6 @@ mempoolLoop:
if err != nil {
log.Tracef("Skipping tx %s due to error in "+
"CheckTransactionInputs: %v", tx.Hash(), err)
logSkippedDeps(tx, deps)
continue
}
err = blockdag.ValidateTransactionScripts(tx, blockUtxos,
@ -652,7 +594,6 @@ mempoolLoop:
if err != nil {
log.Tracef("Skipping tx %s due to error in "+
"ValidateTransactionScripts: %v", tx.Hash(), err)
logSkippedDeps(tx, deps)
continue
}
@ -674,18 +615,6 @@ mempoolLoop:
log.Tracef("Adding tx %s (priority %.2f, feePerKB %.2f)",
prioItem.tx.Hash(), prioItem.priority, prioItem.feePerKB)
// Add transactions which depend on this one (and also do not
// have any other unsatisified dependencies) to the priority
// queue.
for _, item := range deps {
// Add the transaction to the priority queue if there
// are no more dependencies after this one.
delete(item.dependsOn, *tx.Hash())
if len(item.dependsOn) == 0 {
heap.Push(priorityQueue, item)
}
}
}
// Now that the actual transactions have been selected, update the