dcrd/blockchain/sequencelock.go

212 lines
8.9 KiB
Go
Raw Permalink Normal View History

// Copyright (c) 2017-2019 The Decred developers
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
// 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"
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
"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)
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
}
// 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) {
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
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)
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
}
// 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
}
blockchain: Convert to full block index in mem. This reworks the block index code such that it loads all of the headers in the main chain at startup and constructs the full block index accordingly. Since the full index from the current best tip all the way back to the genesis block is now guaranteed to be in memory, this also removes all code related to dynamically loading the nodes and updates some of the logic to take advantage of the fact traversing the block index can no longer potentially fail. There are also many more optimizations and simplifications that can be made in the future as a result of this. Due to removing all of the extra overhead of tracking the dynamic state, and ensuring the block node structs are aligned to eliminate extra padding, the end result of a fully populated block index now takes quite a bit less memory than the previous dynamically loaded version. It also speeds up the initial startup process by roughly 2x since it is faster to bulk load the nodes in order as opposed to dynamically loading only the nodes near the tip in backwards order. For example, here is some startup timing information before and after this commit on a node that contains roughly 238,000 blocks: 7200 RPM HDD: ------------- Startup time before this commit: ~7.71s Startup time after this commit: ~3.47s SSD: ---- Startup time before this commit: ~6.34s Startup time after this commit: ~3.51s Some additional benefits are: - Since every block node is in memory, the code which reconstructs headers from block nodes means that all headers can always be served from memory which will be important since the network will be moving to header-based semantics - Several of the error paths can be removed since they are no longer necessary - It is no longer expensive to calculate CSV sequence locks or median times of blocks way in the past - It is much less expensive to calculate the initial states for the various intervals such as the stake and voter version - It will be possible to create much more efficient iteration and simplified views of the overall index An overview of the logic changes are as follows: - Move AncestorNode from blockIndex to blockNode and greatly simplify since it no longer has to deal with the possibility of dynamically loading nodes and related failures - Replace nodeAtHeightFromTopNode from BlockChain with RelativeAncestor on blockNode and define it in terms of AncestorNode - Move CalcPastMedianTime from blockIndex to blockNode and remove no longer necessary test for nil - Remove findNode and replace all of its uses with direct queries of the block index - Remove blockExists and replace all of its uses with direct queries of the block index - Remove all functions and fields related to dynamically loading nodes - children and parentHash fields from blockNode - depNodes from blockIndex - loadBlockNode from blockIndex - PrevNodeFromBlock from blockIndex - {p,P}revNodeFromNode from blockIndex - RemoveNode - Replace all instances of iterating backwards through nodes to directly access the parent now that nodes don't potentially need to be dynamically loaded - Introduce a lookupNode function on blockIndex which allows the initialization code to locklessly query the index - No longer take the chain lock when only access to the block index, which has its own lock, is needed - Removed the error paths from several functions that can no longer fail - getReorganizeNodes - findPrevTestNetDifficulty - sumPurchasedTickets - findStakeVersionPriorNode - Removed all error paths related to node iteration that can no longer fail - Modify FetchUtxoView to return an empty view for the genesis block
2018-05-27 02:47:45 +00:00
blockNode := node.Ancestor(prevInputHeight)
medianTime := blockNode.CalcPastMedianTime()
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
// 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
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
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)
blockchain: Implement enforced relative seq locks. This repurposes the sequence number of version 2 transaction inputs to provide consensus-enforced relative lock-time semantics so that a transaction can require inputs to have a specified relative age before they are allowed to be included in a block. Each relative time lock can either specify a relative number of seconds (with a granularity of 512 seconds and a maximum value of 33,553,920) or a specific number of blocks (max 65535). The number of seconds is calculated relative to the past median time of the block before the one that contains the referenced output. This is done because, due to other changes that will also be included in the same agenda vote, said time will become the earliest possible time the block that contains the referenced output could have been (technically it will be one second after that, but that complexity is ignored since there is already a granularity involved anyways). It is also possible to disable the behavior by setting bit 31 of the sequence number (which all transactions currently do by default since they are set to the max). In order for the transaction to be permitted to the mempool, relayed, considered for inclusion into block templates, and allowed into a block, the specified relative time locks for all of its inputs must be satisfied. This only implements the required logic and tests to enforce the new behavior. Code to enforce the new behavior when considering candidate transactions for acceptance to the mempool, relaying, and inclusion into block templates will be added in a separate commit. A consensus vote is required in order to reject blocks which contain transactions that violate the new rules at a consensus level. Code to selectively enable consensus enforcement based on the result of an agenda vote will be added in a separate commit. In order to accomplish this new behavior, the concept of a sequence lock is introduced which allows the minimum possible time and height at which a transaction can be included into a block to be calculated from all inputs with non-disabled relative time locks, and functions to calculate and evaluate the sequence lock are added. The following is an overview of the changes: - Introduce a new struct named SequenceLock to represent the previously described sequence lock - Define new constants related to sequence numbers named SequenceLockTimeDisabled, SequenceLockTimeIsSeconds, SequenceLockTimeMask, and SequenceLockTimeGranularity - Add a new function named calcSequenceLock to calculate the sequence lock for a given transaction - Add a new function named SequenceLockActive to determine if a given sequence lock is satisfied for a given block height and past median time - Add a convenience function named LockTimeToSequence which can be used to convert a relative lock time to a sequence number - Add a comprehensive set of tests for all of the new funcs
2017-09-21 17:46:30 +00:00
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
}