// BenchmarkIsCoinBase performs a simple benchmark against the IsCoinBase // function. func BenchmarkIsCoinBase(b *testing.B) { tx, _ := btcutil.NewBlock(&Block100000).Tx(1) b.ResetTimer() for i := 0; i < b.N; i++ { blockchain.IsCoinBase(tx) } }
// GenerateAndSubmitBlock creates a block whose contents include the passed // transactions and submits it to the running simnet node. For generating // blocks with only a coinbase tx, callers can simply pass nil instead of // transactions to be mined. Additionally, a custom block version can be set by // the caller. A blockVersion of -1 indicates that the current default block // version should be used. An uninitialized time.Time should be used for the // blockTime parameter if one doesn't wish to set a custom time. // // This function is safe for concurrent access. func (h *Harness) GenerateAndSubmitBlock(txns []*btcutil.Tx, blockVersion int32, blockTime time.Time) (*btcutil.Block, error) { h.Lock() defer h.Unlock() if blockVersion == -1 { blockVersion = BlockVersion } prevBlockHash, prevBlockHeight, err := h.Node.GetBestBlock() if err != nil { return nil, err } mBlock, err := h.Node.GetBlock(prevBlockHash) if err != nil { return nil, err } prevBlock := btcutil.NewBlock(mBlock) prevBlock.SetHeight(prevBlockHeight) // Create a new block including the specified transactions newBlock, err := CreateBlock(prevBlock, txns, blockVersion, blockTime, h.wallet.coinbaseAddr, h.ActiveNet) if err != nil { return nil, err } // Submit the block to the simnet node. if err := h.Node.SubmitBlock(newBlock, nil); err != nil { return nil, err } return newBlock, nil }
// TestMerkle tests the BuildMerkleTreeStore API. func TestMerkle(t *testing.T) { block := btcutil.NewBlock(&Block100000) merkles := blockchain.BuildMerkleTreeStore(block.Transactions(), false) calculatedMerkleRoot := merkles[len(merkles)-1] wantMerkle := &Block100000.Header.MerkleRoot if !wantMerkle.IsEqual(calculatedMerkleRoot) { t.Errorf("BuildMerkleTreeStore: merkle root mismatch - "+ "got %v, want %v", calculatedMerkleRoot, wantMerkle) } }
// This example demonstrates how to create a new chain instance and use // ProcessBlock to attempt to attempt add a block to the chain. As the package // overview documentation describes, this includes all of the Bitcoin consensus // rules. This example intentionally attempts to insert a duplicate genesis // block to illustrate how an invalid block is handled. func ExampleBlockChain_ProcessBlock() { // Create a new database to store the accepted blocks into. Typically // this would be opening an existing database and would not be deleting // and creating a new database like this, but it is done here so this is // a complete working example and does not leave temporary files laying // around. dbPath := filepath.Join(os.TempDir(), "exampleprocessblock") _ = os.RemoveAll(dbPath) db, err := database.Create("ffldb", dbPath, chaincfg.MainNetParams.Net) if err != nil { fmt.Printf("Failed to create database: %v\n", err) return } defer os.RemoveAll(dbPath) defer db.Close() // Create a new BlockChain instance using the underlying database for // the main bitcoin network. This example does not demonstrate some // of the other available configuration options such as specifying a // notification callback and signature cache. Also, the caller would // ordinarily keep a reference to the median time source and add time // values obtained from other peers on the network so the local time is // adjusted to be in agreement with other peers. chain, err := blockchain.New(&blockchain.Config{ DB: db, ChainParams: &chaincfg.MainNetParams, TimeSource: blockchain.NewMedianTime(), }) if err != nil { fmt.Printf("Failed to create chain instance: %v\n", err) return } // Process a block. For this example, we are going to intentionally // cause an error by trying to process the genesis block which already // exists. genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) isMainChain, isOrphan, err := chain.ProcessBlock(genesisBlock, blockchain.BFNone) if err != nil { fmt.Printf("Failed to process block: %v\n", err) return } fmt.Printf("Block accepted. Is it on the main chain?: %v", isMainChain) fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // Output: // Failed to process block: already have block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f }
// TestCheckConnectBlock tests the CheckConnectBlock function to ensure it // fails. func TestCheckConnectBlock(t *testing.T) { // Create a new database and chain instance to run tests against. chain, teardownFunc, err := chainSetup("checkconnectblock", &chaincfg.MainNetParams) if err != nil { t.Errorf("Failed to setup chain instance: %v", err) return } defer teardownFunc() // The genesis block should fail to connect since it's already inserted. genesisBlock := chaincfg.MainNetParams.GenesisBlock err = chain.CheckConnectBlock(btcutil.NewBlock(genesisBlock)) if err == nil { t.Errorf("CheckConnectBlock: Did not received expected error") } }
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works // as expected. func TestCheckBlockSanity(t *testing.T) { powLimit := chaincfg.MainNetParams.PowLimit block := btcutil.NewBlock(&Block100000) timeSource := blockchain.NewMedianTime() err := blockchain.CheckBlockSanity(block, powLimit, timeSource) if err != nil { t.Errorf("CheckBlockSanity: %v", err) } // Ensure a block that has a timestamp with a precision higher than one // second fails. timestamp := block.MsgBlock().Header.Timestamp block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond) err = blockchain.CheckBlockSanity(block, powLimit, timeSource) if err == nil { t.Errorf("CheckBlockSanity: error is nil when it shouldn't be") } }
// BenchmarkBlockHeader benchmarks how long it takes to load the mainnet genesis // block. func BenchmarkBlock(b *testing.B) { // Start by creating a new database and populating it with the mainnet // genesis block. dbPath := filepath.Join(os.TempDir(), "ffldb-benchblk") _ = os.RemoveAll(dbPath) db, err := database.Create("ffldb", dbPath, blockDataNet) if err != nil { b.Fatal(err) } defer os.RemoveAll(dbPath) defer db.Close() err = db.Update(func(tx database.Tx) error { block := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) return tx.StoreBlock(block) }) if err != nil { b.Fatal(err) } b.ReportAllocs() b.ResetTimer() err = db.View(func(tx database.Tx) error { blockHash := chaincfg.MainNetParams.GenesisHash for i := 0; i < b.N; i++ { _, err := tx.FetchBlock(blockHash) if err != nil { return err } } return nil }) if err != nil { b.Fatal(err) } // Don't benchmark teardown. b.StopTimer() }
// UpdateExtraNonce updates the extra nonce in the coinbase script of the passed // block by regenerating the coinbase script with the passed value and block // height. It also recalculates and updates the new merkle root that results // from changing the coinbase script. func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight int32, extraNonce uint64) error { coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce) if err != nil { return err } if len(coinbaseScript) > blockchain.MaxCoinbaseScriptLen { return fmt.Errorf("coinbase transaction script length "+ "of %d is out of range (min: %d, max: %d)", len(coinbaseScript), blockchain.MinCoinbaseScriptLen, blockchain.MaxCoinbaseScriptLen) } msgBlock.Transactions[0].TxIn[0].SignatureScript = coinbaseScript // TODO(davec): A btcutil.Block should use saved in the state to avoid // recalculating all of the other transaction hashes. // block.Transactions[0].InvalidateCache() // Recalculate the merkle root with the updated extra nonce. block := btcutil.NewBlock(msgBlock) merkles := blockchain.BuildMerkleTreeStore(block.Transactions(), false) msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] return nil }
// CreateBlock creates a new block building from the previous block with a // specified blockversion and timestamp. If the timestamp passed is zero (not // initialized), then the timestamp of the previous block will be used plus 1 // second is used. Passing nil for the previous block results in a block that // builds off of the genesis block for the specified chain. func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx, blockVersion int32, blockTime time.Time, miningAddr btcutil.Address, net *chaincfg.Params) (*btcutil.Block, error) { var ( prevHash *chainhash.Hash blockHeight int32 prevBlockTime time.Time ) // If the previous block isn't specified, then we'll construct a block // that builds off of the genesis block for the chain. if prevBlock == nil { prevHash = net.GenesisHash blockHeight = 1 prevBlockTime = net.GenesisBlock.Header.Timestamp.Add(time.Minute) } else { prevHash = prevBlock.Hash() blockHeight = prevBlock.Height() + 1 prevBlockTime = prevBlock.MsgBlock().Header.Timestamp } // If a target block time was specified, then use that as the header's // timestamp. Otherwise, add one second to the previous block unless // it's the genesis block in which case use the current time. var ts time.Time switch { case !blockTime.IsZero(): ts = blockTime default: ts = prevBlockTime.Add(time.Second) } extraNonce := uint64(0) coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce) if err != nil { return nil, err } coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockHeight, miningAddr, net) if err != nil { return nil, err } // Create a new block ready to be solved. blockTxns := []*btcutil.Tx{coinbaseTx} if inclusionTxs != nil { blockTxns = append(blockTxns, inclusionTxs...) } merkles := blockchain.BuildMerkleTreeStore(blockTxns, false) var block wire.MsgBlock block.Header = wire.BlockHeader{ Version: blockVersion, PrevBlock: *prevHash, MerkleRoot: *merkles[len(merkles)-1], Timestamp: ts, Bits: net.PowLimitBits, } for _, tx := range blockTxns { if err := block.AddTransaction(tx.MsgTx()); err != nil { return nil, err } } found := solveBlock(&block.Header, net.PowLimit) if !found { return nil, errors.New("Unable to solve block") } utilBlock := btcutil.NewBlock(&block) utilBlock.SetHeight(blockHeight) return utilBlock, nil }
// NewBlockTemplate returns a new block template that is ready to be solved // using the transactions from the passed transaction source pool and a coinbase // that either pays to the passed address if it is not nil, or a coinbase that // is redeemable by anyone if the passed address is nil. The nil address // functionality is useful since there are cases such as the getblocktemplate // RPC where external mining software is responsible for creating their own // coinbase which will replace the one generated for the block template. Thus // the need to have configured address can be avoided. // // The transactions selected and included are prioritized according to several // factors. First, each transaction has a priority calculated based on its // value, age of inputs, and size. Transactions which consist of larger // amounts, older inputs, and small sizes have the highest priority. Second, a // fee per kilobyte is calculated for each transaction. Transactions with a // higher fee per kilobyte are preferred. Finally, the block generation related // policy settings are all taken into account. // // Transactions which only spend outputs from other transactions already in the // block chain are immediately added to a priority queue which either // prioritizes based on the priority (then fee per kilobyte) or the fee per // kilobyte (then priority) depending on whether or not the BlockPrioritySize // policy setting allots space for high-priority transactions. Transactions // which spend outputs from other transactions in the source pool are added to a // dependency map so they can be added to the priority queue once the // transactions they depend on have been included. // // Once the high-priority area (if configured) has been filled with // transactions, or the priority falls below what is considered high-priority, // the priority queue is updated to prioritize by fees per kilobyte (then // priority). // // When the fees per kilobyte drop below the TxMinFreeFee policy setting, the // transaction will be skipped unless the BlockMinSize policy setting is // nonzero, in which case the block will be filled with the low-fee/free // transactions until the block size reaches that minimum size. // // Any transactions which would cause the block to exceed the BlockMaxSize // policy setting, exceed the maximum allowed signature operations per block, or // otherwise cause the block to be invalid are skipped. // // Given the above, a block generated by this function is of the following form: // // ----------------------------------- -- -- // | Coinbase Transaction | | | // |-----------------------------------| | | // | | | | ----- policy.BlockPrioritySize // | High-priority Transactions | | | // | | | | // |-----------------------------------| | -- // | | | // | | | // | | |--- policy.BlockMaxSize // | Transactions prioritized by fee | | // | until <= policy.TxMinFreeFee | | // | | | // | | | // | | | // |-----------------------------------| | // | Low-fee/Non high-priority (free) | | // | transactions (while block size | | // | <= policy.BlockMinSize) | | // ----------------------------------- -- func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*BlockTemplate, error) { // Extend the most recently known best block. best := g.chain.BestSnapshot() prevHash := best.Hash nextBlockHeight := best.Height + 1 // Create a standard coinbase transaction paying to the provided // address. NOTE: The coinbase value will be updated to include the // fees from the selected transactions later after they have actually // been selected. It is created here to detect any errors early // before potentially doing a lot of work below. The extra nonce helps // ensure the transaction is not a duplicate transaction (paying the // same value to the same public key address would otherwise be an // identical transaction for block version 1). extraNonce := uint64(0) coinbaseScript, err := standardCoinbaseScript(nextBlockHeight, extraNonce) if err != nil { return nil, err } // TODO(roasbeef): add witnesss commitment output coinbaseTx, err := createCoinbaseTx(g.chainParams, coinbaseScript, nextBlockHeight, payToAddress) if err != nil { return nil, err } coinbaseSigOpCost := int64(blockchain.CountSigOps(coinbaseTx)) * blockchain.WitnessScaleFactor // Get the current source transactions and create a priority queue to // hold the transactions which are ready for inclusion into a block // along with some priority related and fee metadata. Reserve the same // number of items that are available for the priority queue. Also, // choose the initial sort order for the priority queue based on whether // or not there is an area allocated for high-priority transactions. sourceTxns := g.txSource.MiningDescs() sortedByFee := g.policy.BlockPrioritySize == 0 priorityQueue := newTxPriorityQueue(len(sourceTxns), sortedByFee) // Create a slice to hold the transactions to be included in the // generated block with reserved space. Also create a utxo view to // house all of the input transactions so multiple lookups can be // avoided. blockTxns := make([]*btcutil.Tx, 0, len(sourceTxns)) blockTxns = append(blockTxns, coinbaseTx) blockUtxos := blockchain.NewUtxoViewpoint() // 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[chainhash.Hash]map[chainhash.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 // a transaction as it is selected for inclusion in the final block. // However, since the total fees aren't known yet, use a dummy value for // the coinbase fee which will be updated later. txFees := make([]int64, 0, len(sourceTxns)) txSigOpCosts := make([]int64, 0, len(sourceTxns)) txFees = append(txFees, -1) // Updated once known txSigOpCosts = append(txSigOpCosts, coinbaseSigOpCost) 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. tx := txDesc.Tx if blockchain.IsCoinBase(tx) { log.Tracef("Skipping coinbase tx %s", tx.Hash()) continue } if !blockchain.IsFinalizedTransaction(tx, nextBlockHeight, g.timeSource.AdjustedTime()) { log.Tracef("Skipping non-finalized tx %s", tx.Hash()) continue } // Fetch all of the utxos referenced by the this transaction. // NOTE: This intentionally does not fetch inputs from the // mempool since a transaction which depends on other // transactions in the mempool must come after those // dependencies in the final generated block. utxos, err := g.chain.FetchUtxoView(tx) if err != nil { log.Warnf("Unable to fetch utxo view for tx %s: %v", tx.Hash(), err) 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 originIndex := txIn.PreviousOutPoint.Index utxoEntry := utxos.LookupEntry(originHash) if utxoEntry == nil || utxoEntry.IsOutputSpent(originIndex) { 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[chainhash.Hash]*txPrioItem) dependers[*originHash] = deps } deps[*prioItem.tx.Hash()] = prioItem if prioItem.dependsOn == nil { prioItem.dependsOn = make( map[chainhash.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.priority = CalcPriority(tx.MsgTx(), utxos, nextBlockHeight) // Calculate the fee in Satoshi/kB. // TODO(roasbeef): cost accounting by weight 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) } // Merge the referenced outputs from the input transactions to // this transaction into the block utxo view. This allows the // code below to avoid a second lookup. mergeUtxoView(blockUtxos, utxos) } 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. blockWeight := uint32((blockHeaderOverhead * blockchain.WitnessScaleFactor) + blockchain.GetTransactionWeight(coinbaseTx)) blockSigOpCost := coinbaseSigOpCost totalFees := int64(0) // Query the version bits state to see if segwit has been activated, if // so then this means that we'll include any transactions with witness // data in the mempool, and also add the witness commitment as an // OP_RETURN output in the coinbase transaction. segwitState, err := g.chain.ThresholdState(chaincfg.DeploymentSegwit) if err != nil { return nil, err } segwitActive := segwitState == blockchain.ThresholdActive witnessIncluded := false // Choose which transactions make it into the block. for priorityQueue.Len() > 0 { // Grab the highest priority (or highest fee per kilobyte // depending on the sort order) transaction. prioItem := heap.Pop(priorityQueue).(*txPrioItem) tx := prioItem.tx switch { // If segregated witness has not been activated yet, then we // shouldn't include any witness transactions in the block. case tx.MsgTx().HasWitness() && !segwitActive: continue // Otherwise, Keep track of if we've included a transaction // with witness data or not. If so, then we'll need to include // the witness commitment as the last output in the coinbase // transaction. case tx.MsgTx().HasWitness() && segwitActive: // If we're about to include a transaction bearing // witness data, then we'll also need to include a // witness commitment in the coinbase transaction. // Therefore, we account for the additional weight // within the block. if !witnessIncluded { // First we account for the additional witness // data in the witness nonce of the coinbaes // transaction: 32-bytes of zeroes. blockWeight += 2 + 32 // Next we account for the additional flag and // marker bytes in the transaction // serialization. blockWeight += (1 + 1) * blockchain.WitnessScaleFactor // Finally we account for the weight of the // additional OP_RETURN output: 8-bytes (value) // + 1-byte (var-int) + 38-bytes (pkScript), // scaling up the weight as it's non-witness // data. blockWeight += (8 + 1 + 38) * blockchain.WitnessScaleFactor } witnessIncluded = true } // Grab any transactions which depend on this one. deps := dependers[*tx.Hash()] // Enforce maximum block size. Also check for overflow. txWeight := uint32(blockchain.GetTransactionWeight(tx)) blockPlusTxWeight := uint32(blockWeight + txWeight) if blockPlusTxWeight < blockWeight || blockPlusTxWeight >= g.policy.BlockMaxWeight { log.Tracef("Skipping tx %s because it would exceed "+ "the max block weight", tx.Hash()) logSkippedDeps(tx, deps) continue } // Enforce maximum signature operation cost per block. Also // check for overflow. sigOpCost, err := blockchain.GetSigOpCost(tx, false, blockUtxos, true, segwitActive) if err != nil { log.Tracef("Skipping tx %s due to error in "+ "GetSigOpCost: %v", tx.Hash(), err) logSkippedDeps(tx, deps) continue } if blockSigOpCost+int64(sigOpCost) < blockSigOpCost || blockSigOpCost+int64(sigOpCost) > blockchain.MaxBlockSigOpsCost { log.Tracef("Skipping tx %s because it would "+ "exceed the maximum sigops per block", tx.Hash()) logSkippedDeps(tx, deps) continue } // Skip free transactions once the block is larger than the // minimum block size. if sortedByFee && prioItem.feePerKB < int64(g.policy.TxMinFreeFee) && blockPlusTxWeight >= g.policy.BlockMinWeight { log.Tracef("Skipping tx %s with feePerKB %d "+ "< TxMinFreeFee %d and block weight %d >= "+ "minBlockWeight %d", tx.Hash(), prioItem.feePerKB, g.policy.TxMinFreeFee, blockPlusTxWeight, g.policy.BlockMinWeight) logSkippedDeps(tx, deps) continue } // Prioritize by fee per kilobyte once the block is larger than // the priority size or there are no more high-priority // transactions. if !sortedByFee && (blockPlusTxWeight >= g.policy.BlockPrioritySize || prioItem.priority <= MinHighPriority) { log.Tracef("Switching to sort by fees per "+ "kilobyte blockSize %d >= BlockPrioritySize "+ "%d || priority %.2f <= minHighPriority %.2f", blockPlusTxWeight, g.policy.BlockPrioritySize, prioItem.priority, MinHighPriority) sortedByFee = true priorityQueue.SetLessFunc(txPQByFee) // Put the transaction back into the priority queue and // skip it so it is re-priortized by fees if it won't // fit into the high-priority section or the priority // is too low. Otherwise this transaction will be the // final one in the high-priority section, so just fall // though to the code below so it is added now. if blockPlusTxWeight > g.policy.BlockPrioritySize || prioItem.priority < MinHighPriority { heap.Push(priorityQueue, prioItem) continue } } // Ensure the transaction inputs pass all of the necessary // preconditions before allowing it to be added to the block. _, err = blockchain.CheckTransactionInputs(tx, nextBlockHeight, blockUtxos, g.chainParams) if err != nil { log.Tracef("Skipping tx %s due to error in "+ "CheckTransactionInputs: %v", tx.Hash(), err) logSkippedDeps(tx, deps) continue } err = blockchain.ValidateTransactionScripts(tx, blockUtxos, txscript.StandardVerifyFlags, g.sigCache, g.hashCache) if err != nil { log.Tracef("Skipping tx %s due to error in "+ "ValidateTransactionScripts: %v", tx.Hash(), err) logSkippedDeps(tx, deps) continue } // Spend the transaction inputs in the block utxo view and add // an entry for it to ensure any transactions which reference // this one have it available as an input and can ensure they // aren't double spending. spendTransaction(blockUtxos, tx, nextBlockHeight) // Add the transaction to the block, increment counters, and // save the fees and signature operation counts to the block // template. blockTxns = append(blockTxns, tx) blockWeight += txWeight blockSigOpCost += int64(sigOpCost) totalFees += prioItem.fee txFees = append(txFees, prioItem.fee) txSigOpCosts = append(txSigOpCosts, int64(sigOpCost)) 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 // block weight for the real transaction count and coinbase value with // the total fees accordingly. blockWeight -= wire.MaxVarIntPayload - (uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) * blockchain.WitnessScaleFactor) coinbaseTx.MsgTx().TxOut[0].Value += totalFees txFees[0] = -totalFees // If segwit is active and we included transactions with witness data, // then we'll need to include a commitment to the witness data in an // OP_RETURN output within the coinbase transaction. if witnessIncluded { // The witness of the coinbase transaction MUST be exactly 32-bytes // of all zeroes. var witnessNonce [blockchain.CoinbaseWitnessDataLen]byte coinbaseTx.MsgTx().TxIn[0].Witness = wire.TxWitness{witnessNonce[:]} // Next, obtain the merkle root of a tree which consists of the // wtxid of all transactions in the block. The coinbase // transaction will have a special wtxid of all zeroes. witnessMerkleTree := blockchain.BuildMerkleTreeStore(blockTxns, true) witnessMerkleRoot := witnessMerkleTree[len(witnessMerkleTree)-1] // The preimage to the witness commitment is: // witnessRoot || coinbaseWitness var witnessPreimage [64]byte copy(witnessPreimage[:32], witnessMerkleRoot[:]) copy(witnessPreimage[32:], witnessNonce[:]) // The witness commitment itself is the double-sha256 of the // witness preimage generated above. With the commitment // generated, the witness script for the output is: OP_RETURN // OP_DATA_36 {0xaa21a9ed || witnessCommitment}. The leading // prefix is refered to as the "witness magic bytes". witnessCommitment := chainhash.DoubleHashB(witnessPreimage[:]) witnessScript := append(blockchain.WitnessMagicBytes, witnessCommitment...) // Finally, create the OP_RETURN carrying witness commitment // output as an additional output within the coinbase. commitmentOutput := &wire.TxOut{ Value: 0, PkScript: witnessScript, } coinbaseTx.MsgTx().TxOut = append(coinbaseTx.MsgTx().TxOut, commitmentOutput) } // Calculate the required difficulty for the block. The timestamp // is potentially adjusted to ensure it comes after the median time of // the last several blocks per the chain consensus rules. ts := medianAdjustedTime(best, g.timeSource) reqDifficulty, err := g.chain.CalcNextRequiredDifficulty(ts) if err != nil { return nil, err } // Calculate the next expected block version based on the state of the // rule change deployments. nextBlockVersion, err := g.chain.CalcNextBlockVersion() if err != nil { return nil, err } // Create a new block ready to be solved. merkles := blockchain.BuildMerkleTreeStore(blockTxns, false) var msgBlock wire.MsgBlock msgBlock.Header = wire.BlockHeader{ Version: nextBlockVersion, PrevBlock: *prevHash, MerkleRoot: *merkles[len(merkles)-1], Timestamp: ts, Bits: reqDifficulty, } for _, tx := range blockTxns { if err := msgBlock.AddTransaction(tx.MsgTx()); err != nil { return nil, err } } // Finally, perform a full check on the created block against the chain // consensus rules to ensure it properly connects to the current best // chain with no issues. block := btcutil.NewBlock(&msgBlock) block.SetHeight(nextBlockHeight) if err := g.chain.CheckConnectBlock(block); err != nil { return nil, err } log.Debugf("Created new block template (%d transactions, %d in "+ "fees, %d signature operations cost, %d weight, target difficulty "+ "%064x)", len(msgBlock.Transactions), totalFees, blockSigOpCost, blockWeight, blockchain.CompactToBig(msgBlock.Header.Bits)) return &BlockTemplate{ Block: &msgBlock, Fees: txFees, SigOpCosts: txSigOpCosts, Height: nextBlockHeight, ValidPayAddress: payToAddress != nil, }, nil }
// This example demonstrates creating a new database, using a managed read-write // transaction to store a block, and using a managed read-only transaction to // fetch the block. func Example_blockStorageAndRetrieval() { // This example assumes the ffldb driver is imported. // // import ( // "github.com/roasbeef/btcd/database" // _ "github.com/roasbeef/btcd/database/ffldb" // ) // Create a database and schedule it to be closed and removed on exit. // Typically you wouldn't want to remove the database right away like // this, nor put it in the temp directory, but it's done here to ensure // the example cleans up after itself. dbPath := filepath.Join(os.TempDir(), "exampleblkstorage") db, err := database.Create("ffldb", dbPath, wire.MainNet) if err != nil { fmt.Println(err) return } defer os.RemoveAll(dbPath) defer db.Close() // Use the Update function of the database to perform a managed // read-write transaction and store a genesis block in the database as // and example. err = db.Update(func(tx database.Tx) error { genesisBlock := chaincfg.MainNetParams.GenesisBlock return tx.StoreBlock(btcutil.NewBlock(genesisBlock)) }) if err != nil { fmt.Println(err) return } // Use the View function of the database to perform a managed read-only // transaction and fetch the block stored above. var loadedBlockBytes []byte err = db.Update(func(tx database.Tx) error { genesisHash := chaincfg.MainNetParams.GenesisHash blockBytes, err := tx.FetchBlock(genesisHash) if err != nil { return err } // As documented, all data fetched from the database is only // valid during a database transaction in order to support // zero-copy backends. Thus, make a copy of the data so it // can be used outside of the transaction. loadedBlockBytes = make([]byte, len(blockBytes)) copy(loadedBlockBytes, blockBytes) return nil }) if err != nil { fmt.Println(err) return } // Typically at this point, the block could be deserialized via the // wire.MsgBlock.Deserialize function or used in its serialized form // depending on need. However, for this example, just display the // number of serialized bytes to show it was loaded as expected. fmt.Printf("Serialized block size: %d bytes\n", len(loadedBlockBytes)) // Output: // Serialized block size: 285 bytes }
// GenerateNBlocks generates the requested number of blocks. It is self // contained in that it creates block templates and attempts to solve them while // detecting when it is performing stale work and reacting accordingly by // generating a new block template. When a block is solved, it is submitted. // The function returns a list of the hashes of generated blocks. func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) { m.Lock() // Respond with an error if server is already mining. if m.started || m.discreteMining { m.Unlock() return nil, errors.New("Server is already CPU mining. Please call " + "`setgenerate 0` before calling discrete `generate` commands.") } m.started = true m.discreteMining = true m.speedMonitorQuit = make(chan struct{}) m.wg.Add(1) go m.speedMonitor() m.Unlock() log.Tracef("Generating %d blocks", n) i := uint32(0) blockHashes := make([]*chainhash.Hash, n, n) // Start a ticker which is used to signal checks for stale work and // updates to the speed monitor. ticker := time.NewTicker(time.Second * hashUpdateSecs) defer ticker.Stop() for { // Read updateNumWorkers in case someone tries a `setgenerate` while // we're generating. We can ignore it as the `generate` RPC call only // uses 1 worker. select { case <-m.updateNumWorkers: default: } // Grab the lock used for block submission, since the current block will // be changing and this would otherwise end up building a new block // template on a block that is in the process of becoming stale. m.submitBlockLock.Lock() curHeight := m.g.BestSnapshot().Height // Choose a payment address at random. rand.Seed(time.Now().UnixNano()) payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))] // Create a new block template using the available transactions // in the memory pool as a source of transactions to potentially // include in the block. template, err := m.g.NewBlockTemplate(payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ "template: %v", err) log.Errorf(errStr) continue } // Attempt to solve the block. The function will exit early // with false when conditions that trigger a stale block, so // a new block template can be generated. When the return is // true a solution was found, so submit the solved block. if m.solveBlock(template.Block, curHeight+1, ticker, nil) { block := btcutil.NewBlock(template.Block) m.submitBlock(block) blockHashes[i] = block.Hash() i++ if i == n { log.Tracef("Generated %d blocks", i) m.Lock() close(m.speedMonitorQuit) m.wg.Wait() m.started = false m.discreteMining = false m.Unlock() return blockHashes, nil } } } }
// generateBlocks is a worker that is controlled by the miningWorkerController. // It is self contained in that it creates block templates and attempts to solve // them while detecting when it is performing stale work and reacting // accordingly by generating a new block template. When a block is solved, it // is submitted. // // It must be run as a goroutine. func (m *CPUMiner) generateBlocks(quit chan struct{}) { log.Tracef("Starting generate blocks worker") // Start a ticker which is used to signal checks for stale work and // updates to the speed monitor. ticker := time.NewTicker(time.Second * hashUpdateSecs) defer ticker.Stop() out: for { // Quit when the miner is stopped. select { case <-quit: break out default: // Non-blocking select to fall through } // Wait until there is a connection to at least one other peer // since there is no way to relay a found block or receive // transactions to work on when there are no connected peers. if m.cfg.ConnectedCount() == 0 { time.Sleep(time.Second) continue } // No point in searching for a solution before the chain is // synced. Also, grab the same lock as used for block // submission, since the current block will be changing and // this would otherwise end up building a new block template on // a block that is in the process of becoming stale. m.submitBlockLock.Lock() curHeight := m.g.BestSnapshot().Height if curHeight != 0 && !m.cfg.IsCurrent() { m.submitBlockLock.Unlock() time.Sleep(time.Second) continue } // Choose a payment address at random. rand.Seed(time.Now().UnixNano()) payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))] // Create a new block template using the available transactions // in the memory pool as a source of transactions to potentially // include in the block. template, err := m.g.NewBlockTemplate(payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ "template: %v", err) log.Errorf(errStr) continue } // Attempt to solve the block. The function will exit early // with false when conditions that trigger a stale block, so // a new block template can be generated. When the return is // true a solution was found, so submit the solved block. if m.solveBlock(template.Block, curHeight+1, ticker, quit) { block := btcutil.NewBlock(template.Block) m.submitBlock(block) } } m.workerWg.Done() log.Tracef("Generate blocks worker done") }
// TestHaveBlock tests the HaveBlock API to ensure proper functionality. func TestHaveBlock(t *testing.T) { // Load up blocks such that there is a side chain. // (genesis block) -> 1 -> 2 -> 3 -> 4 // \-> 3a testFiles := []string{ "blk_0_to_4.dat.bz2", "blk_3A.dat.bz2", } var blocks []*btcutil.Block for _, file := range testFiles { blockTmp, err := loadBlocks(file) if err != nil { t.Errorf("Error loading file: %v\n", err) return } blocks = append(blocks, blockTmp...) } // Create a new database and chain instance to run tests against. chain, teardownFunc, err := chainSetup("haveblock", &chaincfg.MainNetParams) if err != nil { t.Errorf("Failed to setup chain instance: %v", err) return } defer teardownFunc() // Since we're not dealing with the real block chain, disable // checkpoints and set the coinbase maturity to 1. chain.DisableCheckpoints(true) chain.TstSetCoinbaseMaturity(1) for i := 1; i < len(blocks); i++ { _, isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return } if isOrphan { t.Errorf("ProcessBlock incorrectly returned block %v "+ "is an orphan\n", i) return } } // Insert an orphan block. _, isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), blockchain.BFNone) if err != nil { t.Errorf("Unable to process block: %v", err) return } if !isOrphan { t.Errorf("ProcessBlock indicated block is an not orphan when " + "it should be\n") return } tests := []struct { hash string want bool }{ // Genesis block should be present (in the main chain). {hash: chaincfg.MainNetParams.GenesisHash.String(), want: true}, // Block 3a should be present (on a side chain). {hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true}, // Block 100000 should be present (as an orphan). {hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true}, // Random hashes should not be available. {hash: "123", want: false}, } for i, test := range tests { hash, err := chainhash.NewHashFromStr(test.hash) if err != nil { t.Errorf("NewHashFromStr: %v", err) continue } result, err := chain.HaveBlock(hash) if err != nil { t.Errorf("HaveBlock #%d unexpected error: %v", i, err) return } if result != test.want { t.Errorf("HaveBlock #%d got %v want %v", i, result, test.want) continue } } }
// TestPersistence ensures that values stored are still valid after closing and // reopening the database. func TestPersistence(t *testing.T) { t.Parallel() // Create a new database to run tests against. dbPath := filepath.Join(os.TempDir(), "ffldb-persistencetest") _ = os.RemoveAll(dbPath) db, err := database.Create(dbType, dbPath, blockDataNet) if err != nil { t.Errorf("Failed to create test database (%s) %v", dbType, err) return } defer os.RemoveAll(dbPath) defer db.Close() // Create a bucket, put some values into it, and store a block so they // can be tested for existence on re-open. bucket1Key := []byte("bucket1") storeValues := map[string]string{ "b1key1": "foo1", "b1key2": "foo2", "b1key3": "foo3", } genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) genesisHash := chaincfg.MainNetParams.GenesisHash err = db.Update(func(tx database.Tx) error { metadataBucket := tx.Metadata() if metadataBucket == nil { return fmt.Errorf("Metadata: unexpected nil bucket") } bucket1, err := metadataBucket.CreateBucket(bucket1Key) if err != nil { return fmt.Errorf("CreateBucket: unexpected error: %v", err) } for k, v := range storeValues { err := bucket1.Put([]byte(k), []byte(v)) if err != nil { return fmt.Errorf("Put: unexpected error: %v", err) } } if err := tx.StoreBlock(genesisBlock); err != nil { return fmt.Errorf("StoreBlock: unexpected error: %v", err) } return nil }) if err != nil { t.Errorf("Update: unexpected error: %v", err) return } // Close and reopen the database to ensure the values persist. db.Close() db, err = database.Open(dbType, dbPath, blockDataNet) if err != nil { t.Errorf("Failed to open test database (%s) %v", dbType, err) return } defer db.Close() // Ensure the values previously stored in the 3rd namespace still exist // and are correct. err = db.View(func(tx database.Tx) error { metadataBucket := tx.Metadata() if metadataBucket == nil { return fmt.Errorf("Metadata: unexpected nil bucket") } bucket1 := metadataBucket.Bucket(bucket1Key) if bucket1 == nil { return fmt.Errorf("Bucket1: unexpected nil bucket") } for k, v := range storeValues { gotVal := bucket1.Get([]byte(k)) if !reflect.DeepEqual(gotVal, []byte(v)) { return fmt.Errorf("Get: key '%s' does not "+ "match expected value - got %s, want %s", k, gotVal, v) } } genesisBlockBytes, _ := genesisBlock.Bytes() gotBytes, err := tx.FetchBlock(genesisHash) if err != nil { return fmt.Errorf("FetchBlock: unexpected error: %v", err) } if !reflect.DeepEqual(gotBytes, genesisBlockBytes) { return fmt.Errorf("FetchBlock: stored block mismatch") } return nil }) if err != nil { t.Errorf("View: unexpected error: %v", err) return } }
// loadBlocks loads the blocks contained in the testdata directory and returns // a slice of them. func loadBlocks(t *testing.T, dataFile string, network wire.BitcoinNet) ([]*btcutil.Block, error) { // Open the file that contains the blocks for reading. fi, err := os.Open(dataFile) if err != nil { t.Errorf("failed to open file %v, err %v", dataFile, err) return nil, err } defer func() { if err := fi.Close(); err != nil { t.Errorf("failed to close file %v %v", dataFile, err) } }() dr := bzip2.NewReader(fi) // Set the first block as the genesis block. blocks := make([]*btcutil.Block, 0, 256) genesis := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) blocks = append(blocks, genesis) // Load the remaining blocks. for height := 1; ; height++ { var net uint32 err := binary.Read(dr, binary.LittleEndian, &net) if err == io.EOF { // Hit end of file at the expected offset. No error. break } if err != nil { t.Errorf("Failed to load network type for block %d: %v", height, err) return nil, err } if net != uint32(network) { t.Errorf("Block doesn't match network: %v expects %v", net, network) return nil, err } var blockLen uint32 err = binary.Read(dr, binary.LittleEndian, &blockLen) if err != nil { t.Errorf("Failed to load block size for block %d: %v", height, err) return nil, err } // Read the block. blockBytes := make([]byte, blockLen) _, err = io.ReadFull(dr, blockBytes) if err != nil { t.Errorf("Failed to load block %d: %v", height, err) return nil, err } // Deserialize and store the block. block, err := btcutil.NewBlockFromBytes(blockBytes) if err != nil { t.Errorf("Failed to parse block %v: %v", height, err) return nil, err } blocks = append(blocks, block) } return blocks, nil }
// TestFullBlocks ensures all tests generated by the fullblocktests package // have the expected result when processed via ProcessBlock. func TestFullBlocks(t *testing.T) { tests, err := fullblocktests.Generate(false) if err != nil { t.Fatalf("failed to generate tests: %v", err) } // Create a new database and chain instance to run tests against. chain, teardownFunc, err := chainSetup("fullblocktest", &chaincfg.RegressionNetParams) if err != nil { t.Errorf("Failed to setup chain instance: %v", err) return } defer teardownFunc() // testAcceptedBlock attempts to process the block in the provided test // instance and ensures that it was accepted according to the flags // specified in the test. testAcceptedBlock := func(item fullblocktests.AcceptedBlock) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) isMainChain, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone) if err != nil { t.Fatalf("block %q (hash %s, height %d) should "+ "have been accepted: %v", item.Name, block.Hash(), blockHeight, err) } // Ensure the main chain and orphan flags match the values // specified in the test. if isMainChain != item.IsMainChain { t.Fatalf("block %q (hash %s, height %d) unexpected main "+ "chain flag -- got %v, want %v", item.Name, block.Hash(), blockHeight, isMainChain, item.IsMainChain) } if isOrphan != item.IsOrphan { t.Fatalf("block %q (hash %s, height %d) unexpected "+ "orphan flag -- got %v, want %v", item.Name, block.Hash(), blockHeight, isOrphan, item.IsOrphan) } } // testRejectedBlock attempts to process the block in the provided test // instance and ensures that it was rejected with the reject code // specified in the test. testRejectedBlock := func(item fullblocktests.RejectedBlock) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) _, _, err := chain.ProcessBlock(block, blockchain.BFNone) if err == nil { t.Fatalf("block %q (hash %s, height %d) should not "+ "have been accepted", item.Name, block.Hash(), blockHeight) } // Ensure the error code is of the expected type and the reject // code matches the value specified in the test instance. rerr, ok := err.(blockchain.RuleError) if !ok { t.Fatalf("block %q (hash %s, height %d) returned "+ "unexpected error type -- got %T, want "+ "blockchain.RuleError", item.Name, block.Hash(), blockHeight, err) } if rerr.ErrorCode != item.RejectCode { t.Fatalf("block %q (hash %s, height %d) does not have "+ "expected reject code -- got %v, want %v", item.Name, block.Hash(), blockHeight, rerr.ErrorCode, item.RejectCode) } } // testRejectedNonCanonicalBlock attempts to decode the block in the // provided test instance and ensures that it failed to decode with a // message error. testRejectedNonCanonicalBlock := func(item fullblocktests.RejectedNonCanonicalBlock) { headerLen := len(item.RawBlock) if headerLen > 80 { headerLen = 80 } blockHash := chainhash.DoubleHashH(item.RawBlock[0:headerLen]) blockHeight := item.Height t.Logf("Testing block %s (hash %s, height %d)", item.Name, blockHash, blockHeight) // Ensure there is an error due to deserializing the block. var msgBlock wire.MsgBlock err := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0, wire.BaseEncoding) if _, ok := err.(*wire.MessageError); !ok { t.Fatalf("block %q (hash %s, height %d) should have "+ "failed to decode", item.Name, blockHash, blockHeight) } } // testOrphanOrRejectedBlock attempts to process the block in the // provided test instance and ensures that it was either accepted as an // orphan or rejected with a rule violation. testOrphanOrRejectedBlock := func(item fullblocktests.OrphanOrRejectedBlock) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) _, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone) if err != nil { // Ensure the error code is of the expected type. if _, ok := err.(blockchain.RuleError); !ok { t.Fatalf("block %q (hash %s, height %d) "+ "returned unexpected error type -- "+ "got %T, want blockchain.RuleError", item.Name, block.Hash(), blockHeight, err) } } if !isOrphan { t.Fatalf("block %q (hash %s, height %d) was accepted, "+ "but is not considered an orphan", item.Name, block.Hash(), blockHeight) } } // testExpectedTip ensures the current tip of the blockchain is the // block specified in the provided test instance. testExpectedTip := func(item fullblocktests.ExpectedTip) { blockHeight := item.Height block := btcutil.NewBlock(item.Block) block.SetHeight(blockHeight) t.Logf("Testing tip for block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) // Ensure hash and height match. best := chain.BestSnapshot() if *best.Hash != item.Block.BlockHash() || best.Height != blockHeight { t.Fatalf("block %q (hash %s, height %d) should be "+ "the current tip -- got (hash %s, height %d)", item.Name, block.Hash(), blockHeight, best.Hash, best.Height) } } for testNum, test := range tests { for itemNum, item := range test { switch item := item.(type) { case fullblocktests.AcceptedBlock: testAcceptedBlock(item) case fullblocktests.RejectedBlock: testRejectedBlock(item) case fullblocktests.RejectedNonCanonicalBlock: testRejectedNonCanonicalBlock(item) case fullblocktests.OrphanOrRejectedBlock: testOrphanOrRejectedBlock(item) case fullblocktests.ExpectedTip: testExpectedTip(item) default: t.Fatalf("test #%d, item #%d is not one of "+ "the supported test instance types -- "+ "got type: %T", testNum, itemNum, item) } } } }