mirror of https://github.com/zcash/zips.git
Merge pull request #240 from daira/op-csv
Add ZIPs 68, 112, and 113 (OP_CHECKSEQUENCEVERIFY)
This commit is contained in:
commit
94f654c2ec
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
|
@ -0,0 +1,328 @@
|
||||||
|
::
|
||||||
|
|
||||||
|
ZIP: 68
|
||||||
|
Title: Relative lock-time using consensus-enforced sequence numbers
|
||||||
|
Credits: Mark Friedenbach <mark@friedenbach.org>
|
||||||
|
BtcDrak <btcdrak@gmail.com>
|
||||||
|
Nicolas Dorier <nicolas.dorier@gmail.com>
|
||||||
|
kinoshitajona <kinoshitajona@gmail.com>
|
||||||
|
Category: Consensus
|
||||||
|
Status: Draft
|
||||||
|
Created: 2016-06-06
|
||||||
|
|
||||||
|
|
||||||
|
Terminology
|
||||||
|
===========
|
||||||
|
|
||||||
|
The key words "MUST" and "MAY" in this document are to be interpreted as described in
|
||||||
|
RFC 2119. [#RFC2119]_
|
||||||
|
|
||||||
|
The "Median Time Past" of a block in this document is to be interpreted as described in
|
||||||
|
[#zip-0113]_.
|
||||||
|
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
========
|
||||||
|
|
||||||
|
This ZIP introduces relative lock-time (RLT) consensus-enforced semantics of the sequence
|
||||||
|
number field, to enable a signed transaction input to remain invalid for a defined period
|
||||||
|
of time after confirmation of its corresponding outpoint.
|
||||||
|
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
==========
|
||||||
|
|
||||||
|
Zcash transactions have a sequence number field for each input, inherited from Bitcoin.
|
||||||
|
The original idea in Bitcoin appears to have been that a transaction in the mempool would
|
||||||
|
be replaced by using the same input with a higher sequence value. Although this was not
|
||||||
|
properly implemented, it assumes miners would prefer higher sequence numbers even if the
|
||||||
|
lower ones were more profitable to mine. However, a miner acting on profit motives alone
|
||||||
|
would break that assumption completely. The change described by this ZIP repurposes the
|
||||||
|
sequence number for new use cases without breaking existing functionality. It also leaves
|
||||||
|
room for future expansion and other use cases.
|
||||||
|
|
||||||
|
The transaction ``nLockTime`` is used to prevent the mining of a transaction until a
|
||||||
|
certain date. ``nSequence`` will be repurposed to prevent mining of a transaction until
|
||||||
|
a certain age of the spent output in blocks or timespan. This, among other uses, allows
|
||||||
|
bi-directional payment channels as used in [#deployable-lightning]_ and [#zip-0112]_.
|
||||||
|
|
||||||
|
|
||||||
|
Specification
|
||||||
|
=============
|
||||||
|
|
||||||
|
This specification defines the meaning of sequence numbers for transactions in blocks
|
||||||
|
after this proposal has activated.
|
||||||
|
|
||||||
|
If bit (1 << 31) of the sequence number is set, then no consensus meaning is applied to
|
||||||
|
the sequence number and can be included in any block under all currently possible
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
If bit (1 << 31) of the sequence number is not set, then the sequence number is
|
||||||
|
interpreted as an encoded relative lock-time.
|
||||||
|
|
||||||
|
The sequence number encoding MUST be interpreted as follows:
|
||||||
|
|
||||||
|
Bit (1 << 22) determines if the relative lock-time is time-based or block based:
|
||||||
|
If the bit is set, the relative lock-time specifies a timespan in units of 32 seconds
|
||||||
|
granularity. The timespan starts from the Median Time Past of the output’s previous block,
|
||||||
|
and ends at the Median Time Past of the previous block. If the bit is not set, the
|
||||||
|
relative lock-time specifies a number of blocks.
|
||||||
|
|
||||||
|
Note: the 64-second time unit differs from Bitcoin's BIP 68, which uses a 512-second
|
||||||
|
time unit.
|
||||||
|
|
||||||
|
The flag (1 << 22) is the highest order bit in a 3-byte signed integer for use in
|
||||||
|
Zcash scripts as a 3-byte ``PUSHDATA`` with ``OP_CHECKSEQUENCEVERIFY`` [#zip-0112]_.
|
||||||
|
|
||||||
|
This specification only interprets 22 bits of the sequence number as relative lock-time,
|
||||||
|
so a mask of ``0x003FFFFF`` MUST be applied to the sequence field to extract the relative
|
||||||
|
lock-time. The 22-bit specification allows for over 8.5 years of relative lock-time.
|
||||||
|
|
||||||
|
.. figure:: ../rendered/assets/images/zip-0068-encoding.png
|
||||||
|
:align: center
|
||||||
|
:figclass: align-center
|
||||||
|
|
||||||
|
A 32-bit field with 'Disable Flag' at bit (1 << 31), 'Type Flag' at bit (1 << 22), and 'Value' in the low 22 bits.
|
||||||
|
|
||||||
|
For time-based relative lock-time, 64-second granularity was chosen because the block
|
||||||
|
target spacing for Zcash, after activation of the Blossom network upgrade, is 75 seconds.
|
||||||
|
So when using block-based or time-based, roughly the same amount of time can be encoded
|
||||||
|
with the available number of bits. Converting from a sequence number to seconds is
|
||||||
|
performed by multiplying by 64.
|
||||||
|
|
||||||
|
When the relative lock-time is time-based, it is interpreted as a minimum block-time
|
||||||
|
constraint over the input's age. A relative time-based lock-time of zero indicates an
|
||||||
|
input which can be included in any block. More generally, a relative time-based
|
||||||
|
lock-time n can be included into any block produced 64 \* n seconds after the mining
|
||||||
|
date of the output it is spending, or any block thereafter.
|
||||||
|
The mining date of the output is equal to the Median Time Past of the previous block
|
||||||
|
that mined it.
|
||||||
|
|
||||||
|
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 that can be included in any block. More generally, a relative block lock-time n
|
||||||
|
MAY be included n blocks after the mining date of the output it is spending, or any
|
||||||
|
block thereafter.
|
||||||
|
|
||||||
|
The new rules are not applied to the ``nSequence`` field of the input of the coinbase
|
||||||
|
transaction.
|
||||||
|
|
||||||
|
|
||||||
|
Reference Implementation
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. highlight::c++
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Below flags apply in the context of ZIP 68. */
|
||||||
|
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
|
||||||
|
* relative lock-time. */
|
||||||
|
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* 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 = 0x003fffff;
|
||||||
|
|
||||||
|
/* 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 75s on average after Blossom activation,
|
||||||
|
* the minimum granularity for time-based relative lock-time is
|
||||||
|
* fixed at 64 seconds.
|
||||||
|
* Converting from CTxIn::nSequence to seconds is performed by
|
||||||
|
* multiplying by 64, or equivalently shifting up by 6 bits. */
|
||||||
|
static const int SEQUENCE_LOCKTIME_GRANULARITY = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the block height and previous block's Median Time Past at
|
||||||
|
* which the transaction will be considered final in the context of ZIP 68.
|
||||||
|
* 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 ZIP 68.
|
||||||
|
bool fEnforceZIP68 = 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 (!fEnforceZIP68) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nCoinHeight = (*prevHeights)[txinIndex];
|
||||||
|
|
||||||
|
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
|
||||||
|
int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
|
||||||
|
// NOTE: Subtract 1 to maintain nLockTime semantics
|
||||||
|
// ZIP 68 relative lock times have the semantics of calculating
|
||||||
|
// the first block or time at which the transaction would be
|
||||||
|
// valid. When calculating the effective block time or height
|
||||||
|
// for the entire transaction, we switch to using the
|
||||||
|
// semantics of nLockTime which is the last invalid block
|
||||||
|
// time or height. Thus we subtract 1 from the calculated
|
||||||
|
// time or height.
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
assert(block.pprev);
|
||||||
|
int64_t nBlockTime = block.pprev->GetMedianTimePast();
|
||||||
|
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);
|
||||||
|
AssertLockHeld(mempool.cs);
|
||||||
|
|
||||||
|
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
|
||||||
|
// ConnectBlock(), 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 use 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int64_t> lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
|
||||||
|
return EvaluateSequenceLocks(index, lockPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Acknowledgments
|
||||||
|
===============
|
||||||
|
|
||||||
|
This ZIP is based on BIP 68, authored by Mark Friedenbach, BtcDrak, Nicolas Dorier, and
|
||||||
|
kinoshitajona.
|
||||||
|
|
||||||
|
Credit goes to Gregory Maxwell for providing a succinct and clear description of the behavior
|
||||||
|
of this change, which became the basis of the BIP text.
|
||||||
|
|
||||||
|
|
||||||
|
Deployment
|
||||||
|
==========
|
||||||
|
|
||||||
|
At the time of writing it has not been decided which network upgrade (if any) will implement this
|
||||||
|
proposal.
|
||||||
|
|
||||||
|
This ZIP is designed to be deployed simultaneously with [#zip-0112]_ and [#zip-0113]_.
|
||||||
|
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
=============
|
||||||
|
|
||||||
|
The only use of sequence numbers by the zcashd reference client software is to disable checking
|
||||||
|
the ``nLockTime`` constraints in a transaction. The semantics of that application are preserved
|
||||||
|
by this ZIP.
|
||||||
|
|
||||||
|
As can be seen from the specification section, a number of bits are undefined by this ZIP to
|
||||||
|
allow for other use cases by setting bit (1 << 31) as the remaining 31 bits have no meaning
|
||||||
|
under this ZIP. Additionally, bits (1 << 23) through (1 << 30) inclusive have no meaning at all
|
||||||
|
when bit (1 << 31) is unset.
|
||||||
|
|
||||||
|
Unlike BIP 68 in Bitcoin, all of the low 22 bits are used for the value. This reflects the fact
|
||||||
|
that blocks are more frequent (75 seconds instead of 600 seconds), and so more bits are needed
|
||||||
|
to obtain approximately the same range of time.
|
||||||
|
|
||||||
|
The most efficient way to calculate sequence number from relative lock-time is with bit masks
|
||||||
|
and shifts:
|
||||||
|
|
||||||
|
.. hightlight::c++
|
||||||
|
|
||||||
|
// 0 <= nHeight <= 4194303 blocks (~10 years at post-Blossom block target spacing)
|
||||||
|
nSequence = nHeight;
|
||||||
|
nHeight = nSequence & 0x003fffff;
|
||||||
|
|
||||||
|
// 0 <= nTime <= 268435392 seconds (~8.5 years)
|
||||||
|
nSequence = (1 << 22) | (nTime >> 6);
|
||||||
|
nTime = (nSequence & 0x003fffff) << 6;
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. [#RFC2119] `Key words for use in RFCs to Indicate Requirement Levels <https://tools.ietf.org/html/rfc2119>`_
|
||||||
|
.. [#mailing-list] `Bitcoin mailing list discussion <https://www.mail-archive.com/bitcoin-development@lists.sourceforge.net/msg07864.html>`_
|
||||||
|
.. [#zip-0112] `ZIP 112: CHECKSEQUENCEVERIFY <https://github.com/daira/zips/blob/op-csv/zip-0112.rst>`_
|
||||||
|
.. [#zip-0113] `ZIP 113: Median Time Past as endpoint for lock-time calculations <https://github.com/daira/zips/blob/op-csv/zip-0113.rst>`_
|
||||||
|
.. [#deployable-lightning] `Reaching The Ground With Lightning (draft 0.2) <https://github.com/ElementsProject/lightning/raw/master/doc/deployable-lightning.pdf>`_
|
|
@ -0,0 +1,388 @@
|
||||||
|
::
|
||||||
|
|
||||||
|
ZIP: 112
|
||||||
|
Title: CHECKSEQUENCEVERIFY
|
||||||
|
Author: Daira Hopwood <daira@electriccoin.co>
|
||||||
|
Credits: BtcDrak <btcdrak@gmail.com>
|
||||||
|
Mark Friedenbach <mark@friedenbach.org>
|
||||||
|
Eric Lombrozo <elombrozo@gmail.com>
|
||||||
|
Category: Consensus
|
||||||
|
Status: Draft
|
||||||
|
Created: 2019-06-06
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
|
||||||
|
Terminology
|
||||||
|
===========
|
||||||
|
|
||||||
|
The key word "MUST" in this document is to be interpreted as described in RFC 2119. [#RFC2119]_
|
||||||
|
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
========
|
||||||
|
|
||||||
|
This ZIP describes a new opcode (``CHECKSEQUENCEVERIFY``) for the Zcash scripting system, that
|
||||||
|
in combination with ZIP 68 allows execution pathways of a script to be restricted based on the
|
||||||
|
age of the output being spent.
|
||||||
|
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
==========
|
||||||
|
|
||||||
|
ZIP 68 repurposes the transaction ``nSequence`` field meaning by giving sequence numbers new
|
||||||
|
consensus-enforced semantics as a relative lock-time. However, there is no way to build Zcash
|
||||||
|
scripts to make decisions based on this field.
|
||||||
|
|
||||||
|
By making the ``nSequence`` field accessible to script, it becomes possible to construct code
|
||||||
|
pathways that only become accessible some minimum time after proof-of-publication. This enables
|
||||||
|
a wide variety of applications in phased protocols such as escrow, payment channels, or
|
||||||
|
bidirectional pegs.
|
||||||
|
|
||||||
|
|
||||||
|
Specification
|
||||||
|
=============
|
||||||
|
|
||||||
|
``CHECKSEQUENCEVERIFY`` redefines the existing ``NOP3`` opcode. When executed, if any of the
|
||||||
|
following conditions are true, the script interpreter MUST terminate with an error:
|
||||||
|
|
||||||
|
* the stack is empty; or
|
||||||
|
* the top item on the stack is less than 0; or
|
||||||
|
* the top item on the stack has the disable flag (1 << 31) unset; and any of the following hold:
|
||||||
|
|
||||||
|
* the transaction version is less than 2; or
|
||||||
|
* the transaction input sequence number disable flag (1 << 31) is set; or
|
||||||
|
* the relative lock-time type is not the same; or
|
||||||
|
* the top stack item is greater than the transaction input sequence (when masked according to
|
||||||
|
[#zip-0068]_;
|
||||||
|
|
||||||
|
Otherwise, script execution MUST continue as if a ``NOP`` had been executed.
|
||||||
|
|
||||||
|
ZIP 68 prevents a non-final transaction from being selected for inclusion in a block until the
|
||||||
|
corresponding input has reached the specified age, as measured in block-height or block-time. By
|
||||||
|
comparing the argument to ``CHECKSEQUENCEVERIFY`` against the ``nSequence`` field, we indirectly
|
||||||
|
verify a desired minimum age of the the output being spent; until that relative age has been
|
||||||
|
reached any script execution pathway including the ``CHECKSEQUENCEVERIFY`` will fail to validate,
|
||||||
|
causing the transaction not to be selected for inclusion in a block.
|
||||||
|
|
||||||
|
|
||||||
|
Use cases
|
||||||
|
=========
|
||||||
|
|
||||||
|
Contracts With Expiration Deadlines
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Escrow with Timeout
|
||||||
|
'''''''''''''''''''
|
||||||
|
|
||||||
|
An escrow that times out automatically 30 days after being funded can be established in the
|
||||||
|
following way. Alice, Bob and Escrow create a 2-of-3 address with the following redeem script::
|
||||||
|
|
||||||
|
IF
|
||||||
|
2 <Alice's pubkey> <Bob's pubkey> <Escrow's pubkey> 3 CHECKMULTISIG
|
||||||
|
ELSE
|
||||||
|
"30d" CHECKSEQUENCEVERIFY DROP
|
||||||
|
<Alice's pubkey> CHECKSIG
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
At any time funds can be spent using signatures from any two of Alice, Bob or the Escrow.
|
||||||
|
|
||||||
|
After 30 days Alice can sign alone.
|
||||||
|
|
||||||
|
The clock does not start ticking until the payment to the escrow address confirms.
|
||||||
|
|
||||||
|
|
||||||
|
Retroactive Invalidation
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
In many instances, we would like to create contracts that can be revoked in case of some future
|
||||||
|
event. However, given the immutable nature of the block chain, it is practically impossible to
|
||||||
|
retroactively invalidate a previous commitment that has already confirmed. The only mechanism we
|
||||||
|
really have for retroactive invalidation is block chain reorganization which, for fundamental
|
||||||
|
security reasons, is designed to be very hard and very expensive to do.
|
||||||
|
|
||||||
|
Despite this limitation, we do have a way to provide something functionally similar to retroactive
|
||||||
|
invalidation while preserving irreversibility of past commitments using ``CHECKSEQUENCEVERIFY``.
|
||||||
|
By constructing scripts with multiple branches of execution where one or more of the branches are
|
||||||
|
delayed we provide a time window in which someone can supply an invalidation condition that allows
|
||||||
|
the output to be spent, effectively invalidating the would-be delayed branch and potentially
|
||||||
|
discouraging another party from broadcasting the transaction in the first place. If the
|
||||||
|
invalidation condition does not occur before the timeout, the delayed branch becomes spendable,
|
||||||
|
honoring the original contract.
|
||||||
|
|
||||||
|
Some more specific applications of this idea:
|
||||||
|
|
||||||
|
Hash Time-Locked Contracts
|
||||||
|
''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
Hash Time-Locked Contracts (HTLCs) provide a general mechanism for off-chain contract negotiation.
|
||||||
|
An execution pathway can be made to require knowledge of a secret (a hash preimage) that can be
|
||||||
|
presented within an invalidation time window. By sharing the secret it is possible to guarantee
|
||||||
|
to the counterparty that the transaction will never be broadcast since this would allow the
|
||||||
|
counterparty to claim the output immediately while one would have to wait for the time window
|
||||||
|
to pass. If the secret has not been shared, the counterparty will be unable to use the instant
|
||||||
|
pathway and the delayed pathway must be used instead.
|
||||||
|
|
||||||
|
Bidirectional Payment Channels
|
||||||
|
''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
Scriptable relative locktime provides a predictable amount of time to respond in the event a
|
||||||
|
counterparty broadcasts a revoked transaction: Absolute locktime necessitates closing the channel
|
||||||
|
and reopening it when getting close to the timeout, whereas with relative locktime, the clock
|
||||||
|
starts ticking the moment the transaction confirms in a block. It also provides a means to know
|
||||||
|
exactly how long to wait (in number of blocks) before funds can be pulled out of the channel in
|
||||||
|
the event of a noncooperative counterparty.
|
||||||
|
|
||||||
|
Lightning Network
|
||||||
|
'''''''''''''''''
|
||||||
|
|
||||||
|
The lightning network protocol [#lightning]_ extends the bidirectional payment channel idea to
|
||||||
|
allow for payments to be routed over multiple bidirectional payment channel hops.
|
||||||
|
|
||||||
|
These channels are based on an anchor transaction that requires a 2-of-2 multisig from Alice and
|
||||||
|
Bob, and a series of revocable commitment transactions that spend the anchor transaction. The
|
||||||
|
commitment transaction splits the funds from the anchor between Alice and Bob and the latest
|
||||||
|
commitment transaction may be published by either party at any time, finalising the channel.
|
||||||
|
|
||||||
|
Ideally then, a revoked commitment transaction would never be able to be successfully spent; and the
|
||||||
|
latest commitment transaction would be able to be spent very quickly.
|
||||||
|
|
||||||
|
To allow a commitment transaction to be effectively revoked, Alice and Bob have slightly different
|
||||||
|
versions of the latest commitment transaction. In Alice's version, any outputs in the commitment
|
||||||
|
transaction that pay Alice also include a forced delay, and an alternative branch that allows Bob
|
||||||
|
to spend the output if he knows that transaction's revocation code. In Bob's version, payments
|
||||||
|
to Bob are similarly encumbered. When Alice and Bob negotiate new balances and new commitment
|
||||||
|
transactions, they also reveal the old revocation code, thus committing to not relaying the old
|
||||||
|
transaction.
|
||||||
|
|
||||||
|
A simple output, paying to Alice might then look like::
|
||||||
|
|
||||||
|
HASH160 <revokehash> EQUAL
|
||||||
|
IF
|
||||||
|
<Bob's pubkey>
|
||||||
|
ELSE
|
||||||
|
"24h" CHECKSEQUENCEVERIFY DROP
|
||||||
|
<Alice's pubkey>
|
||||||
|
ENDIF
|
||||||
|
CHECKSIG
|
||||||
|
|
||||||
|
This allows Alice to publish the latest commitment transaction at any time and spend the funds
|
||||||
|
after 24 hours, but also ensures that if Alice relays a revoked transaction, that Bob has 24 hours
|
||||||
|
to claim the funds.
|
||||||
|
|
||||||
|
With ``CHECKLOCKTIMEVERIFY``, this would look like::
|
||||||
|
|
||||||
|
HASH160 <revokehash> EQUAL
|
||||||
|
IF
|
||||||
|
<Bob's pubkey>
|
||||||
|
ELSE
|
||||||
|
"2015/12/15" CHECKLOCKTIMEVERIFY DROP
|
||||||
|
<Alice's pubkey>
|
||||||
|
ENDIF
|
||||||
|
CHECKSIG
|
||||||
|
|
||||||
|
This form of transaction would mean that if the anchor is unspent on 2015/12/16, Alice can use this
|
||||||
|
commitment even if it has been revoked, simply by spending it immediately, giving no time for Bob
|
||||||
|
to claim it.
|
||||||
|
|
||||||
|
This means that the channel has a deadline that cannot be pushed back without hitting the blockchain;
|
||||||
|
and also that funds may not be available until the deadline is hit. ``CHECKSEQUENCEVERIFY`` allows
|
||||||
|
you to avoid making such a tradeoff.
|
||||||
|
|
||||||
|
Hashed Time-Lock Contracts (HTLCs) make this slightly more complicated, since in principle they may
|
||||||
|
pay either Alice or Bob, depending on whether Alice discovers a secret R, or a timeout is reached,
|
||||||
|
but the same principle applies -- the branch paying Alice in Alice's commitment transaction gets a
|
||||||
|
delay, and the entire output can be claimed by the other party if the revocation secret is known.
|
||||||
|
With ``CHECKSEQUENCEVERIFY``, a HTLC payable to Alice might look like the following in Alice's
|
||||||
|
commitment transaction::
|
||||||
|
|
||||||
|
HASH160 DUP <R-HASH> EQUAL
|
||||||
|
IF
|
||||||
|
"24h" CHECKSEQUENCEVERIFY
|
||||||
|
2DROP
|
||||||
|
<Alice's pubkey>
|
||||||
|
ELSE
|
||||||
|
<Commit-Revocation-Hash> EQUAL
|
||||||
|
NOTIF
|
||||||
|
"2015/10/20 10:33" CHECKLOCKTIMEVERIFY DROP
|
||||||
|
ENDIF
|
||||||
|
<Bob's pubkey>
|
||||||
|
ENDIF
|
||||||
|
CHECKSIG
|
||||||
|
|
||||||
|
and correspondingly in Bob's commitment transaction::
|
||||||
|
|
||||||
|
HASH160 DUP <R-HASH> EQUAL
|
||||||
|
SWAP <Commit-Revocation-Hash> EQUAL ADD
|
||||||
|
IF
|
||||||
|
<Alice's pubkey>
|
||||||
|
ELSE
|
||||||
|
"2015/10/20 10:33" CHECKLOCKTIMEVERIFY
|
||||||
|
"24h" CHECKSEQUENCEVERIFY
|
||||||
|
2DROP
|
||||||
|
<Bob's pubkey>
|
||||||
|
ENDIF
|
||||||
|
CHECKSIG
|
||||||
|
|
||||||
|
Note that both ``CHECKSEQUENCEVERIFY`` and ``CHECKLOCKTIMEVERIFY`` are used in the final branch
|
||||||
|
above to ensure Bob cannot spend the output until after both the timeout is complete and Alice
|
||||||
|
has had time to reveal the revocation secret.
|
||||||
|
|
||||||
|
See also the 'Deployable Lightning' paper [#deployable-lightning]_.
|
||||||
|
|
||||||
|
|
||||||
|
2-Way Pegged Sidechains
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The 2-way pegged sidechain requires a new ``REORGPROOFVERIFY`` opcode, the semantics of which
|
||||||
|
are outside the scope of this ZIP. ``CHECKSEQUENCEVERIFY`` is used to make sure that sufficient
|
||||||
|
time has passed since the return peg was posted to publish a reorg proof::
|
||||||
|
|
||||||
|
IF
|
||||||
|
lockTxHeight <lockTxHash> nlocktxOut [<workAmount>] reorgBounty Hash160(<...>) <genesisHash> REORGPROOFVERIFY
|
||||||
|
ELSE
|
||||||
|
withdrawLockTime CHECKSEQUENCEVERIFY DROP HASH160 p2shWithdrawDest EQUAL
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference Implementation
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. highlight::c++
|
||||||
|
|
||||||
|
/* Below flags apply in the context of ZIP 68 */
|
||||||
|
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
|
||||||
|
* relative lock-time. */
|
||||||
|
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
case OP_NOP3:
|
||||||
|
{
|
||||||
|
if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) {
|
||||||
|
// not enabled; treat as a NOP3
|
||||||
|
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
|
||||||
|
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.size() < 1)
|
||||||
|
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
|
||||||
|
|
||||||
|
// Note that elsewhere numeric opcodes are limited to
|
||||||
|
// operands in the range -2**31+1 to 2**31-1, however it is
|
||||||
|
// legal for opcodes to produce results exceeding that
|
||||||
|
// range. This limitation is implemented by CScriptNum's
|
||||||
|
// default 4-byte limit.
|
||||||
|
//
|
||||||
|
// Thus as a special case we tell CScriptNum to accept up
|
||||||
|
// to 5-byte bignums, which are good until 2**39-1, well
|
||||||
|
// beyond the 2**32-1 limit of the nSequence field itself.
|
||||||
|
const CScriptNum nSequence(stacktop(-1), fRequireMinimal, 5);
|
||||||
|
|
||||||
|
// In the rare event that the argument may be < 0 due to
|
||||||
|
// some arithmetic being done first, you can always use
|
||||||
|
// 0 MAX CHECKSEQUENCEVERIFY.
|
||||||
|
if (nSequence < 0)
|
||||||
|
return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME);
|
||||||
|
|
||||||
|
// To provide for future soft-fork extensibility, if the
|
||||||
|
// operand has the disabled lock-time flag set,
|
||||||
|
// CHECKSEQUENCEVERIFY behaves as a NOP.
|
||||||
|
if ((nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Compare the specified sequence number with the input.
|
||||||
|
if (!checker.CheckSequence(nSequence))
|
||||||
|
return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const
|
||||||
|
{
|
||||||
|
// Relative lock times are supported by comparing the passed
|
||||||
|
// in operand to the sequence number of the input.
|
||||||
|
const int64_t txToSequence = (int64_t)txTo->vin[nIn].nSequence;
|
||||||
|
|
||||||
|
// Fail if the transaction's version number is not set high
|
||||||
|
// enough to trigger ZIP 68 rules.
|
||||||
|
if (static_cast<uint32_t>(txTo->nVersion) < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Sequence numbers with their most significant bit set are not
|
||||||
|
// consensus constrained. Testing that the transaction's sequence
|
||||||
|
// number do not have this bit set prevents using this property
|
||||||
|
// to get around a CHECKSEQUENCEVERIFY check.
|
||||||
|
if (txToSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Mask off any bits that do not have consensus-enforced meaning
|
||||||
|
// before doing the integer comparisons
|
||||||
|
const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | CTxIn::SEQUENCE_LOCKTIME_MASK;
|
||||||
|
const int64_t txToSequenceMasked = txToSequence & nLockTimeMask;
|
||||||
|
const CScriptNum nSequenceMasked = nSequence & nLockTimeMask;
|
||||||
|
|
||||||
|
// There are two kinds of nSequence: lock-by-blockheight
|
||||||
|
// and lock-by-blocktime, distinguished by whether
|
||||||
|
// nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG.
|
||||||
|
//
|
||||||
|
// We want to compare apples to apples, so fail the script
|
||||||
|
// unless the type of nSequenceMasked being tested is the same as
|
||||||
|
// the nSequenceMasked in the transaction.
|
||||||
|
if (!(
|
||||||
|
(txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) ||
|
||||||
|
(txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG)
|
||||||
|
))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Now that we know we're comparing apples-to-apples, the
|
||||||
|
// comparison is a simple numeric one.
|
||||||
|
if (nSequenceMasked > txToSequenceMasked)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Deployment
|
||||||
|
==========
|
||||||
|
|
||||||
|
At the time of writing it has not been decided which network upgrade (if any) will implement this
|
||||||
|
proposal.
|
||||||
|
|
||||||
|
This ZIP must be deployed simultaneously with ZIP 68 [#zip-0068]_.
|
||||||
|
|
||||||
|
|
||||||
|
Acknowledgements
|
||||||
|
================
|
||||||
|
|
||||||
|
This ZIP is closely based on BIP 112, authored by BtcDrak.
|
||||||
|
|
||||||
|
Mark Friedenbach invented the application of sequence numbers to achieve relative lock-time, and
|
||||||
|
wrote the reference implementation of ``CHECKSEQUENCEVERIFY`` for Bitcoin.
|
||||||
|
|
||||||
|
The Bitcoin reference implementation and BIP 112 was based heavily on work done by Peter Todd for
|
||||||
|
the closely related BIP 65. Eric Lombrozo and Anthony Towns contributed example use cases.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. [#RFC2119] `Key words for use in RFCs to Indicate Requirement Levels <https://tools.ietf.org/html/rfc2119>`_
|
||||||
|
.. [#protocol] `Zcash Protocol Specification, Version 2019.0.1 or later [Overwinter+Sapling] <https://github.com/zcash/zips/blob/master/protocol/protocol.pdf>`_
|
||||||
|
.. [#bip-0065] `BIP 65: OP_CHECKLOCKTIMEVERIFY <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>`_
|
||||||
|
.. [#zip-0068] `ZIP 68: Relative lock-time through consensus-enforced sequence numbers <https://github.com/zcash/zips/blob/op-csv/zip-0068.rst>`_
|
||||||
|
.. [#deployable-lightning] `Deployable Lightning <https://github.com/ElementsProject/lightning/blob/master/doc/deployable-lightning.pdf>`_
|
||||||
|
.. [#lightning] `Lightning Network paper <http://lightning.network/lightning-network-paper.pdf>`_
|
||||||
|
.. [#HTLCs] `HTLCs using OP_CHECKSEQUENCEVERIFY/OP_LOCKTIMEVERIFY and revocation hashes <http://lists.linuxfoundation.org/pipermail/lightning-dev/2015-July/000021.html>`_
|
||||||
|
.. [#scaling] `Scaling Bitcoin to Billions of Transactions Per Day <http://diyhpl.us/diyhpluswiki/transcripts/sf-bitcoin-meetup/2015-02-23-scaling-bitcoin-to-billions-of-transactions-per-day/>`_
|
||||||
|
.. [#micropayments] `Jeremy Spilman, Micropayment Channels <https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2013-April/002433.html>`_
|
|
@ -0,0 +1,122 @@
|
||||||
|
::
|
||||||
|
|
||||||
|
ZIP: 113
|
||||||
|
Title: Median Time Past as endpoint for lock-time calculations
|
||||||
|
Author: Daira Hopwood <daira@electriccoin.co>
|
||||||
|
Credits: Thomas Kerin <me@thomaskerin.io>
|
||||||
|
Mark Friedenbach <mark@friedenbach.org>
|
||||||
|
Gregory Maxwell
|
||||||
|
Category: Consensus
|
||||||
|
Status: Draft
|
||||||
|
Created: 2019-06-07
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
|
||||||
|
Terminology
|
||||||
|
===========
|
||||||
|
|
||||||
|
The key words "MUST" and "MAY" in this document are to be interpreted as described in
|
||||||
|
RFC 2119. [#RFC2119]_
|
||||||
|
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
========
|
||||||
|
|
||||||
|
This ZIP is a proposal to redefine the semantics used in determining a time-locked
|
||||||
|
transaction's eligibility for inclusion in a block. The median of the last PoWMedianBlockSpan
|
||||||
|
(11) blocks is used instead of the block's timestamp, ensuring that it increases
|
||||||
|
monotonically with each block.
|
||||||
|
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
==========
|
||||||
|
|
||||||
|
At present, transactions are excluded from inclusion in a block if the present time or block
|
||||||
|
height is less than or equal to that specified in the locktime. Since the consensus rules
|
||||||
|
do not mandate strict ordering of block timestamps, this has the unfortunate outcome of
|
||||||
|
creating a perverse incentive for miners to lie about the time of their blocks in order to
|
||||||
|
collect more fees by including transactions that by wall clock determination have not yet
|
||||||
|
matured.
|
||||||
|
|
||||||
|
This ZIP proposes comparing the locktime against the median of the past PoWMedianBlockSpan
|
||||||
|
blocks' timestamps, rather than the timestamp of the block including the transaction.
|
||||||
|
Existing consensus rules guarantee this value to monotonically advance, thereby removing
|
||||||
|
the capability for miners to claim more transaction fees by lying about the timestamps of
|
||||||
|
their block.
|
||||||
|
|
||||||
|
This proposal seeks to ensure reliable behaviour in locktime calculations as required by
|
||||||
|
[#bip-0065]_ (CHECKLOCKTIMEVERIFY) and matching the behavior of [#zip-0112]_ (CHECKSEQUENCEVERIFY).
|
||||||
|
This also matches the use of Median Time Past in difficulty adjustment as specified in
|
||||||
|
section 7.6.3 of [#protocol]_.
|
||||||
|
|
||||||
|
|
||||||
|
Specification
|
||||||
|
=============
|
||||||
|
|
||||||
|
Let PoWMedianBlockSpan be as defined in [#protocol]_ section 5.3, and let the median
|
||||||
|
function be as defined in [#protocol]_ section 7.6.3.
|
||||||
|
|
||||||
|
The Median Time Past of a block is specified as the median of the timestamps of the
|
||||||
|
prior PoWMedianBlockSpan blocks, as calculated by MedianTime(height) in [#protocol]_
|
||||||
|
section 7.6.3.
|
||||||
|
|
||||||
|
The values for transaction locktime remain unchanged. The difference is only in the
|
||||||
|
calculation determining whether a transaction can be included. After activation of
|
||||||
|
this ZIP, lock-time constraints of a transaction MUST be checked according to the
|
||||||
|
Median Time Past of the transaction's block.
|
||||||
|
|
||||||
|
[FIXME make this a proper specification, independent of the zcashd implementation.]
|
||||||
|
|
||||||
|
Lock-time constraints are checked by the consensus method ``IsFinalTx()``. This method
|
||||||
|
takes the block time as one parameter. This ZIP proposes that after activation calls to
|
||||||
|
``IsFinalTx()`` within consensus code use the return value of ``GetMedianTimePast(pindexPrev)``
|
||||||
|
instead.
|
||||||
|
|
||||||
|
The new rule applies to all transactions, including the coinbase transaction.
|
||||||
|
|
||||||
|
|
||||||
|
Reference Implementation
|
||||||
|
========================
|
||||||
|
|
||||||
|
This will be based on `Bitcoin PR 6566 <https://github.com/bitcoin/bitcoin/pull/6566>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Acknowledgements
|
||||||
|
================
|
||||||
|
|
||||||
|
This ZIP is based on BIP 113, authored by Thomas Kerin and Mark Friedenbach.
|
||||||
|
|
||||||
|
Mark Friedenbach designed and authored the reference implementation for Bitcoin.
|
||||||
|
|
||||||
|
Gregory Maxwell came up with the original idea, in #bitcoin-wizards on
|
||||||
|
`2013-07-16 <https://download.wpsoftware.net/bitcoin/wizards/2013/07/13-07-16.log>`_ and
|
||||||
|
`2013-07-17 <https://download.wpsoftware.net/bitcoin/wizards/2013/07/13-07-17.log>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Deployment
|
||||||
|
==========
|
||||||
|
|
||||||
|
At the time of writing it has not been decided which network upgrade (if any) will implement this
|
||||||
|
proposal.
|
||||||
|
|
||||||
|
This ZIP is designed to be deployed simultaneously with [#zip-0068]_ and [#zip-0112]_.
|
||||||
|
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
=============
|
||||||
|
|
||||||
|
At the post-Blossom block target spacing of 75 seconds, transactions generated using time-based
|
||||||
|
lock-time will take approximately 7.5 minutes longer to confirm than would be expected under the
|
||||||
|
old rules. This is not known to introduce any compatibility concerns with existing protocols.
|
||||||
|
This delay is less than in Bitcoin due to the faster block target spacing in Zcash.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. [#RFC2119] `Key words for use in RFCs to Indicate Requirement Levels <https://tools.ietf.org/html/rfc2119>`_
|
||||||
|
.. [#protocol] `Zcash Protocol Specification, Version 2019.0.1 or later [Overwinter+Sapling+Blossom] <https://github.com/zcash/zips/blob/master/protocol/blossom.pdf>`_
|
||||||
|
.. [#bip-0065] `BIP 65: OP_CHECKLOCKTIMEVERIFY <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>`_
|
||||||
|
.. [#zip-0068] `ZIP 68: Consensus-enforced transaction replacement signaled via sequence numbers <https://github.com/daira/zips/blob/op-csv/zip-0068.rst>`_
|
||||||
|
.. [#zip-0112] `ZIP 112: CHECKSEQUENCEVERIFY <https://github.com/daira/zips/blob/op-csv/zip-0112.rst>`_
|
||||||
|
|
Loading…
Reference in New Issue