Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend the CSV timeout limit with another 4 bits #961

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bench/verify_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static void VerifyScriptBench(benchmark::State& state)
txSpend.witness.vtxinwit.resize(1);
CScriptWitness& witness = txSpend.witness.vtxinwit[0].scriptWitness;
witness.stack.emplace_back();
key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back());
key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0, 0), witness.stack.back());
witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL));
witness.stack.push_back(ToByteVector(pubkey));

Expand Down
6 changes: 4 additions & 2 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags
// 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);
const uint32_t sequenceLocktimeMask = SequenceLocktimeMask(flags);
nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & sequenceLocktimeMask) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that the SEQUENCE_LOCKTIME_GRANULARITY still corresponds to 512 seconds. Should we introduce another type of granularity in dynafed to be 64 seconds.

From bip 68:

For time-based relative lock-time, 512 second granularity was chosen because bitcoin blocks are generated every 600 seconds. So when using block-based or time-based, 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 512 = 2^9, or equivalently shifting up by 9 bits.

Not many people use time-based timelocks, so maybe doing this might be unnecessary and we are okay with a granularity of 512sec.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we should leave it alone. Liquid blocks are every minute anyway (and deviate from that less than Bitcoin deviates from 10 minutes).

} else {
nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1);
const uint32_t sequenceLocktimeMask = SequenceLocktimeMask(flags);
nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & sequenceLocktimeMask) - 1);
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,15 @@ class CTxIn
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. */
* applied to extract that lock-time from the sequence field.
* This value is used when Dynafed is not active. */
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;

/* If CTxIn::nSequence encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field.
* This value is used when Dynafed is active. */
static const uint32_t SEQUENCE_LOCKTIME_MASK_DYNAFED = 0x000fffff;

/* 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
Expand Down
4 changes: 2 additions & 2 deletions src/script/generic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SimpleSignatureChecker : public BaseSignatureChecker
bool sighash_byte;

SimpleSignatureChecker(const uint256& hashIn, bool sighash_byte_in) : hash(hashIn), sighash_byte(sighash_byte_in) {};
bool CheckSig(const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
bool CheckSig(const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override
{
std::vector<unsigned char> vchSigCopy(vchSig);
CPubKey pubkey(vchPubKey);
Expand Down Expand Up @@ -47,7 +47,7 @@ class SimpleSignatureCreator : public BaseSignatureCreator
public:
SimpleSignatureCreator(const uint256& hashIn, bool sighash_byte_in) : checker(hashIn, sighash_byte_in), sighash_byte(sighash_byte_in) {};
const BaseSignatureChecker& Checker() const { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override
{
CKey key;
if (!provider.GetKey(keyid, key))
Expand Down
97 changes: 83 additions & 14 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,17 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
return true;
}

bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
bool static IsDefinedHashtypeSignature(const valtype &vchSig, unsigned int flags) {
if (vchSig.size() == 0) {
return false;
}
unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));

// ELEMENTS: Only allow SIGHASH_RANGEPROOF if the flag is set (after dynafed activation).
if ((flags & SCRIPT_DYNAFED_ACTIVE) == SCRIPT_DYNAFED_ACTIVE) {
nHashType = nHashType & (~(SIGHASH_RANGEPROOF));
}

if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
return false;

Expand All @@ -216,7 +222,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
} else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSigCopy, serror)) {
// serror is set
return false;
} else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSigCopy)) {
} else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSigCopy, flags)) {
return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE);
}
return true;
Expand Down Expand Up @@ -463,7 +469,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
break;

// Compare the specified sequence number with the input.
if (!checker.CheckSequence(nSequence))
if (!checker.CheckSequence(nSequence, flags))
return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME);

break;
Expand Down Expand Up @@ -1213,7 +1219,8 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
//serror is set
return false;
}
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);

bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion, flags);

if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
Expand Down Expand Up @@ -1291,7 +1298,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}

// Check signature
bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion, flags);

if (fOk) {
isig++;
Expand Down Expand Up @@ -1466,13 +1473,15 @@ class CTransactionSignatureSerializer
const CScript& scriptCode; //!< output script being consumed
const unsigned int nIn; //!< input index of txTo being signed
const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set
const bool fRangeproof; //!< whether the hashtype has the SIGHASH_RANGEPROOF flag set
const bool fHashSingle; //!< whether the hashtype is SIGHASH_SINGLE
const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE

public:
CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn, unsigned int flags) :
txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn),
fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)),
fRangeproof(!!(flags & SCRIPT_DYNAFED_ACTIVE) && !!(nHashTypeIn & SIGHASH_RANGEPROOF)),
fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE),
fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {}

Expand Down Expand Up @@ -1529,11 +1538,23 @@ class CTransactionSignatureSerializer
/** Serialize an output of txTo */
template<typename S>
void SerializeOutput(S &s, unsigned int nOutput) const {
if (fHashSingle && nOutput != nIn)
if (fHashSingle && nOutput != nIn) {
// Do not lock-in the txout payee at other indices as txin
::Serialize(s, CTxOut());
else
} else {
::Serialize(s, txTo.vout[nOutput]);

// Serialize rangeproof
if (fRangeproof) {
if (nOutput < txTo.witness.vtxoutwit.size()) {
::Serialize(s, txTo.witness.vtxoutwit[nOutput].vchRangeproof);
::Serialize(s, txTo.witness.vtxoutwit[nOutput].vchSurjectionproof);
} else {
::Serialize(s, (unsigned char) 0);
::Serialize(s, (unsigned char) 0);
}
}
}
}

