// makeUtxoView creates a mock unspent transaction output view by using the // transaction index in order to look up all inputs referenced by the // transactions in the block. This is sometimes needed when catching indexes up // because many of the txouts could actually already be spent however the // associated scripts are still required to index them. func makeUtxoView(dbTx database.Tx, block *btcutil.Block) (*blockchain.UtxoViewpoint, error) { view := blockchain.NewUtxoViewpoint() for txIdx, tx := range block.Transactions() { // Coinbases do not reference any inputs. Since the block is // required to have already gone through full validation, it has // already been proven on the first transaction in the block is // a coinbase. if txIdx == 0 { continue } // Use the transaction index to load all of the referenced // inputs and add their outputs to the view. for _, txIn := range tx.MsgTx().TxIn { originOut := &txIn.PreviousOutPoint originTx, err := dbFetchTx(dbTx, &originOut.Hash) if err != nil { return nil, err } view.AddTxOuts(btcutil.NewTx(originTx), 0) } } return view, nil }
// loadUtxoView returns a utxo view loaded from a file. func loadUtxoView(filename string) (*blockchain.UtxoViewpoint, error) { // The utxostore file format is: // <tx hash><serialized utxo len><serialized utxo> // // The serialized utxo len is a little endian uint32 and the serialized // utxo uses the format described in chainio.go. filename = filepath.Join("testdata", filename) fi, err := os.Open(filename) if err != nil { return nil, err } // Choose read based on whether the file is compressed or not. var r io.Reader if strings.HasSuffix(filename, ".bz2") { r = bzip2.NewReader(fi) } else { r = fi } defer fi.Close() view := blockchain.NewUtxoViewpoint() for { // Hash of the utxo entry. var hash chainhash.Hash _, err := io.ReadAtLeast(r, hash[:], len(hash[:])) if err != nil { // Expected EOF at the right offset. if err == io.EOF { break } return nil, err } // Num of serialize utxo entry bytes. var numBytes uint32 err = binary.Read(r, binary.LittleEndian, &numBytes) if err != nil { return nil, err } // Serialized utxo entry. serialized := make([]byte, numBytes) _, err = io.ReadAtLeast(r, serialized, int(numBytes)) if err != nil { return nil, err } // Deserialize it and add it to the view. utxoEntry, err := blockchain.TstDeserializeUtxoEntry(serialized) if err != nil { return nil, err } view.Entries()[hash] = utxoEntry } return view, nil }
// newUtxoViewpoint returns a new utxo view populated with outputs of the // provided source transactions as if there were available at the respective // block height specified in the heights slice. The length of the source txns // and source tx heights must match or it will panic. func newUtxoViewpoint(sourceTxns []*wire.MsgTx, sourceTxHeights []int32) *blockchain.UtxoViewpoint { if len(sourceTxns) != len(sourceTxHeights) { panic("each transaction must have its block height specified") } view := blockchain.NewUtxoViewpoint() for i, tx := range sourceTxns { view.AddTxOuts(btcutil.NewTx(tx), sourceTxHeights[i]) } return view }
// 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 }
// TestCalcSequenceLock tests the LockTimeToSequence function, and the // CalcSequenceLock method of a Chain instance. The tests exercise several // combinations of inputs to the CalcSequenceLock function in order to ensure // the returned SequenceLocks are correct for each test instance. func TestCalcSequenceLock(t *testing.T) { netParams := &chaincfg.SimNetParams // Create a new database and chain instance to run tests against. chain, teardownFunc, err := chainSetup("calcseqlock", netParams) 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) // Create a test mining address to use for the blocks we'll generate // shortly below. k := bytes.Repeat([]byte{1}, 32) _, miningPub := btcec.PrivKeyFromBytes(btcec.S256(), k) miningAddr, err := btcutil.NewAddressPubKey(miningPub.SerializeCompressed(), netParams) if err != nil { t.Fatalf("unable to generate mining addr: %v", err) } // We'll keep track of the previous block for back pointers in blocks // we generated, and also the generated blocks along with the MTP from // their PoV to aide with our relative time lock calculations. var prevBlock *btcutil.Block var blocksWithMTP []struct { block *btcutil.Block mtp time.Time } // We need to activate CSV in order to test the processing logic, so // manually craft the block version that's used to signal the soft-fork // activation. csvBit := netParams.Deployments[chaincfg.DeploymentCSV].BitNumber blockVersion := int32(0x20000000 | (uint32(1) << csvBit)) // Generate enough blocks to activate CSV, collecting each of the // blocks into a slice for later use. numBlocksToActivate := (netParams.MinerConfirmationWindow * 3) for i := uint32(0); i < numBlocksToActivate; i++ { block, err := rpctest.CreateBlock(prevBlock, nil, blockVersion, time.Time{}, miningAddr, netParams) if err != nil { t.Fatalf("unable to generate block: %v", err) } mtp := chain.BestSnapshot().MedianTime _, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err) } if isOrphan { t.Fatalf("ProcessBlock incorrectly returned block %v "+ "is an orphan\n", i) } blocksWithMTP = append(blocksWithMTP, struct { block *btcutil.Block mtp time.Time }{ block: block, mtp: mtp, }) prevBlock = block } // Create a utxo view with all the utxos within the blocks created // above. utxoView := blockchain.NewUtxoViewpoint() for blockHeight, blockWithMTP := range blocksWithMTP { for _, tx := range blockWithMTP.block.Transactions() { utxoView.AddTxOuts(tx, int32(blockHeight)) } } utxoView.SetBestHash(blocksWithMTP[len(blocksWithMTP)-1].block.Hash()) // The median time calculated from the PoV of the best block in our // test chain. For unconfirmed inputs, this value will be used since // the MTP will be calculated from the PoV of the yet-to-be-mined // block. nextMedianTime := int64(1401292712) // We'll refer to this utxo within each input in the transactions // created below. This utxo has an age of 4 blocks and was mined within // block 297 targetTx := blocksWithMTP[len(blocksWithMTP)-4].block.Transactions()[0] utxo := wire.OutPoint{ Hash: *targetTx.Hash(), Index: 0, } // Obtain the median time past from the PoV of the input created above. // The MTP for the input is the MTP from the PoV of the block *prior* // to the one that included it. medianTime := blocksWithMTP[len(blocksWithMTP)-5].mtp.Unix() // Add an additional transaction which will serve as our unconfirmed // output. var fakeScript []byte unConfTx := &wire.MsgTx{ TxOut: []*wire.TxOut{{ PkScript: fakeScript, Value: 5, }}, } unConfUtxo := wire.OutPoint{ Hash: unConfTx.TxHash(), Index: 0, } // Adding a utxo with a height of 0x7fffffff indicates that the output // is currently unmined. utxoView.AddTxOuts(btcutil.NewTx(unConfTx), 0x7fffffff) tests := []struct { tx *btcutil.Tx view *blockchain.UtxoViewpoint want *blockchain.SequenceLock mempool bool }{ // A transaction of version one should disable sequence locks // as the new sequence number semantics only apply to // transactions version 2 or higher. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 3), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: -1, }, }, // A transaction with a single input, that a max int sequence // number. This sequence number has the high bit set, so // sequence locks should be disabled. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: wire.MaxTxInSequenceNum, }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: -1, }, }, // A transaction with a single input whose lock time is // expressed in seconds. However, the specified lock time is // below the required floor for time based lock times since // they have time granularity of 512 seconds. As a result, the // seconds lock-time should be just before the median time of // the targeted block. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime - 1, BlockHeight: -1, }, }, // A transaction with a single input whose lock time is // expressed in seconds. The number of seconds should be 1023 // seconds after the median past time of the last block in the // chain. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 1024), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + 1023, BlockHeight: -1, }, }, // A transaction with multiple inputs. The first input has a // sequence lock in blocks with a value of 4. The last input // has a sequence number with a value of 5, but has the disable // bit set. So the first lock should be selected as it's the // target lock as its the furthest in the future lock that // isn't disabled. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2560), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 5) | wire.SequenceLockTimeDisabled, }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 4), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: 299, }, }, // Transaction has a single input spending the genesis block // transaction. The input's sequence number is encodes a // relative lock-time in blocks (3 blocks). The sequence lock // should have a value of -1 for seconds, but a block height of // 298 meaning it can be included at height 299. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 3), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: 298, }, }, // A transaction with two inputs with lock times expressed in // seconds. The selected sequence lock value for seconds should // be the time further in the future. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 5120), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2560), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + (10 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: -1, }, }, // A transaction with two inputs with lock times expressed in // seconds. The selected sequence lock value for blocks should // be the height further in the future. The converted absolute // block height should be 302, meaning it can be included in // block 303. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 1), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 7), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: 302, }, }, // A transaction with multiple inputs. Two inputs are time // based, and the other two are input maturity based. The lock // lying further into the future for both inputs should be // chosen. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2560), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 6656), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 3), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 9), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: 304, }, }, // A transaction with a single unconfirmed input. As the input // is confirmed, the height of the input should be interpreted // as the height of the *next* block. The current block height // is 300, so the lock time should be calculated using height // 301 as a base. A 2 block relative lock means the transaction // can be included after block 302, so in 303. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: unConfUtxo, Sequence: blockchain.LockTimeToSequence(false, 2), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: 302, }, }, // A transaction with a single unconfirmed input. The input has // a time based lock, so the lock time should be based off the // MTP of the *next* block. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: unConfUtxo, Sequence: blockchain.LockTimeToSequence(true, 1024), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: nextMedianTime + 1023, BlockHeight: -1, }, }, } t.Logf("Running %v SequenceLock tests", len(tests)) for i, test := range tests { seqLock, err := chain.CalcSequenceLock(test.tx, test.view, test.mempool) if err != nil { t.Fatalf("test #%d, unable to calc sequence lock: %v", i, err) } if seqLock.Seconds != test.want.Seconds { t.Fatalf("test #%d got %v seconds want %v seconds", i, seqLock.Seconds, test.want.Seconds) } if seqLock.BlockHeight != test.want.BlockHeight { t.Fatalf("test #%d got height of %v want height of %v ", i, seqLock.BlockHeight, test.want.BlockHeight) } } }