summaryrefslogtreecommitdiff
path: root/bip-0068.mediawiki
diff options
context:
space:
mode:
authorBtcDrak <btcdrak@gmail.com>2016-01-15 22:14:45 +0000
committerBtcDrak <btcdrak@gmail.com>2016-01-15 22:20:20 +0000
commitd6dfcf167b8d568be2d671014ca7377356d1a74e (patch)
treef60541a84a909ddbdad119ef1042c45c7ecbe8f8 /bip-0068.mediawiki
parentb457a740406b9cc9ba20c8c9eb8f9f61503f5fe0 (diff)
downloadbips-d6dfcf167b8d568be2d671014ca7377356d1a74e.tar.xz
Update reference implementation for BIP68
Diffstat (limited to 'bip-0068.mediawiki')
-rw-r--r--bip-0068.mediawiki265
1 files changed, 143 insertions, 122 deletions
diff --git a/bip-0068.mediawiki b/bip-0068.mediawiki
index 55905af..e93d748 100644
--- a/bip-0068.mediawiki
+++ b/bip-0068.mediawiki
@@ -49,139 +49,160 @@ The block produced time is equal to the median-time-past of its previous block.
When the relative lock-time is block-based, it is interpreted as a minimum block-height constraint over the input's age. A relative block-based lock-time of zero indicates an input which can be included in any block. More generally, a relative block lock-time n can be included n blocks after the mining date of the output it is spending, or any block thereafter.
-This is proposed to be accomplished by replacing IsFinalTx() and CheckFinalTx(), existing consensus and non-consensus code functions that return true if a transaction's lock-time constraints are satisfied and false otherwise, with LockTime() and CheckLockTime(), new functions that return a non-zero value if a transaction's lock-time or sequence number constraints are not satisfied and zero otherwise:
+==Implementation==
+
+A reference implementation is provided by the following pull request
+
+https://github.com/bitcoin/bitcoin/pull/7184
<pre>
- enum {
- /* Interpret sequence numbers as relative lock-time constraints. */
- LOCKTIME_VERIFY_SEQUENCE = (1 << 0),
- };
-
- /* Setting nSequence to this value for every input in a transaction
- * disables nLockTime. */
- static const uint32_t SEQUENCE_FINAL = 0xffffffff;
-
- /* If this flag set, CTxIn::nSequence is NOT interpreted as a
- * relative lock-time. Setting the most significant bit of a
- * sequence number disabled relative lock-time. */
- static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
+enum {
+ /* Interpret sequence numbers as relative lock-time constraints. */
+ LOCKTIME_VERIFY_SEQUENCE = (1 << 0),
+};
- /* If CTxIn::nSequence encodes a relative lock-time and this flag
- * is set, the relative lock-time has units of 512 seconds,
- * otherwise it specifies blocks with a granularity of 1. */
- static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
+/* Setting nSequence to this value for every input in a transaction
+ * disables nLockTime. */
+static const uint32_t SEQUENCE_FINAL = 0xffffffff;
- /* If CTxIn::nSequence encodes a relative lock-time, this mask is
- * applied to extract that lock-time from the sequence field. */
- static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
+/* If this flag set, CTxIn::nSequence is NOT interpreted as a
+ * relative lock-time. Setting the most significant bit of a
+ * sequence number disabled relative lock-time. */
+static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
- /* In order to use the same number of bits to encode roughly the
- * same wall-clock duration, and because blocks are naturally
- * limited to occur every 600s on average, the minimum granularity
- * for time-based relative lock-time is fixed at 512 seconds.
- * Converting from CTxIn::nSequence to seconds is performed by
- * multiplying by 512 = 2^9, or equivalently shifting up by
- * 9 bits. */
- static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
+/* If CTxIn::nSequence encodes a relative lock-time and this flag
+ * is set, the relative lock-time has units of 512 seconds,
+ * otherwise it specifies blocks with a granularity of 1. */
+static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
- int64_t LockTime(const CTransaction &tx, int flags, const std::vector<int>* prevHeights, const CBlockIndex& block)
- {
- assert(prevHeights == NULL || prevHeights->size() == tx.vin.size());
- int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST)
- ? block.GetAncestor(std::max(block.nHeight-1, 0))->GetMedianTimePast()
- : block.GetBlockTime();
-
- bool fEnforceBIP68 = static_cast<uint32_t>(tx.nVersion) >= 2
- && flags & LOCKTIME_VERIFY_SEQUENCE;
-
- // Will be set to the equivalent height- and time-based nLockTime
- // values that would be necessary to satisfy all relative lock-
- // time constraints given our view of block chain history.
- int nMinHeight = 0;
- int64_t nMinTime = 0;
- // Will remain equal to true if all inputs are finalized
- // (CTxIn::SEQUENCE_FINAL).
- bool fFinalized = true;
- for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
- const CTxIn& txin = tx.vin[txinIndex];
- // Set a flag if we witness an input that isn't finalized.
- if (txin.nSequence == CTxIn::SEQUENCE_FINAL)
- continue;
- else
- fFinalized = false;
+/* If CTxIn::nSequence encodes a relative lock-time, this mask is
+ * applied to extract that lock-time from the sequence field. */
+static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
- // Do not enforce sequence numbers as a relative lock time
- // unless we have been instructed to, and a view has been
- // provided.
- if (!fEnforceBIP68)
- continue;
+/* In order to use the same number of bits to encode roughly the
+ * same wall-clock duration, and because blocks are naturally
+ * limited to occur every 600s on average, the minimum granularity
+ * for time-based relative lock-time is fixed at 512 seconds.
+ * Converting from CTxIn::nSequence to seconds is performed by
+ * multiplying by 512 = 2^9, or equivalently shifting up by
+ * 9 bits. */
+static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
- // Sequence numbers with the most significant bit set are not
- // treated as relative lock-times, nor are they given any
- // consensus-enforced meaning at this point.
- if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG)
- continue;
-
- if (prevHeights == NULL)
- continue;
-
- int nCoinHeight = (*prevHeights)[txinIndex];
-
- if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
-
- int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
-
- // Time-based relative lock-times are measured from the
- // smallest allowed timestamp of the block containing the
- // txout being spent, which is the median time past of the
- // block prior.
- nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1);
- } else {
- // We subtract 1 from relative lock-times because a lock-
- // time of 0 has the semantics of "same block," so a lock-
- // time of 1 should mean "next block," but nLockTime has
- // the semantics of "last invalid block height."
- nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1);
- }
+/**
+ * Calculates the block height and time which the transaction must be later than
+ * in order to be considered final in the context of BIP 68. It also removes
+ * from the vector of input heights any entries which did not correspond to sequence
+ * locked inputs as they do not affect the calculation.
+ */
+static std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
+{
+ assert(prevHeights->size() == tx.vin.size());
+
+ // Will be set to the equivalent height- and time-based nLockTime
+ // values that would be necessary to satisfy all relative lock-
+ // time constraints given our view of block chain history.
+ // The semantics of nLockTime are the last invalid height/time, so
+ // use -1 to have the effect of any height or time being valid.
+ int nMinHeight = -1;
+ int64_t nMinTime = -1;
+
+ // tx.nVersion is signed integer so requires cast to unsigned otherwise
+ // we would be doing a signed comparison and half the range of nVersion
+ // wouldn't support BIP 68.
+ bool fEnforceBIP68 = static_cast<uint32_t>(tx.nVersion) >= 2
+ && flags & LOCKTIME_VERIFY_SEQUENCE;
+
+ // Do not enforce sequence numbers as a relative lock time
+ // unless we have been instructed to
+ if (!fEnforceBIP68) {
+ return std::make_pair(nMinHeight, nMinTime);
+ }
+
+ for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
+ const CTxIn& txin = tx.vin[txinIndex];
+
+ // Sequence numbers with the most significant bit set are not
+ // treated as relative lock-times, nor are they given any
+ // consensus-enforced meaning at this point.
+ if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) {
+ // The height of this input is not relevant for sequence locks
+ (*prevHeights)[txinIndex] = 0;
+ continue;
}
-
- // If all sequence numbers are CTxIn::SEQUENCE_FINAL, the
- // transaction is considered final and nLockTime constraints
- // are not enforced.
- if (fFinalized)
- return 0;
-
- if ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD)
- nMinHeight = std::max(nMinHeight, (int)tx.nLockTime);
- else
- nMinTime = std::max(nMinTime, (int64_t)tx.nLockTime);
-
- if (nMinHeight >= block.nHeight)
- return nMinHeight;
-
- if (nMinTime >= nBlockTime)
- return nMinTime;
-
- return 0;
- }</pre>
-
-Code conditional on the return value of IsFinalTx() / CheckLockTime() has to be updated as well, since the semantics of the return value has been inverted.
-
-==Example: Bidirectional payment channel==
-A bidirectional payment channel can be established by two parties funding a single output in the following way: Alice funds a 1 BTC output which is the 2-of-2 multisig of Alice AND Bob, or Alice's key only after a sufficiently long timeout, e.g. 30 days or 4320 blocks. The channel-generating transaction is signed by Alice and broadcast to the network.
-
-Alice desires to send Bob a payment of 0.1 BTC. She does so by constructing a transaction spending the 1 BTC output and sending 0.1 BTC to Bob and 0.9 BTC back to herself. She provides her signature for the 2-of-2 multisig constraint, and sets a relative lock-time using the sequence number field such that the transaction will become valid 24-hours or 144 blocks before the refund timeout. Two more times Alice sends Bob a payment of 0.1 BTC, each time generating and signing her half of a transaction spending the 1btc output and sending 0.2 BTC, then 0.3 BTC to Bob with a relative lock-time of 29 days from creation of the channel.
-
-Bob now desires to send Alice a refund of 0.25 BTC. He does so by constructing a transaction spending the 1btc output and sending 0.95 BTC (= 0.7 BTC + 0.25 BTC) to Alice and 0.05 BTC to himself. Since Bob will still have in his logs the transaction giving him 0.7 BTC 29 days after the creation of the channel, Alice demands that this new transaction have a relative lock-time of 28 days so she has a full day to broadcast it before the next transaction matures.
-
-Alice and Bob continue to make payments to each other, decrementing the relative lock-time by one day each time the channel switches direction, until the present time is reached or either party desires to close out the channel. A close-out is performed by finalizing the input (nSequence = MAX_INT) and both parties signing.
-
-==Implementation==
-
-A reference implementation is provided by the following pull request
+ int nCoinHeight = (*prevHeights)[txinIndex];
+
+ if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
+ int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
+
+ // Time-based relative lock-times are measured from the
+ // smallest allowed timestamp of the block containing the
+ // txout being spent, which is the median time past of the
+ // block prior.
+ nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1);
+ } else {
+ // We subtract 1 from relative lock-times because a lock-
+ // time of 0 has the semantics of "same block," so a lock-
+ // time of 1 should mean "next block," but nLockTime has
+ // the semantics of "last invalid block height."
+ nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1);
+ }
+ }
+
+ return std::make_pair(nMinHeight, nMinTime);
+}
+
+static bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> lockPair)
+{
+ int64_t nBlockTime = block.pprev ? block.pprev->GetMedianTimePast() : 0;
+ if (lockPair.first >= block.nHeight || lockPair.second >= nBlockTime)
+ return false;
+
+ return true;
+}
+
+bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
+{
+ return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block));
+}
+
+bool CheckSequenceLocks(const CTransaction &tx, int flags)
+{
+ AssertLockHeld(cs_main);
+
+ CBlockIndex* tip = chainActive.Tip();
+ CBlockIndex index;
+ index.pprev = tip;
+ // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
+ // height based locks because when SequenceLocks() is called within
+ // CBlock::AcceptBlock(), the height of the block *being*
+ // evaluated is what is used. Thus if we want to know if a
+ // transaction can be part of the *next* block, we need to call
+ // SequenceLocks() with one more than chainActive.Height().
+ index.nHeight = tip->nHeight + 1;
+
+ // pcoinsTip contains the UTXO set for chainActive.Tip()
+ CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
+ std::vector<int> prevheights;
+ prevheights.resize(tx.vin.size());
+ for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
+ const CTxIn& txin = tx.vin[txinIndex];
+ CCoins coins;
+ if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) {
+ return error("%s: Missing input", __func__);
+ }
+ if (coins.nHeight == MEMPOOL_HEIGHT) {
+ // Assume all mempool transaction confirm in the next block
+ prevheights[txinIndex] = tip->nHeight + 1;
+ } else {
+ prevheights[txinIndex] = coins.nHeight;
+ }
+ }
-https://github.com/bitcoin/bitcoin/pull/6312
+ std::pair<int, int64_t> lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
+ return EvaluateSequenceLocks(index, lockPair);
+}
+</pre>
==Acknowledgments==