mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
This udpates the blockchain module to use the lastest module major versions. While here, it also corrects a few typos and updates some test function names to more accurately reflect their purpose. The updated direct dependencies are as follows: - github.com/decred/dcrd/blockchain/stake/v2@v2.0.1 - github.com/decred/dcrd/chaincfg/v2@v2.2.0 - github.com/decred/dcrd/database/v2@v2.0.0 - github.com/decred/dcrd/dcrutil/v2@v2.0.0 - github.com/decred/dcrd/txscript/v2@v2.0.0
212 lines
8.9 KiB
Go
212 lines
8.9 KiB
Go
// Copyright (c) 2017-2019 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockchain
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/decred/dcrd/blockchain/stake/v2"
|
|
"github.com/decred/dcrd/blockchain/standalone"
|
|
"github.com/decred/dcrd/dcrutil/v2"
|
|
"github.com/decred/dcrd/wire"
|
|
)
|
|
|
|
// SequenceLock represents the minimum timestamp and minimum block height after
|
|
// which a transaction can be included into a block while satisfying the
|
|
// relative lock times of all of its input sequence numbers. It is calculated
|
|
// via the CalcSequenceLock function. Each field may be -1 if none of the input
|
|
// sequence numbers require a specific relative lock time for the respective
|
|
// type. Since all valid heights and times are larger than -1, this implies
|
|
// that it will not prevent a transaction from being included due to the
|
|
// sequence lock, which is the desired behavior.
|
|
type SequenceLock struct {
|
|
MinHeight int64
|
|
MinTime int64
|
|
}
|
|
|
|
// isStakeBaseTx determines whether or not a transaction is a stakebase (also
|
|
// known as a vote). A stakebase is a special transaction created by the
|
|
// proof-of-stake system that creates subsidy. This function simply delegates
|
|
// to the IsSSGen function in the stake package and exists to make calling code
|
|
// that does not care about the specific reason the transaction is not a
|
|
// stakebase, rather only if it is one or not.
|
|
func isStakeBaseTx(tx *wire.MsgTx) bool {
|
|
return stake.IsSSGen(tx)
|
|
}
|
|
|
|
// calcSequenceLock computes the relative lock times for the passed transaction
|
|
// from the point of view of the block node passed in as the first argument.
|
|
//
|
|
// See the CalcSequenceLock comments for more details.
|
|
func (b *BlockChain) calcSequenceLock(node *blockNode, tx *dcrutil.Tx, view *UtxoViewpoint, isActive bool) (*SequenceLock, error) {
|
|
// A value of -1 for each lock type allows a transaction to be included
|
|
// in a block at any given height or time.
|
|
sequenceLock := &SequenceLock{MinHeight: -1, MinTime: -1}
|
|
|
|
// Sequence locks do not apply if they are not yet active, the tx
|
|
// version is less than 2, or the tx is a coinbase or stakebase, so
|
|
// return now with a sequence lock that indicates the tx can possibly be
|
|
// included in a block at any given height or time.
|
|
msgTx := tx.MsgTx()
|
|
enforce := isActive && msgTx.Version >= 2
|
|
if !enforce || standalone.IsCoinBaseTx(msgTx) || isStakeBaseTx(msgTx) {
|
|
return sequenceLock, nil
|
|
}
|
|
|
|
for txInIndex, txIn := range msgTx.TxIn {
|
|
// Nothing to calculate for this input when relative time locks
|
|
// are disabled for it.
|
|
sequenceNum := txIn.Sequence
|
|
if sequenceNum&wire.SequenceLockTimeDisabled != 0 {
|
|
continue
|
|
}
|
|
|
|
utxo := view.LookupEntry(&txIn.PreviousOutPoint.Hash)
|
|
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)
|
|
}
|
|
|
|
// Calculate the sequence locks from the point of view of the
|
|
// next block for inputs that are in the mempool.
|
|
inputHeight := utxo.BlockHeight()
|
|
if inputHeight == 0x7fffffff {
|
|
inputHeight = node.height + 1
|
|
}
|
|
|
|
// Mask off the value portion of the sequence number to obtain
|
|
// the time lock delta required before this input can be spent.
|
|
// The relative lock can be time based or block based.
|
|
relativeLock := int64(sequenceNum & wire.SequenceLockTimeMask)
|
|
|
|
if sequenceNum&wire.SequenceLockTimeIsSeconds != 0 {
|
|
// This input requires a time based relative lock
|
|
// expressed in seconds before it can be spent and time
|
|
// based locks are calculated relative to the earliest
|
|
// possible time the block that contains the referenced
|
|
// output could have been. That time is the past
|
|
// median time of the block before it (technically one
|
|
// second after that, but that complexity is ignored for
|
|
// time based locks which already have a granularity
|
|
// associated with them anyways). Therefore, the block
|
|
// prior to the one in which the referenced output was
|
|
// included is needed to compute its past median time.
|
|
prevInputHeight := inputHeight - 1
|
|
if prevInputHeight < 0 {
|
|
prevInputHeight = 0
|
|
}
|
|
blockNode := node.Ancestor(prevInputHeight)
|
|
medianTime := blockNode.CalcPastMedianTime()
|
|
|
|
// Calculate the minimum required timestamp based on the
|
|
// sum of the aforementioned past median time and
|
|
// required relative number of seconds. Since time
|
|
// based relative locks have a granularity associated
|
|
// with them, shift left accordingly in order to convert
|
|
// to the proper number of relative seconds. Also,
|
|
// subtract one from the relative lock to maintain the
|
|
// original lock time semantics.
|
|
relativeSecs := relativeLock << wire.SequenceLockTimeGranularity
|
|
minTime := medianTime.Unix() + relativeSecs - 1
|
|
if minTime > sequenceLock.MinTime {
|
|
sequenceLock.MinTime = minTime
|
|
}
|
|
} else {
|
|
// This input requires a relative lock expressed in
|
|
// blocks before it can be spent. Therefore, calculate
|
|
// the minimum required height based on the sum of the
|
|
// input height and required relative number of blocks.
|
|
// Also, subtract one from the relative lock in order to
|
|
// maintain the original lock time semantics.
|
|
minHeight := inputHeight + relativeLock - 1
|
|
if minHeight > sequenceLock.MinHeight {
|
|
sequenceLock.MinHeight = minHeight
|
|
}
|
|
}
|
|
}
|
|
|
|
return sequenceLock, nil
|
|
}
|
|
|
|
// CalcSequenceLock computes the minimum block height and time after which the
|
|
// passed transaction can be included into a block while satisfying the relative
|
|
// lock times of all of its input sequence numbers. The passed view is used to
|
|
// obtain the past median time and block heights of the blocks in which the
|
|
// referenced outputs of the inputs to the transaction were included. The
|
|
// generated sequence lock can be used in conjunction with a block height and
|
|
// median time to determine if all inputs to the transaction have reached the
|
|
// required maturity allowing it to be included in a block.
|
|
//
|
|
// NOTE: This will calculate the sequence locks regardless of the state of the
|
|
// agenda which conditionally activates it. This is acceptable for standard
|
|
// transactions, however, callers which are intending to perform any type of
|
|
// consensus checking must check the status of the agenda first.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) CalcSequenceLock(tx *dcrutil.Tx, view *UtxoViewpoint) (*SequenceLock, error) {
|
|
b.chainLock.Lock()
|
|
seqLock, err := b.calcSequenceLock(b.bestChain.Tip(), tx, view, true)
|
|
b.chainLock.Unlock()
|
|
return seqLock, err
|
|
}
|
|
|
|
// LockTimeToSequence converts the passed relative lock time to a sequence
|
|
// number in accordance with DCP0003.
|
|
//
|
|
// A sequence number is defined as follows:
|
|
//
|
|
// - bit 31 is the disable bit
|
|
// - the next 8 bits are reserved
|
|
// - bit 22 is the relative lock type (unset = block height, set = seconds)
|
|
// - the next 6 bites are reserved
|
|
// - the least significant 16 bits represent the value
|
|
// - value has a granularity of 512 when interpreted as seconds (bit 22 set)
|
|
//
|
|
// ---------------------------------------------------
|
|
// | Disable | Reserved | Type | Reserved | Value |
|
|
// ---------------------------------------------------
|
|
// | 1 bit | 8 bits | 1 bit | 6 bits | 16 bits |
|
|
// ---------------------------------------------------
|
|
// | [31] | [30-23] | [22] | [21-16] | [15-0] |
|
|
// ---------------------------------------------------
|
|
//
|
|
// The above implies that the maximum relative block height that can be encoded
|
|
// is 65535 and the maximum relative number of seconds that can be encoded is
|
|
// 65535*512 = 33,553,920 seconds (~1.06 years). It also means that seconds are
|
|
// truncated to the nearest granularity towards 0 (e.g. 536 seconds will end up
|
|
// round tripping as 512 seconds and 1500 seconds will end up round tripping as
|
|
// 1024 seconds).
|
|
//
|
|
// An error will be returned for values that are larger than can be represented.
|
|
func LockTimeToSequence(isSeconds bool, lockTime uint32) (uint32, error) {
|
|
// The corresponding sequence number is simply the desired input age
|
|
// when expressing the relative lock time in blocks.
|
|
if !isSeconds {
|
|
if lockTime > wire.SequenceLockTimeMask {
|
|
return 0, fmt.Errorf("max relative block height a "+
|
|
"sequence number can represent is %d",
|
|
wire.SequenceLockTimeMask)
|
|
}
|
|
return lockTime, nil
|
|
}
|
|
|
|
maxSeconds := uint32(wire.SequenceLockTimeMask <<
|
|
wire.SequenceLockTimeGranularity)
|
|
if lockTime > maxSeconds {
|
|
return 0, fmt.Errorf("max relative seconds a sequence number "+
|
|
"can represent is %d", maxSeconds)
|
|
}
|
|
|
|
// Set the 22nd bit which indicates the lock time is in seconds, then
|
|
// shift the lock time over by 9 since the time granularity is in
|
|
// 512-second intervals (2^9). This results in a max lock time of
|
|
// 33,553,920 seconds (~1.06 years).
|
|
return wire.SequenceLockTimeIsSeconds |
|
|
lockTime>>wire.SequenceLockTimeGranularity, nil
|
|
}
|