/** Serialize txTo */
Expand Down Expand Up @@ -1599,6 +1620,21 @@ uint256 GetOutputsHash(const T& txTo)
return ss.GetHash();
}

template <class T>
uint256 GetRangeproofsHash(const T& txTo) {
CHashWriter ss(SER_GETHASH, 0);
for (size_t i = 0; i < txTo.vout.size(); i++) {
if (i < txTo.witness.vtxoutwit.size()) {
ss << txTo.witness.vtxoutwit[i].vchRangeproof;
ss << txTo.witness.vtxoutwit[i].vchSurjectionproof;
} else {
ss << (unsigned char) 0;
ss << (unsigned char) 0;
}
}
return ss.GetHash();
}

} // namespace

template <class T>
Expand All @@ -1610,6 +1646,7 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
hashSequence = GetSequenceHash(txTo);
hashIssuance = GetIssuanceHash(txTo);
hashOutputs = GetOutputsHash(txTo);
hashRangeproofs = GetRangeproofsHash(txTo);
ready = true;
}
}
Expand All @@ -1619,7 +1656,7 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CTransacti
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);

template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, unsigned int flags, const PrecomputedTransactionData* cache)
{
assert(nIn < txTo.vin.size());

Expand All @@ -1628,7 +1665,9 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
uint256 hashSequence;
uint256 hashIssuance;
uint256 hashOutputs;
uint256 hashRangeproofs;
const bool cacheready = cache && cache->ready;
bool fRangeproof = !!(flags & SCRIPT_DYNAFED_ACTIVE) && !!(nHashType & SIGHASH_RANGEPROOF);

if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
Expand All @@ -1644,10 +1683,26 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn

if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);

if (fRangeproof) {
hashRangeproofs = cacheready ? cache->hashRangeproofs : GetRangeproofsHash(txTo);
}
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
CHashWriter ss(SER_GETHASH, 0);
ss << txTo.vout[nIn];
hashOutputs = ss.GetHash();

if (fRangeproof) {
CHashWriter ss(SER_GETHASH, 0);
if (nIn < txTo.witness.vtxoutwit.size()) {
ss << txTo.witness.vtxoutwit[nIn].vchRangeproof;
ss << txTo.witness.vtxoutwit[nIn].vchSurjectionproof;
} else {
ss << (unsigned char) 0;
ss << (unsigned char) 0;
}
hashRangeproofs = ss.GetHash();
}
}

