Skip to content

Commit

Permalink
Merge ElementsProject#960: Add SIGHASH_RANGEPROOF support
Browse files Browse the repository at this point in the history
b888f42 tests: Add test feature_sighash_rangeproof.py (Steven Roose)
691040a Add SIGHASH_RANGEPROOF support (Steven Roose)
236f0b1 tests: Replace CECKey with new ECKey impl from Bitcoin master (Steven Roose)

Pull request description:

  Add a new sighash flag that includes the rangeproof information in the signature. This avoids certain kinds of malleability in PSBT scenarios.

Tree-SHA512: 60b52ca88a64d81a38f5c70bae810e477f69e6402553b24fa22e59b9a8491c077168576187e7da88e525591933907ea77e937242af6d248908bfedbb8532bb33
  • Loading branch information
stevenroose committed Mar 2, 2021
2 parents 88067dd + b888f42 commit a993a7c
Show file tree
Hide file tree
Showing 23 changed files with 956 additions and 302 deletions.
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());
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_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

0 comments on commit a993a7c

Please sign in to comment.