2

BIP68(nSequence)

 2 years ago
source link: https://imnisen.github.io/bip0068.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

5 代码示例

代码来自btcd的blockchain/chain.go

计算sequenceLock

// calcSequenceLock computes the relative lock-times for the passed
// transaction. See the exported version, CalcSequenceLock for further details.
//
// This function MUST be called with the chain state lock held (for writes).
func (b *BlockChain) calcSequenceLock(node *blockNode, tx *btcutil.Tx, utxoView *UtxoViewpoint, mempool bool) (*SequenceLock, error) {
        // A value of -1 for each relative lock type represents a relative time
        // lock value that will allow a transaction to be included in a block
        // at any given height or time. This value is returned as the relative
        // lock time in the case that BIP 68 is disabled, or has not yet been
        // activated.
        sequenceLock := &SequenceLock{Seconds: -1, BlockHeight: -1}

        // The sequence locks semantics are always active for transactions
        // within the mempool.
        csvSoftforkActive := mempool

        // If we're performing block validation, then we need to query the BIP9
        // state.
        if !csvSoftforkActive {
                // Obtain the latest BIP9 version bits state for the
                // CSV-package soft-fork deployment. The adherence of sequence
                // locks depends on the current soft-fork state.
                csvState, err := b.deploymentState(node.parent, chaincfg.DeploymentCSV)
                if err != nil {
                        return nil, err
                }
                csvSoftforkActive = csvState == ThresholdActive
        }

        // If the transaction's version is less than 2, and BIP 68 has not yet
        // been activated then sequence locks are disabled. Additionally,
        // sequence locks don't apply to coinbase transactions Therefore, we
        // return sequence lock values of -1 indicating that this transaction
        // can be included within a block at any given height or time.
        mTx := tx.MsgTx()
        sequenceLockActive := mTx.Version >= 2 && csvSoftforkActive
        if !sequenceLockActive || IsCoinBase(tx) {
                return sequenceLock, nil
        }

        // Grab the next height from the PoV of the passed blockNode to use for
        // inputs present in the mempool.
        nextHeight := node.height + 1

        for txInIndex, txIn := range mTx.TxIn {
                utxo := utxoView.LookupEntry(txIn.PreviousOutPoint)
                if utxo == nil {
                        str := fmt.Sprintf("output %v referenced from "+
                                "transaction %s:%d either does not exist or "+
                                "has already been spent", txIn.PreviousOutPoint,
                                tx.Hash(), txInIndex)
                        return sequenceLock, ruleError(ErrMissingTxOut, str)
                }

                // If the input height is set to the mempool height, then we
                // assume the transaction makes it into the next block when
                // evaluating its sequence blocks.
                inputHeight := utxo.BlockHeight()
                if inputHeight == 0x7fffffff {
                        inputHeight = nextHeight
                }

                // Given a sequence number, we apply the relative time lock
                // mask in order to obtain the time lock delta required before
                // this input can be spent.
                sequenceNum := txIn.Sequence
                relativeLock := int64(sequenceNum & wire.SequenceLockTimeMask)

                switch {
                // Relative time locks are disabled for this input, so we can
                // skip any further calculation.
                case sequenceNum&wire.SequenceLockTimeDisabled == wire.SequenceLockTimeDisabled:
                        continue
                case sequenceNum&wire.SequenceLockTimeIsSeconds == wire.SequenceLockTimeIsSeconds:
                        // This input requires a relative time lock expressed
                        // in seconds before it can be spent.  Therefore, we
                        // need to query for the block prior to the one in
                        // which this input was included within so we can
                        // compute the past median time for the block prior to
                        // the one which included this referenced output.
                        prevInputHeight := inputHeight - 1
                        if prevInputHeight < 0 {
                                prevInputHeight = 0
                        }
                        blockNode := node.Ancestor(prevInputHeight)
                        medianTime := blockNode.CalcPastMedianTime()

                        // Time based relative time-locks as defined by BIP 68
                        // have a time granularity of RelativeLockSeconds, so
                        // we shift left by this amount to convert to the
                        // proper relative time-lock. We also subtract one from
                        // the relative lock to maintain the original lockTime
                        // semantics.
                        timeLockSeconds := (relativeLock << wire.SequenceLockTimeGranularity) - 1
                        timeLock := medianTime.Unix() + timeLockSeconds
                        if timeLock > sequenceLock.Seconds {
                                sequenceLock.Seconds = timeLock
                        }
                default:
                        // The relative lock-time for this input is expressed
                        // in blocks so we calculate the relative offset from
                        // the input's height as its converted absolute
                        // lock-time. We subtract one from the relative lock in
                        // order to maintain the original lockTime semantics.
                        blockHeight := inputHeight + int32(relativeLock-1)
                        if blockHeight > sequenceLock.BlockHeight {
                                sequenceLock.BlockHeight = blockHeight
                        }
                }
        }

        return sequenceLock, nil
}

检查sequence是否满足条件

// 节选自 blockchain/validate.go 的 checkConnectBlock方法
if csvState == ThresholdActive {
        // If the CSV soft-fork is now active, then modify the
        // scriptFlags to ensure that the CSV op code is properly
        // validated during the script checks bleow.
        scriptFlags |= txscript.ScriptVerifyCheckSequenceVerify

        // We obtain the MTP of the *previous* block in order to
        // determine if transactions in the current block are final.
        medianTime := node.parent.CalcPastMedianTime()

        // Additionally, if the CSV soft-fork package is now active,
        // then we also enforce the relative sequence number based
        // lock-times within the inputs of all transactions in this
        // candidate block.
        for _, tx := range block.Transactions() {
                // A transaction can only be included within a block
                // once the sequence locks of *all* its inputs are
                // active.
                sequenceLock, err := b.calcSequenceLock(node, tx, view,
                        false)
                if err != nil {
                        return err
                }
                if !SequenceLockActive(sequenceLock, node.height,
                        medianTime) {
                        str := fmt.Sprintf("block contains " +
                                "transaction whose input sequence " +
                                "locks are not met")
                        return ruleError(ErrUnfinalizedTx, str)
                }
        }
}


// SequenceLockActive determines if a transaction's sequence locks have been
// met, meaning that all the inputs of a given transaction have reached a
// height or time sufficient for their relative lock-time maturity.
func SequenceLockActive(sequenceLock *SequenceLock, blockHeight int32,
        medianTimePast time.Time) bool {

        // If either the seconds, or height relative-lock time has not yet
        // reached, then the transaction is not yet mature according to its
        // sequence locks.
        if sequenceLock.Seconds >= medianTimePast.Unix() ||
                sequenceLock.BlockHeight >= blockHeight {
                return false
        }

        return true
}


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK