From 6feea909464cafe007b32af895376b0a063e2233 Mon Sep 17 00:00:00 2001 From: Evgeny Khirin <32414982+evgeny-khirin@users.noreply.github.com> Date: Mon, 7 Jan 2019 18:07:49 +0200 Subject: [PATCH] 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 --- mining/mining.go | 117 ++++++++++------------------------------------- 1 file changed, 23 insertions(+), 94 deletions(-) diff --git a/mining/mining.go b/mining/mining.go index 22ea4a12b..8ff4e1172 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -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