github.com/deso-protocol/core@v1.2.9/lib/block_producer.go (about)

     1  package lib
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"github.com/btcsuite/btcd/wire"
     7  	"github.com/tyler-smith/go-bip39"
     8  	"math"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/davecgh/go-spew/spew"
    13  	"github.com/deso-protocol/go-deadlock"
    14  
    15  	"github.com/btcsuite/btcd/btcec"
    16  	"github.com/golang/glog"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  type DeSoBlockProducer struct {
    21  	// The minimum amount of time we wait before trying to produce a new block
    22  	// template. If this value is set low enough then we will produce a block template
    23  	// continuously.
    24  	minBlockUpdateIntervalSeconds uint64
    25  	// The number of templates to cache so that we can accept headers for blocks
    26  	// that are a bit stale.
    27  	maxBlockTemplatesToCache uint64
    28  	// A private key that is used to sign blocks produced by this block producer. Only
    29  	// set if a blockProducerSeed is provided when constructing the BlockProducer.
    30  	blockProducerPrivateKey *btcec.PrivateKey
    31  	// A lock on the block templates produced to avoid concurrency issues.
    32  	mtxRecentBlockTemplatesProduced deadlock.RWMutex
    33  	// The most recent N blocks that we've produced indexed by their hash.
    34  	// Keeping this list allows us to accept a valid header from a miner without
    35  	// requiring them to download/send the whole block.
    36  	recentBlockTemplatesProduced map[BlockHash]*MsgDeSoBlock
    37  	latestBlockTemplateHash      *BlockHash
    38  	currentDifficultyTarget      *BlockHash
    39  
    40  	latestBlockTemplateStats *BlockTemplateStats
    41  
    42  	mempool *DeSoMempool
    43  	chain   *Blockchain
    44  	params  *DeSoParams
    45  
    46  	producerWaitGroup   sync.WaitGroup
    47  	stopProducerChannel chan struct{}
    48  
    49  	postgres *Postgres
    50  }
    51  
    52  type BlockTemplateStats struct {
    53  	// The number of txns in the block template.
    54  	TxnCount uint32
    55  	// The final txn we attempted to put in the block.
    56  	FailingTxnHash string
    57  	// The reason why the final txn failed to add.
    58  	FailingTxnError string
    59  	// The "Added" time on a transaction changes every time a block is mined so we record
    60  	// the first time added val we are aware of for a specific txn hash here.
    61  	FailingTxnOriginalTimeAdded time.Time
    62  	// The time since the failing txn was added to the mempool.
    63  	FailingTxnMinutesSinceAdded float64
    64  }
    65  
    66  func NewDeSoBlockProducer(
    67  	minBlockUpdateIntervalSeconds uint64,
    68  	maxBlockTemplatesToCache uint64,
    69  	blockProducerSeed string,
    70  	mempool *DeSoMempool,
    71  	chain *Blockchain,
    72  	params *DeSoParams,
    73  	postgres *Postgres,
    74  ) (*DeSoBlockProducer, error) {
    75  
    76  	var privKey *btcec.PrivateKey
    77  	if blockProducerSeed != "" {
    78  		seedBytes, err := bip39.NewSeedWithErrorChecking(blockProducerSeed, "")
    79  		if err != nil {
    80  			return nil, fmt.Errorf("NewDeSoBlockProducer: Error converting mnemonic: %+v", err)
    81  		}
    82  
    83  		_, privKey, _, err = ComputeKeysFromSeed(seedBytes, 0, params)
    84  		if err != nil {
    85  			return nil, fmt.Errorf(
    86  				"NewDeSoBlockProducer: Error computing keys from seed: %+v", err)
    87  		}
    88  	}
    89  
    90  	return &DeSoBlockProducer{
    91  		minBlockUpdateIntervalSeconds: minBlockUpdateIntervalSeconds,
    92  		maxBlockTemplatesToCache:      maxBlockTemplatesToCache,
    93  		blockProducerPrivateKey:       privKey,
    94  		recentBlockTemplatesProduced:  make(map[BlockHash]*MsgDeSoBlock),
    95  
    96  		mempool:             mempool,
    97  		chain:               chain,
    98  		params:              params,
    99  		stopProducerChannel: make(chan struct{}),
   100  		postgres:            postgres,
   101  	}, nil
   102  }
   103  
   104  func (bbp *DeSoBlockProducer) GetLatestBlockTemplateStats() *BlockTemplateStats {
   105  	return bbp.latestBlockTemplateStats
   106  }
   107  
   108  func (desoBlockProducer *DeSoBlockProducer) _updateBlockTimestamp(blk *MsgDeSoBlock, lastNode *BlockNode) {
   109  	// Set the block's timestamp. If the timesource's time happens to be before
   110  	// the timestamp set in the last block then set the time based on the last
   111  	// block's timestamp instead. We do this because consensus rules require a
   112  	// monotonically increasing timestamp.
   113  	blockTstamp := uint32(desoBlockProducer.chain.timeSource.AdjustedTime().Unix())
   114  	if blockTstamp <= uint32(lastNode.Header.TstampSecs) {
   115  		blockTstamp = uint32(lastNode.Header.TstampSecs) + 1
   116  	}
   117  	blk.Header.TstampSecs = uint64(blockTstamp)
   118  }
   119  
   120  func (desoBlockProducer *DeSoBlockProducer) _getBlockTemplate(publicKey []byte) (
   121  	_blk *MsgDeSoBlock, _diffTarget *BlockHash, _lastNode *BlockNode, _err error) {
   122  
   123  	// Get the current tip of the best block chain. Note that using the tip of the
   124  	// best block chain as opposed to the best header chain means we'll be mining
   125  	// stale blocks until we're fully synced. This isn't ideal, but is currently
   126  	// preferred to mining atop the best header chain because the latter currently results
   127  	// in the blocks being rejected as unconnectedTxns before the block tip is in-sync.
   128  	lastNode := desoBlockProducer.chain.blockTip()
   129  
   130  	// Compute the public key to contribute the reward to.
   131  	rewardPk, err := btcec.ParsePubKey(publicKey, btcec.S256())
   132  	if err != nil {
   133  		return nil, nil, nil, errors.Wrapf(err, "DeSoBlockProducer._getBlockTemplate: ")
   134  	}
   135  
   136  	// Construct the next block.
   137  	blockRewardOutput := &DeSoOutput{}
   138  	if rewardPk != nil {
   139  		// This is to account for a really weird edge case where somebody stops the BlockProducer
   140  		// in the middle of us getting a block.
   141  		blockRewardOutput.PublicKey = rewardPk.SerializeCompressed()
   142  	}
   143  	// Set the block reward output initially to the maximum value for a uint64.
   144  	// This ensures it will take the maximum amount of space in the block when
   145  	// encoded as a varint so our size estimates won't get messed up.
   146  	blockRewardOutput.AmountNanos = math.MaxUint64
   147  
   148  	// Block reward txn only needs a single output. No need to specify spending
   149  	// pk or sigs.
   150  	blockRewardTxn := NewMessage(MsgTypeTxn).(*MsgDeSoTxn)
   151  	blockRewardTxn.TxOutputs = append(blockRewardTxn.TxOutputs, blockRewardOutput)
   152  	// Set the ExtraData to zero. This gives miners something they can
   153  	// twiddle if they run out of space on their actual nonce.
   154  	blockRewardTxn.TxnMeta = &BlockRewardMetadataa{
   155  		ExtraData: UintToBuf(0),
   156  	}
   157  
   158  	// Create the block and add the BlockReward txn to it.
   159  	blockRet := NewMessage(MsgTypeBlock).(*MsgDeSoBlock)
   160  	blockRet.Txns = append(blockRet.Txns, blockRewardTxn)
   161  	// The version may be swapped out in a call to GetBlockTemplate in order to remain
   162  	// backwards-compatible with existing miners that use an older version.
   163  	blockRet.Header.Version = CurrentHeaderVersion
   164  	blockRet.Header.Height = uint64(lastNode.Height + 1)
   165  	blockRet.Header.PrevBlockHash = lastNode.Hash
   166  	desoBlockProducer._updateBlockTimestamp(blockRet, lastNode)
   167  	// Start the nonce at zero. This is OK because we'll set a random ExtraData for the
   168  	// miner later.
   169  	blockRet.Header.Nonce = 0
   170  
   171  	// Only add transactions to the block if our chain is done syncing.
   172  	if desoBlockProducer.chain.chainState() != SyncStateSyncingHeaders &&
   173  		desoBlockProducer.chain.chainState() != SyncStateNeedBlocksss {
   174  
   175  		// Fetch a bunch of mempool transactions to add.
   176  		txnsOrderedByTimeAdded, _, err := desoBlockProducer.mempool.GetTransactionsOrderedByTimeAdded()
   177  		if err != nil {
   178  			return nil, nil, nil, errors.Wrapf(err, "DeSoBlockProducer._getBlockTemplate: Problem getting mempool transactions: ")
   179  		}
   180  
   181  		// Now keep
   182  		// adding transactions to the block until the block is full.
   183  		//
   184  		// Compute the size of the header and then add the number of bytes used to encode
   185  		// the number of transactions in the block. Note that headers have a fixed size.
   186  		//
   187  		// TODO: The code below is lazily-written and could be optimized to squeeze a few
   188  		// more bytes into each block.
   189  		//
   190  		// Track the total size of the block as we go. Since the number of transactions
   191  		// encoded in the block can become larger as we add transactions to it, add the
   192  		// maximum size for this field to the current size to ensure we don't overfill
   193  		// the block.
   194  		blockBytes, err := blockRet.ToBytes(false)
   195  		if err != nil {
   196  			return nil, nil, nil, errors.Wrapf(err, "DeSoBlockProducer._getBlockTemplate: Problem serializing block: ")
   197  		}
   198  		currentBlockSize := uint64(len(blockBytes) + MaxVarintLen64)
   199  
   200  		// Create a new view object.
   201  		utxoView, err := NewUtxoView(desoBlockProducer.chain.db, desoBlockProducer.params, desoBlockProducer.postgres)
   202  		if err != nil {
   203  			return nil, nil, nil, errors.Wrapf(err,
   204  				"DeSoBlockProducer._getBlockTemplate: Error generating checker UtxoView: ")
   205  		}
   206  
   207  		txnsAddedToBlock := make(map[BlockHash]bool)
   208  		for ii, mempoolTx := range txnsOrderedByTimeAdded {
   209  			// If we hit a transaction that's too big to fit into a block then we're done.
   210  			if mempoolTx.TxSizeBytes+currentBlockSize > desoBlockProducer.params.MinerMaxBlockSizeBytes {
   211  				break
   212  			}
   213  
   214  			// Try to apply the transaction to the view with the strictest possible checks.
   215  			_, _, _, _, err := utxoView._connectTransaction(
   216  				mempoolTx.Tx, mempoolTx.Hash, int64(mempoolTx.TxSizeBytes), uint32(blockRet.Header.Height), true,
   217  				false /*ignoreUtxos*/)
   218  			if err != nil {
   219  				// If we fail to apply this transaction then we're done. Don't mine any of the
   220  				// other transactions since they could be dependent on this one.
   221  				txnErrorString := fmt.Sprintf(
   222  					"DeSoBlockProducer._getBlockTemplate: Stopping at txn %v because it's not ready yet: %v", ii, err)
   223  				glog.Infof(txnErrorString)
   224  				if mempoolTx.Tx.TxnMeta.GetTxnType() == TxnTypeBitcoinExchange {
   225  					// Print the Bitcoin block hash when we break out due to this.
   226  					btcErrorString := fmt.Sprintf("A bad BitcoinExchange transaction may be holding "+
   227  						"up block production: %v",
   228  						mempoolTx.Tx.TxnMeta.(*BitcoinExchangeMetadata).BitcoinTransaction.TxHash())
   229  					glog.Infof(btcErrorString)
   230  					txnErrorString += (" " + btcErrorString)
   231  					scs := spew.ConfigState{DisableMethods: true, Indent: "  "}
   232  					glog.V(1).Infof("Spewing Bitcoin txn: %v", scs.Sdump(mempoolTx.Tx))
   233  				}
   234  
   235  				// Update the block template stats for the admin dashboard.
   236  				failingTxnHash := mempoolTx.Hash.String()
   237  				failingTxnOriginalTimeAdded := mempoolTx.Added
   238  				if desoBlockProducer.latestBlockTemplateStats != nil &&
   239  					desoBlockProducer.latestBlockTemplateStats.FailingTxnHash == failingTxnHash {
   240  					// If we already have the txn stored, update the error message in case it changed
   241  					// and set the originalTimeAdded variable to compute an accurate staleness metric.
   242  					desoBlockProducer.latestBlockTemplateStats.FailingTxnError = txnErrorString
   243  					failingTxnOriginalTimeAdded = desoBlockProducer.latestBlockTemplateStats.FailingTxnOriginalTimeAdded
   244  				} else {
   245  					// If we haven't seen this txn before, build the block template stats from scratch.
   246  					blockTemplateStats := &BlockTemplateStats{}
   247  					blockTemplateStats.FailingTxnHash = mempoolTx.Hash.String()
   248  					blockTemplateStats.TxnCount = uint32(ii)
   249  					blockTemplateStats.FailingTxnError = txnErrorString
   250  					blockTemplateStats.FailingTxnOriginalTimeAdded = failingTxnOriginalTimeAdded
   251  					desoBlockProducer.latestBlockTemplateStats = blockTemplateStats
   252  				}
   253  				// Compute the time since this txn started holding up the mempool.
   254  				currentTime := time.Now()
   255  				timeElapsed := currentTime.Sub(failingTxnOriginalTimeAdded)
   256  				desoBlockProducer.latestBlockTemplateStats.FailingTxnMinutesSinceAdded = timeElapsed.Minutes()
   257  
   258  				break
   259  			} else if desoBlockProducer.latestBlockTemplateStats != nil {
   260  				desoBlockProducer.latestBlockTemplateStats.FailingTxnError = "You good"
   261  				desoBlockProducer.latestBlockTemplateStats.FailingTxnHash = "Nada"
   262  				desoBlockProducer.latestBlockTemplateStats.FailingTxnMinutesSinceAdded = 0
   263  				desoBlockProducer.latestBlockTemplateStats.FailingTxnOriginalTimeAdded = time.Now()
   264  			}
   265  
   266  			// If we get here then it means the txn is ready to be processed *and* we've added
   267  			// all of its dependencies to the block already. So go ahead and it to the block.
   268  			currentBlockSize += mempoolTx.TxSizeBytes + MaxVarintLen64
   269  			blockRet.Txns = append(blockRet.Txns, mempoolTx.Tx)
   270  			txnsAddedToBlock[*mempoolTx.Hash] = true
   271  		}
   272  
   273  		// Double-check that the final block size is below the limit.
   274  		blockBytes, err = blockRet.ToBytes(false)
   275  		if err != nil {
   276  			return nil, nil, nil, errors.Wrapf(err, "DeSoBlockProducer._getBlockTemplate: Problem serializing block after txns added: ")
   277  		}
   278  		if uint64(len(blockBytes)) > desoBlockProducer.params.MinerMaxBlockSizeBytes {
   279  			return nil, nil, nil, fmt.Errorf("DeSoBlockProducer._getBlockTemplate: Block created with size "+
   280  				"(%d) exceeds BlockProducerMaxBlockSizeBytes (%d): ", len(blockBytes), desoBlockProducer.params.MinerMaxBlockSizeBytes)
   281  		}
   282  	}
   283  
   284  	// Compute the total fee the BlockProducer should get.
   285  	totalFeeNanos := uint64(0)
   286  	feesUtxoView, err := NewUtxoView(desoBlockProducer.chain.db, desoBlockProducer.params, desoBlockProducer.postgres)
   287  	if err != nil {
   288  		return nil, nil, nil, fmt.Errorf(
   289  			"DeSoBlockProducer._getBlockTemplate: Error generating UtxoView to compute txn fees: %v", err)
   290  	}
   291  	// Skip the block reward, which is the first txn in the block.
   292  	for _, txnInBlock := range blockRet.Txns[1:] {
   293  		var feeNanos uint64
   294  		_, _, _, feeNanos, err = feesUtxoView._connectTransaction(
   295  			txnInBlock, txnInBlock.Hash(), 0, uint32(blockRet.Header.Height), false, /*verifySignatures*/
   296  			false /*ignoreUtxos*/)
   297  		if err != nil {
   298  			return nil, nil, nil, fmt.Errorf(
   299  				"DeSoBlockProducer._getBlockTemplate: Error attaching txn to UtxoView for computed block: %v", err)
   300  		}
   301  
   302  		// Add the fee to the block reward output as we go. Note this has some risk of
   303  		// increasing the size of the block by one byte, but it seems like this is an
   304  		// extreme edge case that goes away as soon as the function is called again.
   305  		totalFeeNanos += feeNanos
   306  	}
   307  
   308  	// Now that the total fees have been computed, set the value of the block reward
   309  	// output.
   310  	blockRewardOutput.AmountNanos = CalcBlockRewardNanos(uint32(blockRet.Header.Height)) + totalFeeNanos
   311  
   312  	// Compute the merkle root for the block now that all of the transactions have
   313  	// been added.
   314  	merkleRoot, _, err := ComputeMerkleRoot(blockRet.Txns)
   315  	if err != nil {
   316  		return nil, nil, nil, errors.Wrapf(err, "DeSoBlockProducer._getBlockTemplate: Problem computing merkle root: ")
   317  	}
   318  	blockRet.Header.TransactionMerkleRoot = merkleRoot
   319  
   320  	// Compute the next difficulty target given the current tip.
   321  	diffTarget, err := CalcNextDifficultyTarget(
   322  		lastNode, CurrentHeaderVersion, desoBlockProducer.params)
   323  	if err != nil {
   324  		return nil, nil, nil, errors.Wrapf(err, "DeSoBlockProducer._getBlockTemplate: Problem computing next difficulty: ")
   325  	}
   326  
   327  	glog.Infof("Produced block with %v txns with approx %v total txns in mempool",
   328  		len(blockRet.Txns), len(desoBlockProducer.mempool.readOnlyUniversalTransactionList))
   329  	return blockRet, diffTarget, lastNode, nil
   330  }
   331  
   332  func (desoBlockProducer *DeSoBlockProducer) Stop() {
   333  	desoBlockProducer.stopProducerChannel <- struct{}{}
   334  	desoBlockProducer.producerWaitGroup.Wait()
   335  }
   336  
   337  func (desoBlockProducer *DeSoBlockProducer) GetRecentBlock(blockHash *BlockHash) *MsgDeSoBlock {
   338  	// Find the block and quickly lock/unlock for reading.
   339  	desoBlockProducer.mtxRecentBlockTemplatesProduced.RLock()
   340  	defer desoBlockProducer.mtxRecentBlockTemplatesProduced.RUnlock()
   341  
   342  	blockFound, exists := desoBlockProducer.recentBlockTemplatesProduced[*blockHash]
   343  	if !exists {
   344  		return nil
   345  	}
   346  
   347  	return blockFound
   348  }
   349  
   350  func (desoBlockProducer *DeSoBlockProducer) GetCopyOfRecentBlock(blockID string) (*MsgDeSoBlock, error) {
   351  	blockHashBytes, err := hex.DecodeString(blockID)
   352  	if err != nil {
   353  		return nil, errors.Wrap(err, "")
   354  	}
   355  	if len(blockHashBytes) != HashSizeBytes {
   356  		return nil, fmt.Errorf("Invalid blockID. Length was %v but must "+
   357  			"be %v", len(blockHashBytes), HashSizeBytes)
   358  	}
   359  
   360  	blockHash := &BlockHash{}
   361  	copy(blockHash[:], blockHashBytes)
   362  
   363  	blockFound := desoBlockProducer.GetRecentBlock(blockHash)
   364  	if blockFound == nil {
   365  		return nil, fmt.Errorf("Block with blockID %v not found "+
   366  			"in BlockProducer", blockID)
   367  	}
   368  
   369  	blockFoundBytes, err := blockFound.ToBytes(false /*preSignature*/)
   370  	if err != nil {
   371  		return nil, fmt.Errorf("Error serializing block: %v", err)
   372  	}
   373  
   374  	newBlock := &MsgDeSoBlock{}
   375  	err = newBlock.FromBytes(blockFoundBytes)
   376  	if err != nil {
   377  		return nil, fmt.Errorf("Error de-serializing block: %v", err)
   378  	}
   379  
   380  	return newBlock, nil
   381  }
   382  
   383  func (desoBlockProducer *DeSoBlockProducer) AddBlockTemplate(block *MsgDeSoBlock, diffTarget *BlockHash) {
   384  	desoBlockProducer.mtxRecentBlockTemplatesProduced.Lock()
   385  	defer desoBlockProducer.mtxRecentBlockTemplatesProduced.Unlock()
   386  
   387  	hash, _ := block.Header.Hash()
   388  	desoBlockProducer.recentBlockTemplatesProduced[*hash] = block
   389  	desoBlockProducer.latestBlockTemplateHash = hash
   390  	desoBlockProducer.currentDifficultyTarget = diffTarget
   391  
   392  	// Evict entries if we're at capacity.
   393  	for uint64(len(desoBlockProducer.recentBlockTemplatesProduced)) >
   394  		desoBlockProducer.maxBlockTemplatesToCache {
   395  
   396  		// TODO: We could be evicting things out of order if they both happen at the same
   397  		// second. The fix is to use nanos rather than seconds but we're skipping the work
   398  		// to do this for now since it doesn't really matter.
   399  		minTstamp := uint32(math.MaxUint32)
   400  		var oldestBlockHash *BlockHash
   401  		for _, cachedBlock := range desoBlockProducer.recentBlockTemplatesProduced {
   402  			if uint32(cachedBlock.Header.TstampSecs) < minTstamp {
   403  				minTstamp = uint32(cachedBlock.Header.TstampSecs)
   404  				oldestBlockHash, _ = cachedBlock.Header.Hash()
   405  			}
   406  		}
   407  
   408  		delete(desoBlockProducer.recentBlockTemplatesProduced, *oldestBlockHash)
   409  	}
   410  }
   411  
   412  func (blockProducer *DeSoBlockProducer) GetHeadersAndExtraDatas(
   413  	publicKeyBytes []byte, numHeaders int64, headerVersion uint32) (
   414  	_blockID string, _headers [][]byte, _extraNonces []uint64, _diffTarget *BlockHash, _err error) {
   415  
   416  	// If we haven't computed the latest block template, then compute it now to bootstrap.
   417  	if blockProducer.latestBlockTemplateHash == nil {
   418  		// Use a dummy public key.
   419  		currentBlockTemplate, diffTarget, _, err :=
   420  			blockProducer._getBlockTemplate(MustBase58CheckDecode(ArchitectPubKeyBase58Check))
   421  		if err != nil {
   422  			return "", nil, nil, nil,
   423  				fmt.Errorf("GetBlockTemplate: Problem computing first block template: %v", err)
   424  		}
   425  
   426  		blockProducer.AddBlockTemplate(currentBlockTemplate, diffTarget)
   427  	}
   428  	// BlockProducer.latestBlockTemplateHash should always be set at this point.
   429  
   430  	// Get the latest block
   431  	blockID := hex.EncodeToString(blockProducer.latestBlockTemplateHash[:])
   432  	latestBLockCopy, err := blockProducer.GetCopyOfRecentBlock(blockID)
   433  	if err != nil {
   434  		return "", nil, nil, nil, errors.Wrap(
   435  			fmt.Errorf("GetBlockTemplate: Problem getting latest block: %v", err), "")
   436  	}
   437  
   438  	// Swap out the public key in the block
   439  	latestBLockCopy.Txns[0].TxOutputs[0].PublicKey = publicKeyBytes
   440  
   441  	headers := [][]byte{}
   442  	extraNonces := []uint64{}
   443  
   444  	// For each header the caller asked us for, compute an ExtraData nonce and a block header
   445  	// using that nonced block reward.
   446  	for ii := int64(0); ii < numHeaders; ii++ {
   447  		// Set the version of the header
   448  		latestBLockCopy.Header.Version = headerVersion
   449  
   450  		extraNonce, err := wire.RandomUint64()
   451  		if err != nil {
   452  			return "", nil, nil, nil, errors.Wrap(
   453  				fmt.Errorf("GetBlockTemplate: Error computing extraNonce: %v", err), "")
   454  		}
   455  		latestBLockCopy.Txns[0].TxnMeta.(*BlockRewardMetadataa).ExtraData = UintToBuf(extraNonce)
   456  
   457  		// Compute the merkle root for the block now that all of the transactions have
   458  		// been added.
   459  		merkleRoot, _, err := ComputeMerkleRoot(latestBLockCopy.Txns)
   460  		if err != nil {
   461  			return "", nil, nil, nil, errors.Wrapf(
   462  				err, "GetBlockTemplate: Problem computing merkle root: ")
   463  		}
   464  
   465  		// Set the merkle root in the header.
   466  		latestBLockCopy.Header.TransactionMerkleRoot = merkleRoot
   467  
   468  		headerBytes, err := latestBLockCopy.Header.ToBytes(false)
   469  		if err != nil {
   470  			return "", nil, nil, nil, errors.Wrapf(
   471  				err, "GetBlockTemplate: Problem serializing header: ")
   472  		}
   473  
   474  		// If we get here then the header bytes and the ExtraNonce are good to go.
   475  		headers = append(headers, headerBytes)
   476  		extraNonces = append(extraNonces, extraNonce)
   477  	}
   478  	// At this point we have everything the miner needs so we should be good to go.
   479  
   480  	return blockID, headers, extraNonces, blockProducer.currentDifficultyTarget, nil
   481  }
   482  
   483  func (desoBlockProducer *DeSoBlockProducer) UpdateLatestBlockTemplate() error {
   484  	// Use a dummy public key.
   485  	currentBlockTemplate, diffTarget, lastNode, err :=
   486  		desoBlockProducer._getBlockTemplate(MustBase58CheckDecode(ArchitectPubKeyBase58Check))
   487  	if err != nil {
   488  		return err
   489  	}
   490  
   491  	// Log the results.
   492  	glog.V(1).Infof("Produced block template with difficulty target %v "+
   493  		"and lastNode %v", diffTarget, lastNode)
   494  
   495  	desoBlockProducer.AddBlockTemplate(currentBlockTemplate, diffTarget)
   496  	return nil
   497  }
   498  
   499  func (desoBlockProducer *DeSoBlockProducer) SignBlock(blockFound *MsgDeSoBlock) error {
   500  	// If there's no private key on this BlockProducer then there's nothing to do.
   501  	if desoBlockProducer.blockProducerPrivateKey == nil {
   502  		return nil
   503  	}
   504  
   505  	// If there is a private key on this block producer then sign the block hash with it
   506  	// and include the signature in the block.
   507  	blockHash, err := blockFound.Header.Hash()
   508  	if err != nil {
   509  		return errors.Wrap(
   510  			fmt.Errorf("Error computing block hash from header submitted: %v", err), "")
   511  	}
   512  
   513  	signature, err := desoBlockProducer.blockProducerPrivateKey.Sign(blockHash[:])
   514  	if err != nil {
   515  		return errors.Wrap(
   516  			fmt.Errorf("Error signing block: %v", err), "")
   517  	}
   518  	// If we get here, we now have a valid signature for the block.
   519  
   520  	// Embed the signature into the block.
   521  	blockFound.BlockProducerInfo = &BlockProducerInfo{
   522  		PublicKey: desoBlockProducer.blockProducerPrivateKey.PubKey().SerializeCompressed(),
   523  		Signature: signature,
   524  	}
   525  
   526  	return nil
   527  }
   528  
   529  func (desoBlockProducer *DeSoBlockProducer) Start() {
   530  	// Set the time to a nil value so we run on the first iteration of the loop.
   531  	var lastBlockUpdate time.Time
   532  	desoBlockProducer.producerWaitGroup.Add(1)
   533  
   534  	for {
   535  		select {
   536  		case <-desoBlockProducer.stopProducerChannel:
   537  			desoBlockProducer.producerWaitGroup.Done()
   538  			return
   539  		default:
   540  			secondsLeft := float64(desoBlockProducer.minBlockUpdateIntervalSeconds) - time.Since(lastBlockUpdate).Seconds()
   541  			if !lastBlockUpdate.IsZero() && secondsLeft > 0 {
   542  				glog.V(1).Infof("Sleeping for %v seconds before producing next block template...", secondsLeft)
   543  				time.Sleep(time.Duration(math.Ceil(secondsLeft)) * time.Second)
   544  				continue
   545  			}
   546  
   547  			// Update the time so start the clock for the next iteration.
   548  			lastBlockUpdate = time.Now()
   549  
   550  			glog.V(1).Infof("Producing block template...")
   551  			err := desoBlockProducer.UpdateLatestBlockTemplate()
   552  			if err != nil {
   553  				// If we hit an error, log it and sleep for a second. This could happen due to us
   554  				// being in the middle of processing a block or something.
   555  				glog.Errorf("Error producing block template: %v", err)
   556  				time.Sleep(time.Second)
   557  				continue
   558  			}
   559  
   560  		}
   561  	}
   562  }