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

Add SIGHASH_RANGEPROOF support #960

Merged
merged 3 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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
82 changes: 71 additions & 11 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_SIGHASH_RANGEPROOF) == SCRIPT_SIGHASH_RANGEPROOF) {
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 @@ -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_SIGHASH_RANGEPROOF) && !!(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());
roconnor-blockstream marked this conversation as resolved.
Show resolved Hide resolved
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);
Copy link
Member

Choose a reason for hiding this comment

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

nb this branch is not covered by the functional test

}
}
}
}

/** 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_SIGHASH_RANGEPROOF) && !!(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
18 changes: 14 additions & 4 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,22 @@ 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 SIGHASH_RANGEPROOF.
//
SCRIPT_SIGHASH_RANGEPROOF = (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 +153,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 Down Expand Up @@ -181,7 +191,7 @@ 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;
};
Expand Down
Loading