1
0
mirror of https://github.com/bitcoin/bips.git synced 2025-01-19 05:45:07 +01:00

Update reference implementation for BIP68

This commit is contained in:
BtcDrak 2016-01-15 22:14:45 +00:00
parent b457a74040
commit d6dfcf167b

View File

@ -49,7 +49,11 @@ 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 {
@ -84,51 +88,51 @@ This is proposed to be accomplished by replacing IsFinalTx() and CheckFinalTx(),
* 9 bits. */
static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
int64_t LockTime(const CTransaction &tx, int flags, const std::vector<int>* prevHeights, const CBlockIndex& block)
/**
* 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 == 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;
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.
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;
// 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, and a view has been
// provided.
if (!fEnforceBIP68)
continue;
// 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)
continue;
if (prevHeights == NULL)
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();
// Time-based relative lock-times are measured from the
@ -145,43 +149,60 @@ This is proposed to be accomplished by replacing IsFinalTx() and CheckFinalTx(),
}
}
// If all sequence numbers are CTxIn::SEQUENCE_FINAL, the
// transaction is considered final and nLockTime constraints
// are not enforced.
if (fFinalized)
return 0;
return std::make_pair(nMinHeight, nMinTime);
}
if ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD)
nMinHeight = std::max(nMinHeight, (int)tx.nLockTime);
else
nMinTime = std::max(nMinTime, (int64_t)tx.nLockTime);
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;
if (nMinHeight >= block.nHeight)
return nMinHeight;
return true;
}
if (nMinTime >= nBlockTime)
return nMinTime;
bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
{
return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block));
}
return 0;
}</pre>
bool CheckSequenceLocks(const CTransaction &tx, int flags)
{
AssertLockHeld(cs_main);
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.
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;
==Example: Bidirectional payment channel==
// 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;
}
}
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
https://github.com/bitcoin/bitcoin/pull/6312
std::pair<int, int64_t> lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
return EvaluateSequenceLocks(index, lockPair);
}
</pre>
==Acknowledgments==