CHashWriter ss(SER_GETHASH, 0);
Expand Down Expand Up @@ -1676,6 +1731,11 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
}
// Outputs (none/one/all, depending on flags)
ss << hashOutputs;
if (fRangeproof) {
// This addition must be conditional because it was added after
// the segwit sighash was specified.
ss << hashRangeproofs;
}
// Locktime
ss << txTo.nLockTime;
// Sighash type
Expand All @@ -1695,7 +1755,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
}

// Wrapper to serialize only the necessary parts of the transaction being signed
CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType);
CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType, flags);

// Serialize and hash
CHashWriter ss(SER_GETHASH, 0);
Expand All @@ -1710,7 +1770,7 @@ bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<un
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
Expand All @@ -1723,7 +1783,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned
int nHashType = vchSig.back();
vchSig.pop_back();

uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, flags, this->txdata);

if (!VerifySignature(vchSig, pubkey, sighash))
return false;
Expand Down Expand Up @@ -1769,7 +1829,7 @@ bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLoc
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSequence) const
bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSequence, unsigned int flags) const
{
// Relative lock times are supported by comparing the passed
// in operand to the sequence number of the input.
Expand All @@ -1789,7 +1849,8 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq

// 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 uint32_t sequenceLocktimeMask = SequenceLocktimeMask(flags);
const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | sequenceLocktimeMask;
const int64_t txToSequenceMasked = txToSequence & nLockTimeMask;
const CScriptNum nSequenceMasked = nSequence & nLockTimeMask;

Expand Down Expand Up @@ -2037,3 +2098,11 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,

return 0;
}

uint32_t SequenceLocktimeMask(unsigned int flags) {
if (!!(flags & SCRIPT_DYNAFED_ACTIVE)) {
return CTxIn::SEQUENCE_LOCKTIME_MASK_DYNAFED;
} else {
return CTxIn::SEQUENCE_LOCKTIME_MASK;
}
}
27 changes: 21 additions & 6 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ enum
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
SIGHASH_ANYONECANPAY = 0x80,

// ELEMENTS:
// A flag that means the rangeproofs should be included in the sighash.
SIGHASH_RANGEPROOF = 0x40,
};

/** Script verification flags.
Expand Down Expand Up @@ -116,16 +120,24 @@ enum
//
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),

// ELEMENTS:

// Signature checking assumes no sighash byte after the DER signature
//
SCRIPT_NO_SIGHASH_BYTE = (1U << 17),

// Support/allow dynafed hardfork features:
// - SIGHASH_RANGEPROOF
// - extended CSV range (see SEQUENCE_LOCKTIME_MASK_DYNAFED)
//
SCRIPT_DYNAFED_ACTIVE = (1U << 18),
};

bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);

struct PrecomputedTransactionData
{
uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance;
uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance, hashRangeproofs;
bool ready = false;

template <class T>
Expand All @@ -143,12 +155,12 @@ static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;

template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, unsigned int flags, const PrecomputedTransactionData* cache = nullptr);

class BaseSignatureChecker
{
public:
virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const
{
return false;
}
Expand All @@ -158,7 +170,7 @@ class BaseSignatureChecker
return false;
}

virtual bool CheckSequence(const CScriptNum& nSequence) const
virtual bool CheckSequence(const CScriptNum& nSequence, unsigned int flags) const
{
return false;
}
Expand All @@ -181,9 +193,9 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
public:
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;
bool CheckSequence(const CScriptNum& nSequence, unsigned int flags) const override;
};

using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
Expand All @@ -198,4 +210,7 @@ int FindAndDelete(CScript& script, const CScript& b);

bool CheckMinimalPush(const std::vector<unsigned char>& data, opcodetype opcode);

/** Get the CSV mask that is currently active. */
uint32_t SequenceLocktimeMask(unsigned int flags);

#endif // BITCOIN_SCRIPT_INTERPRETER_H
Loading