mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
Merge #960: Add SIGHASH_RANGEPROOF support
b888f4227tests: Add test feature_sighash_rangeproof.py (Steven Roose)691040a63Add SIGHASH_RANGEPROOF support (Steven Roose)236f0b176tests: 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
This commit is contained in:
commit
a993a7c675
23 changed files with 934 additions and 280 deletions
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public:
|
|||
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);
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
|
|
@ -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++;
|
||||
|
|
@ -1466,13 +1473,15 @@ private:
|
|||
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) {}
|
||||
|
||||
|
|
@ -1529,11 +1538,23 @@ public:
|
|||
/** 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 */
|
||||
|
|
@ -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>
|
||||
|
|
@ -1610,6 +1646,7 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
|
|||
hashSequence = GetSequenceHash(txTo);
|
||||
hashIssuance = GetIssuanceHash(txTo);
|
||||
hashOutputs = GetOutputsHash(txTo);
|
||||
hashRangeproofs = GetRangeproofsHash(txTo);
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
@ -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())
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -181,7 +191,7 @@ protected:
|
|||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ typedef std::vector<unsigned char> valtype;
|
|||
|
||||
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
|
||||
|
||||
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
|
||||
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const
|
||||
{
|
||||
CKey key;
|
||||
if (!provider.GetKey(address, key))
|
||||
|
|
@ -27,7 +27,7 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
|
|||
if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
|
||||
return false;
|
||||
|
||||
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
|
||||
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, flags);
|
||||
if (!key.Sign(hash, vchSig))
|
||||
return false;
|
||||
vchSig.push_back((unsigned char)nHashType);
|
||||
|
|
@ -68,7 +68,7 @@ static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigd
|
|||
return provider.GetPubKey(address, pubkey);
|
||||
}
|
||||
|
||||
static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion)
|
||||
static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion, unsigned int flags)
|
||||
{
|
||||
CKeyID keyid = pubkey.GetID();
|
||||
const auto it = sigdata.signatures.find(keyid);
|
||||
|
|
@ -80,7 +80,7 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
|
|||
if (provider.GetKeyOrigin(keyid, info)) {
|
||||
sigdata.misc_pubkeys.emplace(keyid, std::make_pair(pubkey, std::move(info)));
|
||||
}
|
||||
if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
|
||||
if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion, flags)) {
|
||||
auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
|
||||
assert(i.second);
|
||||
return true;
|
||||
|
|
@ -97,8 +97,9 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
|
|||
* Returns false if scriptPubKey could not be completely satisfied.
|
||||
*/
|
||||
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
|
||||
std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
|
||||
{
|
||||
std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion,
|
||||
SignatureData& sigdata, unsigned int flags
|
||||
) {
|
||||
CScript scriptRet;
|
||||
uint160 h160;
|
||||
ret.clear();
|
||||
|
|
@ -114,7 +115,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
|||
case TX_WITNESS_UNKNOWN:
|
||||
return false;
|
||||
case TX_PUBKEY:
|
||||
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
|
||||
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion, flags)) return false;
|
||||
ret.push_back(std::move(sig));
|
||||
return true;
|
||||
case TX_PUBKEYHASH: {
|
||||
|
|
@ -125,7 +126,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
|||
sigdata.missing_pubkeys.push_back(keyID);
|
||||
return false;
|
||||
}
|
||||
if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false;
|
||||
if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion, flags)) return false;
|
||||
ret.push_back(std::move(sig));
|
||||
ret.push_back(ToByteVector(pubkey));
|
||||
return true;
|
||||
|
|
@ -145,7 +146,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
|||
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
|
||||
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
|
||||
CPubKey pubkey = CPubKey(vSolutions[i]);
|
||||
if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) {
|
||||
if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion, flags)) {
|
||||
ret.push_back(std::move(sig));
|
||||
}
|
||||
}
|
||||
|
|
@ -196,9 +197,14 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
|
|||
{
|
||||
if (sigdata.complete) return true;
|
||||
|
||||
// We will already activate SIGHASH_RANGEPROOF for signing. This means that
|
||||
// users using the flag before it activates will produce invalid signatures.
|
||||
unsigned int signFlags = SCRIPT_SIGHASH_RANGEPROOF;
|
||||
unsigned int verifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_SIGHASH_RANGEPROOF | additional_flags;
|
||||
|
||||
std::vector<valtype> result;
|
||||
txnouttype whichType;
|
||||
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
|
||||
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata, signFlags);
|
||||
bool P2SH = false;
|
||||
CScript subscript;
|
||||
sigdata.scriptWitness.stack.clear();
|
||||
|
|
@ -210,7 +216,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
|
|||
// and then the serialized subscript:
|
||||
subscript = CScript(result[0].begin(), result[0].end());
|
||||
sigdata.redeem_script = subscript;
|
||||
solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH;
|
||||
solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata, signFlags) && whichType != TX_SCRIPTHASH;
|
||||
P2SH = true;
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +225,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
|
|||
CScript witnessscript;
|
||||
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
txnouttype subType;
|
||||
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
|
||||
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata, signFlags);
|
||||
sigdata.scriptWitness.stack = result;
|
||||
sigdata.witness = true;
|
||||
result.clear();
|
||||
|
|
@ -229,7 +235,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
|
|||
CScript witnessscript(result[0].begin(), result[0].end());
|
||||
sigdata.witness_script = witnessscript;
|
||||
txnouttype subType;
|
||||
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
|
||||
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata, signFlags) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
|
||||
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
|
||||
sigdata.scriptWitness.stack = result;
|
||||
sigdata.witness = true;
|
||||
|
|
@ -244,7 +250,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
|
|||
sigdata.scriptSig = PushAll(result);
|
||||
|
||||
// Test solution
|
||||
sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS|additional_flags, creator.Checker());
|
||||
sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, verifyFlags, creator.Checker());
|
||||
return sigdata.complete;
|
||||
}
|
||||
|
||||
|
|
@ -256,12 +262,12 @@ private:
|
|||
|
||||
public:
|
||||
SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
|
||||
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 SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
bool SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const
|
||||
{
|
||||
if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
|
||||
if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion, flags)) {
|
||||
CPubKey pubkey(vchPubKey);
|
||||
sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
|
||||
return true;
|
||||
|
|
@ -330,6 +336,8 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
|
|||
stack.witness.clear();
|
||||
sigversion = SigVersion::WITNESS_V0;
|
||||
}
|
||||
// We enable SIGHASH_RANGEPROOF for signing.
|
||||
unsigned int flags = SCRIPT_SIGHASH_RANGEPROOF;
|
||||
if (script_type == TX_MULTISIG && !stack.script.empty()) {
|
||||
// Build a map of pubkey -> signature by matching sigs to pubkeys:
|
||||
assert(solutions.size() > 1);
|
||||
|
|
@ -339,7 +347,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
|
|||
for (unsigned int i = last_success_key; i < num_pubkeys; ++i) {
|
||||
const valtype& pubkey = solutions[i+1];
|
||||
// We either have a signature for this pubkey, or we have found a signature and it is valid
|
||||
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
|
||||
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion, flags)) {
|
||||
last_success_key = i + 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -404,7 +412,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
|
|||
{
|
||||
public:
|
||||
DummySignatureChecker() {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override { return true; }
|
||||
};
|
||||
const DummySignatureChecker DUMMY_CHECKER;
|
||||
|
||||
|
|
@ -415,7 +423,7 @@ private:
|
|||
public:
|
||||
DummySignatureCreator(char r_len, char s_len) : m_r_len(r_len), m_s_len(s_len) {}
|
||||
const BaseSignatureChecker& Checker() const override { return DUMMY_CHECKER; }
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override
|
||||
{
|
||||
// Create a dummy signature that is a valid DER-encoding
|
||||
vchSig.assign(m_r_len + m_s_len + 7, '\000');
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public:
|
|||
virtual const BaseSignatureChecker& Checker() const =0;
|
||||
|
||||
/** Create a singular (non-script) signature. */
|
||||
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
|
||||
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const =0;
|
||||
};
|
||||
|
||||
/** A signature creator for transactions. */
|
||||
|
|
@ -109,7 +109,7 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
|
|||
public:
|
||||
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn = SIGHASH_ALL);
|
||||
const BaseSignatureChecker& Checker() const override { return checker; }
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override;
|
||||
};
|
||||
|
||||
/** A signature creator that just produces 71-byte empty signatures. */
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup)
|
|||
static CScript
|
||||
sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction, int whichIn)
|
||||
{
|
||||
uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE, 0);
|
||||
|
||||
CScript result;
|
||||
result << OP_0; // CHECKMULTISIG bug workaround
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ public:
|
|||
|
||||
TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32, SigVersion sigversion = SigVersion::BASE, CAmount amount = 0)
|
||||
{
|
||||
uint256 hash = SignatureHash(script, spendTx, 0, nHashType, amount, sigversion);
|
||||
uint256 hash = SignatureHash(script, spendTx, 0, nHashType, amount, sigversion, 0);
|
||||
std::vector<unsigned char> vchSig, r, s;
|
||||
uint32_t iter = 0;
|
||||
do {
|
||||
|
|
@ -1063,7 +1063,7 @@ BOOST_AUTO_TEST_CASE(script_cltv_truncated)
|
|||
static CScript
|
||||
sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction)
|
||||
{
|
||||
uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0);
|
||||
|
||||
CScript result;
|
||||
//
|
||||
|
|
@ -1267,15 +1267,15 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
|
|||
|
||||
// A couple of partially-signed versions:
|
||||
std::vector<unsigned char> sig1;
|
||||
uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK(keys[0].Sign(hash1, sig1));
|
||||
sig1.push_back(SIGHASH_ALL);
|
||||
std::vector<unsigned char> sig2;
|
||||
uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, SigVersion::BASE);
|
||||
uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK(keys[1].Sign(hash2, sig2));
|
||||
sig2.push_back(SIGHASH_NONE);
|
||||
std::vector<unsigned char> sig3;
|
||||
uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, SigVersion::BASE);
|
||||
uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK(keys[2].Sign(hash3, sig3));
|
||||
sig3.push_back(SIGHASH_SINGLE);
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ BOOST_AUTO_TEST_CASE(sighash_test)
|
|||
int nRandomTests = 50000;
|
||||
#endif
|
||||
for (int i=0; i<nRandomTests; i++) {
|
||||
int nHashType = InsecureRand32();
|
||||
// In randomized test, we disable SIGHASH_RANGEPROOF.
|
||||
int nHashType = InsecureRand32() & ~SIGHASH_RANGEPROOF;
|
||||
CMutableTransaction txTo;
|
||||
RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
|
||||
CScript scriptCode;
|
||||
|
|
@ -138,7 +139,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
|
|||
|
||||
uint256 sh, sho;
|
||||
sho = SignatureHashOld(scriptCode, CTransaction(txTo), nIn, nHashType);
|
||||
sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE);
|
||||
sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE, SCRIPT_SIGHASH_RANGEPROOF);
|
||||
#if defined(PRINT_SIGHASH_JSON)
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << txTo;
|
||||
|
|
@ -204,7 +205,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
|
|||
continue;
|
||||
}
|
||||
|
||||
sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE);
|
||||
sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
|||
|
||||
// Sign:
|
||||
std::vector<unsigned char> vchSig;
|
||||
uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
|
||||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
spends[i].vin[0].scriptSig << vchSig;
|
||||
|
|
@ -183,7 +183,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
|
|||
// Sign, with a non-DER signature
|
||||
{
|
||||
std::vector<unsigned char> vchSig;
|
||||
uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
|
||||
vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER
|
||||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
|
|
@ -256,7 +256,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
|
|||
|
||||
// Sign
|
||||
std::vector<unsigned char> vchSig;
|
||||
uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
|
||||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
|
||||
|
|
@ -284,7 +284,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
|
|||
|
||||
// Sign
|
||||
std::vector<unsigned char> vchSig;
|
||||
uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
|
||||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
|
||||
|
|
|
|||
|
|
@ -969,7 +969,14 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
|||
}
|
||||
}
|
||||
|
||||
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
|
||||
// Temporarily add additional script flags based on the activation of
|
||||
// Dynamic Federations. This can be included in the
|
||||
// STANDARD_LOCKTIME_VERIFY_FLAGS in a release post-activation.
|
||||
if (IsDynaFedEnabled(chainActive.Tip(), chainparams.GetConsensus())) {
|
||||
scriptVerifyFlags |= SCRIPT_SIGHASH_RANGEPROOF;
|
||||
}
|
||||
|
||||
// Check against previous transactions
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
|
|
@ -1902,6 +1909,10 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
|
|||
flags |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
}
|
||||
|
||||
if (IsDynaFedEnabled(pindex->pprev, consensusparams)) {
|
||||
flags |= SCRIPT_SIGHASH_RANGEPROOF;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ Start three nodes:
|
|||
import time
|
||||
|
||||
from test_framework.blocktools import (create_block, create_coinbase)
|
||||
from test_framework.key import CECKey
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
CBlockHeader,
|
||||
COutPoint,
|
||||
|
|
@ -104,9 +104,9 @@ class AssumeValidTest(BitcoinTestFramework):
|
|||
self.blocks = []
|
||||
|
||||
# Get a pubkey for the coinbase TXO
|
||||
coinbase_key = CECKey()
|
||||
coinbase_key.set_secretbytes(b"horsebattery")
|
||||
coinbase_pubkey = coinbase_key.get_pubkey()
|
||||
coinbase_key = ECKey()
|
||||
coinbase_key.set(b'\x09' * 32, True)
|
||||
coinbase_pubkey = coinbase_key.get_pubkey().get_bytes()
|
||||
|
||||
# Create the first block with a coinbase output to our key
|
||||
height = self.nodes[0].getblockcount() + 1
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from test_framework.blocktools import (
|
|||
get_legacy_sigopcount_block,
|
||||
MAX_BLOCK_SIGOPS,
|
||||
)
|
||||
from test_framework.key import CECKey
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
CBlock,
|
||||
COIN,
|
||||
|
|
@ -85,9 +85,9 @@ class FullBlockTest(BitcoinTestFramework):
|
|||
self.bootstrap_p2p() # Add one p2p connection to the node
|
||||
|
||||
self.block_heights = {}
|
||||
self.coinbase_key = CECKey()
|
||||
self.coinbase_key.set_secretbytes(b"horsebattery")
|
||||
self.coinbase_pubkey = self.coinbase_key.get_pubkey()
|
||||
self.coinbase_key = ECKey()
|
||||
self.coinbase_key.set(b'\x09' * 32, True)
|
||||
self.coinbase_pubkey = self.coinbase_key.get_pubkey().get_bytes()
|
||||
self.tip = None
|
||||
self.blocks = {}
|
||||
self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
|
||||
|
|
@ -535,7 +535,7 @@ class FullBlockTest(BitcoinTestFramework):
|
|||
tx.vout.append(CTxOut(b39.vtx[i].vout[0].nValue.getAmount())) # fee
|
||||
# Note: must pass the redeem_script (not p2sh_script) to the signature hash function
|
||||
(sighash, err) = SignatureHash(redeem_script, tx, 1, SIGHASH_ALL)
|
||||
sig = self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))
|
||||
sig = self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))
|
||||
scriptSig = CScript([sig, redeem_script])
|
||||
|
||||
tx.vin[1].scriptSig = scriptSig
|
||||
|
|
@ -1321,7 +1321,7 @@ class FullBlockTest(BitcoinTestFramework):
|
|||
tx.vin[0].scriptSig = CScript()
|
||||
return
|
||||
(sighash, err) = SignatureHash(spend_tx.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL)
|
||||
tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
|
||||
tx.vin[0].scriptSig = CScript([self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))])
|
||||
|
||||
def create_and_sign_transaction(self, spend_tx, value, script=CScript([OP_TRUE])):
|
||||
tx = self.create_tx(spend_tx, 0, value, script)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ def make_signblockscript(num_nodes, required_signers, keys):
|
|||
for i in range(num_nodes):
|
||||
k = keys[i]
|
||||
script += "21"
|
||||
script += codecs.encode(k.get_pubkey(), 'hex_codec').decode("utf-8")
|
||||
script += codecs.encode(k.get_pubkey().get_bytes(), 'hex_codec').decode("utf-8")
|
||||
script += "{}".format(50 + num_nodes) # num keys
|
||||
script += "ae" # OP_CHECKMULTISIG
|
||||
return script
|
||||
|
|
@ -66,11 +66,10 @@ class BlockSignTest(BitcoinTestFramework):
|
|||
self.keys = []
|
||||
self.wifs = []
|
||||
for i in range(num_keys):
|
||||
k = key.CECKey()
|
||||
pk_bytes = hashlib.sha256(str(random.getrandbits(256)).encode('utf-8')).digest()
|
||||
k.set_secretbytes(pk_bytes)
|
||||
k.set_compressed(True)
|
||||
w = wif(pk_bytes)
|
||||
k = key.ECKey()
|
||||
sk_bytes = hashlib.sha256(str(random.getrandbits(256)).encode('utf-8')).digest()
|
||||
k.set(sk_bytes, True)
|
||||
w = wif(sk_bytes)
|
||||
self.keys.append(k)
|
||||
self.wifs.append(w)
|
||||
|
||||
|
|
|
|||
334
test/functional/feature_sighash_rangeproof.py
Executable file
334
test/functional/feature_sighash_rangeproof.py
Executable file
|
|
@ -0,0 +1,334 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 The Elements Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
"""
|
||||
Test the post-dynafed elements-only SIGHASH_RANGEPROOF sighash flag.
|
||||
"""
|
||||
|
||||
import struct
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.script import (
|
||||
hash160,
|
||||
SignatureHash,
|
||||
SegwitVersion1SignatureHash,
|
||||
SIGHASH_ALL,
|
||||
SIGHASH_SINGLE,
|
||||
SIGHASH_NONE,
|
||||
SIGHASH_ANYONECANPAY,
|
||||
SIGHASH_RANGEPROOF,
|
||||
CScript,
|
||||
CScriptOp,
|
||||
FindAndDelete,
|
||||
OP_CODESEPARATOR,
|
||||
OP_CHECKSIG,
|
||||
OP_DUP,
|
||||
OP_EQUALVERIFY,
|
||||
OP_HASH160,
|
||||
)
|
||||
from test_framework.key import ECKey
|
||||
|
||||
from test_framework.messages import (
|
||||
CBlock,
|
||||
CTransaction,
|
||||
CTxOut,
|
||||
FromHex,
|
||||
WitToHex,
|
||||
hash256, uint256_from_str, ser_uint256, ser_string, ser_vector
|
||||
)
|
||||
|
||||
from test_framework import util
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
hex_str_to_bytes,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
from test_framework.blocktools import add_witness_commitment
|
||||
|
||||
def get_p2pkh_script(pubkeyhash):
|
||||
"""Get the script associated with a P2PKH."""
|
||||
return CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)])
|
||||
|
||||
def SignatureHash_legacy(script, txTo, inIdx, hashtype):
|
||||
"""
|
||||
This method is identical to the regular `SignatureHash` method,
|
||||
but without support for SIGHASH_RANGEPROOF.
|
||||
So basically it's the old version of the method from before the
|
||||
new sighash flag was added.
|
||||
"""
|
||||
HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
if inIdx >= len(txTo.vin):
|
||||
return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
|
||||
txtmp = CTransaction(txTo)
|
||||
|
||||
for txin in txtmp.vin:
|
||||
txin.scriptSig = b''
|
||||
txtmp.vin[inIdx].scriptSig = FindAndDelete(script, CScript([OP_CODESEPARATOR]))
|
||||
|
||||
if (hashtype & 0x1f) == SIGHASH_NONE:
|
||||
txtmp.vout = []
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
if i != inIdx:
|
||||
txtmp.vin[i].nSequence = 0
|
||||
|
||||
elif (hashtype & 0x1f) == SIGHASH_SINGLE:
|
||||
outIdx = inIdx
|
||||
if outIdx >= len(txtmp.vout):
|
||||
return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))
|
||||
|
||||
tmp = txtmp.vout[outIdx]
|
||||
txtmp.vout = []
|
||||
for i in range(outIdx):
|
||||
txtmp.vout.append(CTxOut(-1))
|
||||
txtmp.vout.append(tmp)
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
if i != inIdx:
|
||||
txtmp.vin[i].nSequence = 0
|
||||
|
||||
if hashtype & SIGHASH_ANYONECANPAY:
|
||||
tmp = txtmp.vin[inIdx]
|
||||
txtmp.vin = []
|
||||
txtmp.vin.append(tmp)
|
||||
|
||||
# sighash serialization is different from non-witness serialization
|
||||
# do manual sighash serialization:
|
||||
s = b""
|
||||
s += struct.pack("<i", txtmp.nVersion)
|
||||
s += ser_vector(txtmp.vin)
|
||||
s += ser_vector(txtmp.vout)
|
||||
s += struct.pack("<I", txtmp.nLockTime)
|
||||
|
||||
# add sighash type
|
||||
s += struct.pack(b"<I", hashtype)
|
||||
|
||||
hash = hash256(s)
|
||||
|
||||
return (hash, None)
|
||||
|
||||
def SegwitVersion1SignatureHash_legacy(script, txTo, inIdx, hashtype, amount):
|
||||
"""
|
||||
This method is identical to the regular `SegwitVersion1SignatureHash`
|
||||
method, but without support for SIGHASH_RANGEPROOF.
|
||||
So basically it's the old version of the method from before the
|
||||
new sighash flag was added.
|
||||
"""
|
||||
|
||||
hashPrevouts = 0
|
||||
hashSequence = 0
|
||||
hashIssuance = 0
|
||||
hashOutputs = 0
|
||||
|
||||
if not (hashtype & SIGHASH_ANYONECANPAY):
|
||||
serialize_prevouts = bytes()
|
||||
for i in txTo.vin:
|
||||
serialize_prevouts += i.prevout.serialize()
|
||||
hashPrevouts = uint256_from_str(hash256(serialize_prevouts))
|
||||
|
||||
if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
|
||||
serialize_sequence = bytes()
|
||||
for i in txTo.vin:
|
||||
serialize_sequence += struct.pack("<I", i.nSequence)
|
||||
hashSequence = uint256_from_str(hash256(serialize_sequence))
|
||||
|
||||
if not (hashtype & SIGHASH_ANYONECANPAY):
|
||||
serialize_issuance = bytes()
|
||||
# TODO actually serialize issuances
|
||||
for _ in txTo.vin:
|
||||
serialize_issuance += b'\x00'
|
||||
hashIssuance = uint256_from_str(hash256(serialize_issuance))
|
||||
|
||||
if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
|
||||
serialize_outputs = bytes()
|
||||
for o in txTo.vout:
|
||||
serialize_outputs += o.serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
|
||||
serialize_outputs = txTo.vout[inIdx].serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
ss = bytes()
|
||||
ss += struct.pack("<i", txTo.nVersion)
|
||||
ss += ser_uint256(hashPrevouts)
|
||||
ss += ser_uint256(hashSequence)
|
||||
ss += ser_uint256(hashIssuance)
|
||||
ss += txTo.vin[inIdx].prevout.serialize()
|
||||
ss += ser_string(script)
|
||||
ss += amount.serialize()
|
||||
ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
|
||||
ss += ser_uint256(hashOutputs)
|
||||
ss += struct.pack("<i", txTo.nLockTime)
|
||||
ss += struct.pack("<I", hashtype)
|
||||
|
||||
return hash256(ss)
|
||||
|
||||
|
||||
class SighashRangeproofTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 3
|
||||
# We want to test activation of dynafed
|
||||
args = ["-con_dyna_deploy_start=1000", "-blindedaddresses=1", "-initialfreecoins=2100000000000000", "-con_blocksubsidy=0", "-con_connect_genesis_outputs=1", "-txindex=1"]
|
||||
self.extra_args = [args] * self.num_nodes
|
||||
self.extra_args[0].append("-anyonecanspendaremine=1") # first node gets the coins
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def prepare_tx_signed_with_sighash(self, address_type, sighash_rangeproof_aware):
|
||||
# Create a tx that is signed with a specific version of the sighash
|
||||
# method.
|
||||
# If `sighash_rangeproof_aware` is
|
||||
# true, the sighash will contain the rangeproofs if SIGHASH_RANGEPROOF is set
|
||||
# false, the sighash will NOT contain the rangeproofs if SIGHASH_RANGEPROOF is set
|
||||
|
||||
addr = self.nodes[1].getnewaddress("", address_type)
|
||||
assert len(self.nodes[1].getaddressinfo(addr)["confidential_key"]) > 0
|
||||
self.nodes[0].sendtoaddress(addr, 1.0)
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
utxo = self.nodes[1].listunspent(1, 1, [addr])[0]
|
||||
utxo_tx = FromHex(CTransaction(), self.nodes[1].getrawtransaction(utxo["txid"]))
|
||||
utxo_spk = CScript(hex_str_to_bytes(utxo["scriptPubKey"]))
|
||||
utxo_value = utxo_tx.vout[utxo["vout"]].nValue
|
||||
|
||||
assert len(utxo["amountblinder"]) > 0
|
||||
sink_addr = self.nodes[2].getnewaddress()
|
||||
unsigned_hex = self.nodes[1].createrawtransaction(
|
||||
[{"txid": utxo["txid"], "vout": utxo["vout"]}],
|
||||
{sink_addr: 0.9, "fee": 0.1}
|
||||
)
|
||||
blinded_hex = self.nodes[1].blindrawtransaction(unsigned_hex)
|
||||
blinded_tx = FromHex(CTransaction(), blinded_hex)
|
||||
signed_hex = self.nodes[1].signrawtransactionwithwallet(blinded_hex)["hex"]
|
||||
signed_tx = FromHex(CTransaction(), signed_hex)
|
||||
|
||||
# Make sure that the tx the node produced is always valid.
|
||||
test_accept = self.nodes[0].testmempoolaccept([signed_hex])[0]
|
||||
assert test_accept["allowed"], "not accepted: {}".format(test_accept["reject-reason"])
|
||||
|
||||
# Prepare the keypair we need to re-sign the tx.
|
||||
wif = self.nodes[1].dumpprivkey(addr)
|
||||
privkey = ECKey()
|
||||
privkey.set_wif(wif)
|
||||
pubkey = privkey.get_pubkey()
|
||||
|
||||
# Now we need to replace the signature with an equivalent one with the new sighash set.
|
||||
hashtype = SIGHASH_ALL | SIGHASH_RANGEPROOF
|
||||
if address_type == "legacy":
|
||||
if sighash_rangeproof_aware:
|
||||
(sighash, _) = SignatureHash(utxo_spk, blinded_tx, 0, hashtype)
|
||||
else:
|
||||
(sighash, _) = SignatureHash_legacy(utxo_spk, blinded_tx, 0, hashtype)
|
||||
signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode('latin-1')
|
||||
assert len(signature) <= 0xfc
|
||||
assert len(pubkey.get_bytes()) <= 0xfc
|
||||
signed_tx.vin[0].scriptSig = CScript(
|
||||
struct.pack("<B", len(signature)) + signature
|
||||
+ struct.pack("<B", len(pubkey.get_bytes())) + pubkey.get_bytes()
|
||||
)
|
||||
elif address_type == "bech32" or address_type == "p2sh-segwit":
|
||||
assert signed_tx.wit.vtxinwit[0].scriptWitness.stack[1] == pubkey.get_bytes()
|
||||
pubkeyhash = hash160(pubkey.get_bytes())
|
||||
script = get_p2pkh_script(pubkeyhash)
|
||||
if sighash_rangeproof_aware:
|
||||
sighash = SegwitVersion1SignatureHash(script, blinded_tx, 0, hashtype, utxo_value)
|
||||
else:
|
||||
sighash = SegwitVersion1SignatureHash_legacy(script, blinded_tx, 0, hashtype, utxo_value)
|
||||
signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode('latin-1')
|
||||
signed_tx.wit.vtxinwit[0].scriptWitness.stack[0] = signature
|
||||
else:
|
||||
assert False
|
||||
|
||||
signed_tx.rehash()
|
||||
return signed_tx
|
||||
|
||||
def assert_tx_standard(self, tx, assert_standard=True):
|
||||
# Test the standardness of the tx by submitting it to the mempool.
|
||||
|
||||
test_accept = self.nodes[0].testmempoolaccept([WitToHex(tx)])[0]
|
||||
if assert_standard:
|
||||
assert test_accept["allowed"], "tx was not accepted: {}".format(test_accept["reject-reason"])
|
||||
else:
|
||||
assert not test_accept["allowed"], "tx was accepted"
|
||||
|
||||
def assert_tx_valid(self, tx, assert_valid=True):
|
||||
# Test the validity of the transaction by manually mining a block that contains the tx.
|
||||
|
||||
block = FromHex(CBlock(), self.nodes[2].getnewblockhex())
|
||||
assert len(block.vtx) > 0
|
||||
block.vtx.append(tx)
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
add_witness_commitment(block)
|
||||
block.solve()
|
||||
block_hex = WitToHex(block)
|
||||
|
||||
# First test the testproposed block RPC.
|
||||
if assert_valid:
|
||||
self.nodes[0].testproposedblock(block_hex)
|
||||
else:
|
||||
assert_raises_rpc_error(-25, "block-validation-failed", self.nodes[0].testproposedblock, block_hex)
|
||||
|
||||
# Then try submit the block and check if it was accepted or not.
|
||||
pre = self.nodes[0].getblockcount()
|
||||
self.nodes[0].submitblock(block_hex)
|
||||
post = self.nodes[0].getblockcount()
|
||||
|
||||
if assert_valid:
|
||||
# assert block was accepted
|
||||
assert pre < post
|
||||
else:
|
||||
# assert block was not accepted
|
||||
assert pre == post
|
||||
|
||||
def run_test(self):
|
||||
util.node_fastmerkle = self.nodes[0]
|
||||
ADDRESS_TYPES = ["legacy", "bech32", "p2sh-segwit"]
|
||||
|
||||
# Different test scenarios.
|
||||
# - before activation, using the flag is non-standard
|
||||
# - before activation, using the flag but a non-flag-aware signature is legal
|
||||
# - after activation, using the flag but a non-flag-aware signature is illegal
|
||||
# - after activation, using the flag is standard (and thus also legal)
|
||||
|
||||
# Mine come coins for node 0.
|
||||
self.nodes[0].generate(200)
|
||||
self.sync_all()
|
||||
|
||||
# Ensure that if we use the SIGHASH_RANGEPROOF flag before it's activated,
|
||||
# - the tx is not accepted in the mempool and
|
||||
# - the tx is accepted if manually mined in a block
|
||||
for address_type in ADDRESS_TYPES:
|
||||
self.log.info("Pre-activation for {} address".format(address_type))
|
||||
tx = self.prepare_tx_signed_with_sighash(address_type, False)
|
||||
self.assert_tx_standard(tx, False)
|
||||
self.assert_tx_valid(tx, True)
|
||||
|
||||
# Activate dynafed (nb of blocks taken from dynafed activation test)
|
||||
self.nodes[0].generate(1006 + 1 + 144 + 144)
|
||||
assert_equal(self.nodes[0].getblockchaininfo()["bip9_softforks"]["dynafed"]["status"], "active")
|
||||
self.sync_all()
|
||||
|
||||
# Test that the use of SIGHASH_RANGEPROOF is legal and standard
|
||||
# after activation.
|
||||
for address_type in ADDRESS_TYPES:
|
||||
self.log.info("Post-activation for {} address".format(address_type))
|
||||
tx = self.prepare_tx_signed_with_sighash(address_type, True)
|
||||
self.assert_tx_standard(tx, True)
|
||||
self.assert_tx_valid(tx, True)
|
||||
|
||||
# Ensure that if we then use the old sighash algorith that doesn't hash
|
||||
# the rangeproofs, the signature is no longer valid.
|
||||
for address_type in ADDRESS_TYPES:
|
||||
self.log.info("Post-activation invalid sighash for {} address".format(address_type))
|
||||
tx = self.prepare_tx_signed_with_sighash(address_type, False)
|
||||
self.assert_tx_standard(tx, False)
|
||||
self.assert_tx_valid(tx, False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
SighashRangeproofTest().main()
|
||||
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
"""
|
||||
|
||||
from test_framework.messages import CTransaction, CBlock, ser_uint256, FromHex, uint256_from_str, CTxOut, ToHex, CTxIn, COutPoint, OUTPOINT_ISSUANCE_FLAG, ser_string
|
||||
from test_framework.messages import CTransaction, CBlock, ser_uint256, FromHex, uint256_from_str, CTxOut, ToHex, WitToHex, CTxIn, COutPoint, OUTPOINT_ISSUANCE_FLAG, ser_string
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal, bytes_to_hex_str, hex_str_to_bytes, assert_raises_rpc_error, assert_greater_than
|
||||
from test_framework import util
|
||||
|
|
@ -126,9 +126,6 @@ class TxWitnessTest(BitcoinTestFramework):
|
|||
|
||||
def test_coinbase_witness(self):
|
||||
|
||||
def WitToHex(obj):
|
||||
return bytes_to_hex_str(obj.serialize(with_witness=True))
|
||||
|
||||
block = self.nodes[0].getnewblockhex()
|
||||
block_struct = FromHex(CBlock(), block)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import struct
|
|||
import time
|
||||
|
||||
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, WITNESS_COMMITMENT_HEADER
|
||||
from test_framework.key import CECKey, CPubKey
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
COIN,
|
||||
BIP125_SEQUENCE_NUMBER,
|
||||
|
|
@ -109,7 +109,7 @@ def get_p2pkh_script(pubkeyhash):
|
|||
def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
|
||||
"""Add signature for a P2PK witness program."""
|
||||
tx_hash = SegwitVersion1SignatureHash(script, tx_to, in_idx, hashtype, CTxOutValue(value))
|
||||
signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1')
|
||||
signature = key.sign_ecdsa(tx_hash) + chr(hashtype).encode('latin-1')
|
||||
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script]
|
||||
tx_to.rehash()
|
||||
|
||||
|
|
@ -1527,10 +1527,9 @@ class SegWitTest(BitcoinTestFramework):
|
|||
|
||||
# Segwit transactions using uncompressed pubkeys are not accepted
|
||||
# under default policy, but should still pass consensus.
|
||||
key = CECKey()
|
||||
key.set_secretbytes(b"9")
|
||||
key.set_compressed(False)
|
||||
pubkey = CPubKey(key.get_pubkey())
|
||||
key = ECKey()
|
||||
key.set(b'\x09' * 32, False)
|
||||
pubkey = key.get_pubkey().get_bytes()
|
||||
assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey
|
||||
|
||||
utxo = self.utxo.pop(0)
|
||||
|
|
@ -1562,7 +1561,7 @@ class SegWitTest(BitcoinTestFramework):
|
|||
tx2.vout.append(CTxOut(1000)) # fee
|
||||
script = get_p2pkh_script(pubkeyhash)
|
||||
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
|
||||
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
tx2.wit.vtxinwit.append(CTxInWitness())
|
||||
tx2.wit.vtxinwit[0].scriptWitness.stack = [signature, pubkey]
|
||||
tx2.rehash()
|
||||
|
|
@ -1619,7 +1618,7 @@ class SegWitTest(BitcoinTestFramework):
|
|||
tx5.vout.append(CTxOut(tx4.vout[0].nValue.getAmount() - 1000, CScript([OP_TRUE])))
|
||||
tx5.vout.append(CTxOut(1000)) # fee
|
||||
(sig_hash, err) = SignatureHash(script_pubkey, tx5, 0, SIGHASH_ALL)
|
||||
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
tx5.vin[0].scriptSig = CScript([signature, pubkey])
|
||||
tx5.rehash()
|
||||
# Should pass policy and consensus.
|
||||
|
|
@ -1632,9 +1631,9 @@ class SegWitTest(BitcoinTestFramework):
|
|||
@subtest
|
||||
def test_signature_version_1(self):
|
||||
|
||||
key = CECKey()
|
||||
key.set_secretbytes(b"9")
|
||||
pubkey = CPubKey(key.get_pubkey())
|
||||
key = ECKey()
|
||||
key.set(b'\x09' * 32, True)
|
||||
pubkey = key.get_pubkey().get_bytes()
|
||||
|
||||
witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
|
||||
witness_hash = sha256(witness_program)
|
||||
|
|
@ -1774,7 +1773,7 @@ class SegWitTest(BitcoinTestFramework):
|
|||
|
||||
script = get_p2pkh_script(pubkeyhash)
|
||||
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
|
||||
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
|
||||
# Check that we can't have a scriptSig
|
||||
tx2.vin[0].scriptSig = CScript([signature, pubkey])
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"""Encode and decode BASE58, P2PKH and P2SH addresses."""
|
||||
|
||||
from .script import hash256, hash160, sha256, CScript, OP_0
|
||||
from .util import bytes_to_hex_str, hex_str_to_bytes
|
||||
from .util import bytes_to_hex_str, hex_str_to_bytes, assert_equal
|
||||
|
||||
from . import segwit_addr
|
||||
|
||||
|
|
@ -29,7 +29,36 @@ def byte_to_base58(b, version):
|
|||
str = str[2:]
|
||||
return result
|
||||
|
||||
# TODO: def base58_decode
|
||||
|
||||
def base58_to_byte(s):
|
||||
"""Converts a base58-encoded string to its data and version.
|
||||
|
||||
Throws if the base58 checksum is invalid."""
|
||||
if not s:
|
||||
return b''
|
||||
n = 0
|
||||
for c in s:
|
||||
n *= 58
|
||||
assert c in chars
|
||||
digit = chars.index(c)
|
||||
n += digit
|
||||
h = '%x' % n
|
||||
if len(h) % 2:
|
||||
h = '0' + h
|
||||
res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
|
||||
pad = 0
|
||||
for c in s:
|
||||
if c == chars[0]:
|
||||
pad += 1
|
||||
else:
|
||||
break
|
||||
res = b'\x00' * pad + res
|
||||
|
||||
# Assert if the checksum is invalid
|
||||
assert_equal(hash256(res[:-4])[:4], res[-4:])
|
||||
|
||||
return res[1:-4], int(res[0])
|
||||
|
||||
|
||||
def keyhash_to_p2pkh(hash, main = False):
|
||||
assert (len(hash) == 20)
|
||||
|
|
|
|||
|
|
@ -1,226 +1,379 @@
|
|||
# Copyright (c) 2011 Sam Rushing
|
||||
"""ECC secp256k1 OpenSSL wrapper.
|
||||
# Copyright (c) 2019 Pieter Wuille
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test-only secp256k1 elliptic curve implementation
|
||||
|
||||
WARNING: This module does not mlock() secrets; your private keys may end up on
|
||||
disk in swap! Use with caution!
|
||||
WARNING: This code is slow, uses bad randomness, does not properly protect
|
||||
keys, and is trivially vulnerable to side channel attacks. Do not use for
|
||||
anything but tests."""
|
||||
import random
|
||||
|
||||
This file is modified from python-bitcoinlib.
|
||||
"""
|
||||
from .util import modinv
|
||||
from .address import base58_to_byte
|
||||
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import hashlib
|
||||
def jacobi_symbol(n, k):
|
||||
"""Compute the Jacobi symbol of n modulo k
|
||||
|
||||
ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library ('ssl') or 'libeay32')
|
||||
See http://en.wikipedia.org/wiki/Jacobi_symbol
|
||||
|
||||
ssl.BN_new.restype = ctypes.c_void_p
|
||||
ssl.BN_new.argtypes = []
|
||||
For our application k is always prime, so this is the same as the Legendre symbol."""
|
||||
assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k"
|
||||
n %= k
|
||||
t = 0
|
||||
while n != 0:
|
||||
while n & 1 == 0:
|
||||
n >>= 1
|
||||
r = k & 7
|
||||
t ^= (r == 3 or r == 5)
|
||||
n, k = k, n
|
||||
t ^= (n & k & 3 == 3)
|
||||
n = n % k
|
||||
if k == 1:
|
||||
return -1 if t else 1
|
||||
return 0
|
||||
|
||||
ssl.BN_bin2bn.restype = ctypes.c_void_p
|
||||
ssl.BN_bin2bn.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p]
|
||||
def modsqrt(a, p):
|
||||
"""Compute the square root of a modulo p when p % 4 = 3.
|
||||
|
||||
ssl.BN_CTX_free.restype = None
|
||||
ssl.BN_CTX_free.argtypes = [ctypes.c_void_p]
|
||||
The Tonelli-Shanks algorithm can be used. See https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm
|
||||
|
||||
ssl.BN_CTX_new.restype = ctypes.c_void_p
|
||||
ssl.BN_CTX_new.argtypes = []
|
||||
Limiting this function to only work for p % 4 = 3 means we don't need to
|
||||
iterate through the loop. The highest n such that p - 1 = 2^n Q with Q odd
|
||||
is n = 1. Therefore Q = (p-1)/2 and sqrt = a^((Q+1)/2) = a^((p+1)/4)
|
||||
|
||||
ssl.ECDH_compute_key.restype = ctypes.c_int
|
||||
ssl.ECDH_compute_key.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p]
|
||||
secp256k1's is defined over field of size 2**256 - 2**32 - 977, which is 3 mod 4.
|
||||
"""
|
||||
if p % 4 != 3:
|
||||
raise NotImplementedError("modsqrt only implemented for p % 4 = 3")
|
||||
sqrt = pow(a, (p + 1)//4, p)
|
||||
if pow(sqrt, 2, p) == a % p:
|
||||
return sqrt
|
||||
return None
|
||||
|
||||
ssl.ECDSA_sign.restype = ctypes.c_int
|
||||
ssl.ECDSA_sign.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
class EllipticCurve:
|
||||
def __init__(self, p, a, b):
|
||||
"""Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p)."""
|
||||
self.p = p
|
||||
self.a = a % p
|
||||
self.b = b % p
|
||||
|
||||
ssl.ECDSA_verify.restype = ctypes.c_int
|
||||
ssl.ECDSA_verify.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
|
||||
def affine(self, p1):
|
||||
"""Convert a Jacobian point tuple p1 to affine form, or None if at infinity.
|
||||
|
||||
ssl.EC_KEY_free.restype = None
|
||||
ssl.EC_KEY_free.argtypes = [ctypes.c_void_p]
|
||||
An affine point is represented as the Jacobian (x, y, 1)"""
|
||||
x1, y1, z1 = p1
|
||||
if z1 == 0:
|
||||
return None
|
||||
inv = modinv(z1, self.p)
|
||||
inv_2 = (inv**2) % self.p
|
||||
inv_3 = (inv_2 * inv) % self.p
|
||||
return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1)
|
||||
|
||||
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_new_by_curve_name.argtypes = [ctypes.c_int]
|
||||
def negate(self, p1):
|
||||
"""Negate a Jacobian point tuple p1."""
|
||||
x1, y1, z1 = p1
|
||||
return (x1, (self.p - y1) % self.p, z1)
|
||||
|
||||
ssl.EC_KEY_get0_group.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_get0_group.argtypes = [ctypes.c_void_p]
|
||||
def on_curve(self, p1):
|
||||
"""Determine whether a Jacobian tuple p is on the curve (and not infinity)"""
|
||||
x1, y1, z1 = p1
|
||||
z2 = pow(z1, 2, self.p)
|
||||
z4 = pow(z2, 2, self.p)
|
||||
return z1 != 0 and (pow(x1, 3, self.p) + self.a * x1 * z4 + self.b * z2 * z4 - pow(y1, 2, self.p)) % self.p == 0
|
||||
|
||||
ssl.EC_KEY_get0_public_key.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_get0_public_key.argtypes = [ctypes.c_void_p]
|
||||
def is_x_coord(self, x):
|
||||
"""Test whether x is a valid X coordinate on the curve."""
|
||||
x_3 = pow(x, 3, self.p)
|
||||
return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1
|
||||
|
||||
ssl.EC_KEY_set_private_key.restype = ctypes.c_int
|
||||
ssl.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
def lift_x(self, x):
|
||||
"""Given an X coordinate on the curve, return a corresponding affine point."""
|
||||
x_3 = pow(x, 3, self.p)
|
||||
v = x_3 + self.a * x + self.b
|
||||
y = modsqrt(v, self.p)
|
||||
if y is None:
|
||||
return None
|
||||
return (x, y, 1)
|
||||
|
||||
ssl.EC_KEY_set_conv_form.restype = None
|
||||
ssl.EC_KEY_set_conv_form.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
||||
def double(self, p1):
|
||||
"""Double a Jacobian tuple p1
|
||||
|
||||
ssl.EC_KEY_set_public_key.restype = ctypes.c_int
|
||||
ssl.EC_KEY_set_public_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Doubling"""
|
||||
x1, y1, z1 = p1
|
||||
if z1 == 0:
|
||||
return (0, 1, 0)
|
||||
y1_2 = (y1**2) % self.p
|
||||
y1_4 = (y1_2**2) % self.p
|
||||
x1_2 = (x1**2) % self.p
|
||||
s = (4*x1*y1_2) % self.p
|
||||
m = 3*x1_2
|
||||
if self.a:
|
||||
m += self.a * pow(z1, 4, self.p)
|
||||
m = m % self.p
|
||||
x2 = (m**2 - 2*s) % self.p
|
||||
y2 = (m*(s - x2) - 8*y1_4) % self.p
|
||||
z2 = (2*y1*z1) % self.p
|
||||
return (x2, y2, z2)
|
||||
|
||||
ssl.i2o_ECPublicKey.restype = ctypes.c_void_p
|
||||
ssl.i2o_ECPublicKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
def add_mixed(self, p1, p2):
|
||||
"""Add a Jacobian tuple p1 and an affine tuple p2
|
||||
|
||||
ssl.EC_POINT_new.restype = ctypes.c_void_p
|
||||
ssl.EC_POINT_new.argtypes = [ctypes.c_void_p]
|
||||
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)"""
|
||||
x1, y1, z1 = p1
|
||||
x2, y2, z2 = p2
|
||||
assert(z2 == 1)
|
||||
# Adding to the point at infinity is a no-op
|
||||
if z1 == 0:
|
||||
return p2
|
||||
z1_2 = (z1**2) % self.p
|
||||
z1_3 = (z1_2 * z1) % self.p
|
||||
u2 = (x2 * z1_2) % self.p
|
||||
s2 = (y2 * z1_3) % self.p
|
||||
if x1 == u2:
|
||||
if (y1 != s2):
|
||||
# p1 and p2 are inverses. Return the point at infinity.
|
||||
return (0, 1, 0)
|
||||
# p1 == p2. The formulas below fail when the two points are equal.
|
||||
return self.double(p1)
|
||||
h = u2 - x1
|
||||
r = s2 - y1
|
||||
h_2 = (h**2) % self.p
|
||||
h_3 = (h_2 * h) % self.p
|
||||
u1_h_2 = (x1 * h_2) % self.p
|
||||
x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
|
||||
y3 = (r*(u1_h_2 - x3) - y1*h_3) % self.p
|
||||
z3 = (h*z1) % self.p
|
||||
return (x3, y3, z3)
|
||||
|
||||
ssl.EC_POINT_free.restype = None
|
||||
ssl.EC_POINT_free.argtypes = [ctypes.c_void_p]
|
||||
def add(self, p1, p2):
|
||||
"""Add two Jacobian tuples p1 and p2
|
||||
|
||||
ssl.EC_POINT_mul.restype = ctypes.c_int
|
||||
ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition"""
|
||||
x1, y1, z1 = p1
|
||||
x2, y2, z2 = p2
|
||||
# Adding the point at infinity is a no-op
|
||||
if z1 == 0:
|
||||
return p2
|
||||
if z2 == 0:
|
||||
return p1
|
||||
# Adding an Affine to a Jacobian is more efficient since we save field multiplications and squarings when z = 1
|
||||
if z1 == 1:
|
||||
return self.add_mixed(p2, p1)
|
||||
if z2 == 1:
|
||||
return self.add_mixed(p1, p2)
|
||||
z1_2 = (z1**2) % self.p
|
||||
z1_3 = (z1_2 * z1) % self.p
|
||||
z2_2 = (z2**2) % self.p
|
||||
z2_3 = (z2_2 * z2) % self.p
|
||||
u1 = (x1 * z2_2) % self.p
|
||||
u2 = (x2 * z1_2) % self.p
|
||||
s1 = (y1 * z2_3) % self.p
|
||||
s2 = (y2 * z1_3) % self.p
|
||||
if u1 == u2:
|
||||
if (s1 != s2):
|
||||
# p1 and p2 are inverses. Return the point at infinity.
|
||||
return (0, 1, 0)
|
||||
# p1 == p2. The formulas below fail when the two points are equal.
|
||||
return self.double(p1)
|
||||
h = u2 - u1
|
||||
r = s2 - s1
|
||||
h_2 = (h**2) % self.p
|
||||
h_3 = (h_2 * h) % self.p
|
||||
u1_h_2 = (u1 * h_2) % self.p
|
||||
x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
|
||||
y3 = (r*(u1_h_2 - x3) - s1*h_3) % self.p
|
||||
z3 = (h*z1*z2) % self.p
|
||||
return (x3, y3, z3)
|
||||
|
||||
# this specifies the curve used with ECDSA.
|
||||
NID_secp256k1 = 714 # from openssl/obj_mac.h
|
||||
def mul(self, ps):
|
||||
"""Compute a (multi) point multiplication
|
||||
|
||||
ps is a list of (Jacobian tuple, scalar) pairs.
|
||||
"""
|
||||
r = (0, 1, 0)
|
||||
for i in range(255, -1, -1):
|
||||
r = self.double(r)
|
||||
for (p, n) in ps:
|
||||
if ((n >> i) & 1):
|
||||
r = self.add(r, p)
|
||||
return r
|
||||
|
||||
SECP256K1 = EllipticCurve(2**256 - 2**32 - 977, 0, 7)
|
||||
SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
|
||||
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
||||
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
|
||||
|
||||
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
|
||||
def _check_result(val, func, args):
|
||||
if val == 0:
|
||||
raise ValueError
|
||||
else:
|
||||
return ctypes.c_void_p (val)
|
||||
|
||||
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_new_by_curve_name.errcheck = _check_result
|
||||
|
||||
class CECKey():
|
||||
"""Wrapper around OpenSSL's EC_KEY"""
|
||||
|
||||
POINT_CONVERSION_COMPRESSED = 2
|
||||
POINT_CONVERSION_UNCOMPRESSED = 4
|
||||
class ECPubKey():
|
||||
"""A secp256k1 public key"""
|
||||
|
||||
def __init__(self):
|
||||
self.k = ssl.EC_KEY_new_by_curve_name(NID_secp256k1)
|
||||
"""Construct an uninitialized public key"""
|
||||
self.valid = False
|
||||
|
||||
def __del__(self):
|
||||
if ssl:
|
||||
ssl.EC_KEY_free(self.k)
|
||||
self.k = None
|
||||
|
||||
def set_secretbytes(self, secret):
|
||||
priv_key = ssl.BN_bin2bn(secret, 32, ssl.BN_new())
|
||||
group = ssl.EC_KEY_get0_group(self.k)
|
||||
pub_key = ssl.EC_POINT_new(group)
|
||||
ctx = ssl.BN_CTX_new()
|
||||
if not ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx):
|
||||
raise ValueError("Could not derive public key from the supplied secret.")
|
||||
ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx)
|
||||
ssl.EC_KEY_set_private_key(self.k, priv_key)
|
||||
ssl.EC_KEY_set_public_key(self.k, pub_key)
|
||||
ssl.EC_POINT_free(pub_key)
|
||||
ssl.BN_CTX_free(ctx)
|
||||
return self.k
|
||||
|
||||
def set_privkey(self, key):
|
||||
self.mb = ctypes.create_string_buffer(key)
|
||||
return ssl.d2i_ECPrivateKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
|
||||
|
||||
def set_pubkey(self, key):
|
||||
self.mb = ctypes.create_string_buffer(key)
|
||||
return ssl.o2i_ECPublicKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
|
||||
|
||||
def get_privkey(self):
|
||||
size = ssl.i2d_ECPrivateKey(self.k, 0)
|
||||
mb_pri = ctypes.create_string_buffer(size)
|
||||
ssl.i2d_ECPrivateKey(self.k, ctypes.byref(ctypes.pointer(mb_pri)))
|
||||
return mb_pri.raw
|
||||
|
||||
def get_pubkey(self):
|
||||
size = ssl.i2o_ECPublicKey(self.k, 0)
|
||||
mb = ctypes.create_string_buffer(size)
|
||||
ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb)))
|
||||
return mb.raw
|
||||
|
||||
def get_raw_ecdh_key(self, other_pubkey):
|
||||
ecdh_keybuffer = ctypes.create_string_buffer(32)
|
||||
r = ssl.ECDH_compute_key(ctypes.pointer(ecdh_keybuffer), 32,
|
||||
ssl.EC_KEY_get0_public_key(other_pubkey.k),
|
||||
self.k, 0)
|
||||
if r != 32:
|
||||
raise Exception('CKey.get_ecdh_key(): ECDH_compute_key() failed')
|
||||
return ecdh_keybuffer.raw
|
||||
|
||||
def get_ecdh_key(self, other_pubkey, kdf=lambda k: hashlib.sha256(k).digest()):
|
||||
# FIXME: be warned it's not clear what the kdf should be as a default
|
||||
r = self.get_raw_ecdh_key(other_pubkey)
|
||||
return kdf(r)
|
||||
|
||||
def sign(self, hash, low_s = True):
|
||||
# FIXME: need unit tests for below cases
|
||||
if not isinstance(hash, bytes):
|
||||
raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
|
||||
if len(hash) != 32:
|
||||
raise ValueError('Hash must be exactly 32 bytes long')
|
||||
|
||||
sig_size0 = ctypes.c_uint32()
|
||||
sig_size0.value = ssl.ECDSA_size(self.k)
|
||||
mb_sig = ctypes.create_string_buffer(sig_size0.value)
|
||||
result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
|
||||
assert 1 == result
|
||||
assert mb_sig.raw[0] == 0x30
|
||||
assert mb_sig.raw[1] == sig_size0.value - 2
|
||||
total_size = mb_sig.raw[1]
|
||||
assert mb_sig.raw[2] == 2
|
||||
r_size = mb_sig.raw[3]
|
||||
assert mb_sig.raw[4 + r_size] == 2
|
||||
s_size = mb_sig.raw[5 + r_size]
|
||||
s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
|
||||
if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
|
||||
return mb_sig.raw[:sig_size0.value]
|
||||
def set(self, data):
|
||||
"""Construct a public key from a serialization in compressed or uncompressed format"""
|
||||
if (len(data) == 65 and data[0] == 0x04):
|
||||
p = (int.from_bytes(data[1:33], 'big'), int.from_bytes(data[33:65], 'big'), 1)
|
||||
self.valid = SECP256K1.on_curve(p)
|
||||
if self.valid:
|
||||
self.p = p
|
||||
self.compressed = False
|
||||
elif (len(data) == 33 and (data[0] == 0x02 or data[0] == 0x03)):
|
||||
x = int.from_bytes(data[1:33], 'big')
|
||||
if SECP256K1.is_x_coord(x):
|
||||
p = SECP256K1.lift_x(x)
|
||||
# if the oddness of the y co-ord isn't correct, find the other
|
||||
# valid y
|
||||
if (p[1] & 1) != (data[0] & 1):
|
||||
p = SECP256K1.negate(p)
|
||||
self.p = p
|
||||
self.valid = True
|
||||
self.compressed = True
|
||||
else:
|
||||
self.valid = False
|
||||
else:
|
||||
low_s_value = SECP256K1_ORDER - s_value
|
||||
low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
|
||||
while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
|
||||
low_s_bytes = low_s_bytes[1:]
|
||||
new_s_size = len(low_s_bytes)
|
||||
new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
|
||||
new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
|
||||
return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
|
||||
|
||||
def verify(self, hash, sig):
|
||||
"""Verify a DER signature"""
|
||||
return ssl.ECDSA_verify(0, hash, len(hash), sig, len(sig), self.k) == 1
|
||||
|
||||
def set_compressed(self, compressed):
|
||||
if compressed:
|
||||
form = self.POINT_CONVERSION_COMPRESSED
|
||||
else:
|
||||
form = self.POINT_CONVERSION_UNCOMPRESSED
|
||||
ssl.EC_KEY_set_conv_form(self.k, form)
|
||||
|
||||
|
||||
class CPubKey(bytes):
|
||||
"""An encapsulated public key
|
||||
|
||||
Attributes:
|
||||
|
||||
is_valid - Corresponds to CPubKey.IsValid()
|
||||
is_fullyvalid - Corresponds to CPubKey.IsFullyValid()
|
||||
is_compressed - Corresponds to CPubKey.IsCompressed()
|
||||
"""
|
||||
|
||||
def __new__(cls, buf, _cec_key=None):
|
||||
self = super(CPubKey, cls).__new__(cls, buf)
|
||||
if _cec_key is None:
|
||||
_cec_key = CECKey()
|
||||
self._cec_key = _cec_key
|
||||
self.is_fullyvalid = _cec_key.set_pubkey(self) != 0
|
||||
return self
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return len(self) > 0
|
||||
self.valid = False
|
||||
|
||||
@property
|
||||
def is_compressed(self):
|
||||
return len(self) == 33
|
||||
return self.compressed
|
||||
|
||||
def verify(self, hash, sig):
|
||||
return self._cec_key.verify(hash, sig)
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.valid
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
def get_bytes(self):
|
||||
assert(self.valid)
|
||||
p = SECP256K1.affine(self.p)
|
||||
if p is None:
|
||||
return None
|
||||
if self.compressed:
|
||||
return bytes([0x02 + (p[1] & 1)]) + p[0].to_bytes(32, 'big')
|
||||
else:
|
||||
return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big')
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__())
|
||||
# def verify_ecdsa(self, sig, msg, low_s=True):
|
||||
# """Verify a strictly DER-encoded ECDSA signature against this pubkey.
|
||||
|
||||
# See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
||||
# ECDSA verifier algorithm"""
|
||||
# assert(self.valid)
|
||||
|
||||
# # Extract r and s from the DER formatted signature. Return false for
|
||||
# # any DER encoding errors.
|
||||
# if (sig[1] + 2 != len(sig)):
|
||||
# return False
|
||||
# if (len(sig) < 4):
|
||||
# return False
|
||||
# if (sig[0] != 0x30):
|
||||
# return False
|
||||
# if (sig[2] != 0x02):
|
||||
# return False
|
||||
# rlen = sig[3]
|
||||
# if (len(sig) < 6 + rlen):
|
||||
# return False
|
||||
# if rlen < 1 or rlen > 33:
|
||||
# return False
|
||||
# if sig[4] >= 0x80:
|
||||
# return False
|
||||
# if (rlen > 1 and (sig[4] == 0) and not (sig[5] & 0x80)):
|
||||
# return False
|
||||
# r = int.from_bytes(sig[4:4+rlen], 'big')
|
||||
# if (sig[4+rlen] != 0x02):
|
||||
# return False
|
||||
# slen = sig[5+rlen]
|
||||
# if slen < 1 or slen > 33:
|
||||
# return False
|
||||
# if (len(sig) != 6 + rlen + slen):
|
||||
# return False
|
||||
# if sig[6+rlen] >= 0x80:
|
||||
# return False
|
||||
# if (slen > 1 and (sig[6+rlen] == 0) and not (sig[7+rlen] & 0x80)):
|
||||
# return False
|
||||
# s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big')
|
||||
|
||||
# # Verify that r and s are within the group order
|
||||
# if r < 1 or s < 1 or r >= SECP256K1_ORDER or s >= SECP256K1_ORDER:
|
||||
# return False
|
||||
# if low_s and s >= SECP256K1_ORDER_HALF:
|
||||
# return False
|
||||
# z = int.from_bytes(msg, 'big')
|
||||
|
||||
# # Run verifier algorithm on r, s
|
||||
# w = modinv(s, SECP256K1_ORDER)
|
||||
# u1 = z*w % SECP256K1_ORDER
|
||||
# u2 = r*w % SECP256K1_ORDER
|
||||
# R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, u1), (self.p, u2)]))
|
||||
# if R is None or R[0] != r:
|
||||
# return False
|
||||
# return True
|
||||
|
||||
class ECKey():
|
||||
"""A secp256k1 private key"""
|
||||
|
||||
def __init__(self):
|
||||
self.valid = False
|
||||
|
||||
def set(self, secret, compressed):
|
||||
"""Construct a private key object with given 32-byte secret and compressed flag."""
|
||||
assert(len(secret) == 32)
|
||||
secret = int.from_bytes(secret, 'big')
|
||||
self.valid = (secret > 0 and secret < SECP256K1_ORDER)
|
||||
if self.valid:
|
||||
self.secret = secret
|
||||
self.compressed = compressed
|
||||
|
||||
def set_wif(self, wif):
|
||||
(b, v) = base58_to_byte(wif)
|
||||
if len(b) != 32 and len(b) != 33:
|
||||
raise ValueError("invalid WIF: unexpected length {}".format(len(b)))
|
||||
compressed = len(b) == 33
|
||||
self.set(b[0:32], compressed)
|
||||
|
||||
def generate(self, compressed=True):
|
||||
"""Generate a random private key (compressed or uncompressed)."""
|
||||
self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed)
|
||||
|
||||
def get_bytes(self):
|
||||
"""Retrieve the 32-byte representation of this key."""
|
||||
assert(self.valid)
|
||||
return self.secret.to_bytes(32, 'big')
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.valid
|
||||
|
||||
@property
|
||||
def is_compressed(self):
|
||||
return self.compressed
|
||||
|
||||
def get_pubkey(self):
|
||||
"""Compute an ECPubKey object for this secret key."""
|
||||
assert(self.valid)
|
||||
ret = ECPubKey()
|
||||
p = SECP256K1.mul([(SECP256K1_G, self.secret)])
|
||||
ret.p = p
|
||||
ret.valid = True
|
||||
ret.compressed = self.compressed
|
||||
return ret
|
||||
|
||||
def sign_ecdsa(self, msg, low_s=True):
|
||||
"""Construct a DER-encoded ECDSA signature with this key.
|
||||
|
||||
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
||||
ECDSA signer algorithm."""
|
||||
assert(self.valid)
|
||||
z = int.from_bytes(msg, 'big')
|
||||
# Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation)
|
||||
k = random.randrange(1, SECP256K1_ORDER)
|
||||
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
|
||||
r = R[0] % SECP256K1_ORDER
|
||||
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
|
||||
if low_s and s > SECP256K1_ORDER_HALF:
|
||||
s = SECP256K1_ORDER - s
|
||||
# Represent in DER format. The byte representations of r and s have
|
||||
# length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33
|
||||
# bytes).
|
||||
rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
|
||||
sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
|
||||
return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
|
||||
|
|
|
|||
|
|
@ -183,6 +183,11 @@ def FromHex(obj, hex_string):
|
|||
def ToHex(obj):
|
||||
return bytes_to_hex_str(obj.serialize())
|
||||
|
||||
# Convert a binary-serializable object to hex (eg for submission via RPC)
|
||||
# This variant also serializes the witness.
|
||||
def WitToHex(obj):
|
||||
return bytes_to_hex_str(obj.serialize(with_witness=True))
|
||||
|
||||
# Objects that map to bitcoind objects, which can be serialized/deserialized
|
||||
|
||||
|
||||
|
|
@ -754,8 +759,11 @@ class CTransaction:
|
|||
r += self.wit.serialize()
|
||||
return r
|
||||
|
||||
def serialize(self):
|
||||
return self.serialize_with_witness()
|
||||
def serialize(self, with_witness=True):
|
||||
if with_witness:
|
||||
return self.serialize_with_witness()
|
||||
else:
|
||||
return self.serialize_without_witness()
|
||||
|
||||
def rehash(self):
|
||||
self.sha256 = None
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
This file is modified from python-bitcoinlib.
|
||||
"""
|
||||
|
||||
from .messages import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string, ser_vector
|
||||
from .messages import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string, ser_vector, ser_compact_size
|
||||
|
||||
from binascii import hexlify
|
||||
import hashlib
|
||||
|
|
@ -594,6 +594,8 @@ SIGHASH_ALL = 1
|
|||
SIGHASH_NONE = 2
|
||||
SIGHASH_SINGLE = 3
|
||||
SIGHASH_ANYONECANPAY = 0x80
|
||||
# ELEMENTS:
|
||||
SIGHASH_RANGEPROOF = 0x40
|
||||
|
||||
def FindAndDelete(script, sig):
|
||||
"""Consensus critical, see FindAndDelete() in Satoshi codebase"""
|
||||
|
|
@ -661,7 +663,15 @@ def SignatureHash(script, txTo, inIdx, hashtype):
|
|||
s = b""
|
||||
s += struct.pack("<i", txtmp.nVersion)
|
||||
s += ser_vector(txtmp.vin)
|
||||
s += ser_vector(txtmp.vout)
|
||||
# If SIGHASH_RANGEPROOF is set, we need to add the rangeproof serialization after each output
|
||||
if hashtype & SIGHASH_RANGEPROOF:
|
||||
s += ser_compact_size(len(txtmp.vout))
|
||||
for i in range(len(txtmp.vout)):
|
||||
s += txtmp.vout[i].serialize()
|
||||
s += ser_string(txtmp.wit.vtxoutwit[i].vchRangeproof)
|
||||
s += ser_string(txtmp.wit.vtxoutwit[i].vchSurjectionproof)
|
||||
else:
|
||||
s += ser_vector(txtmp.vout)
|
||||
s += struct.pack("<I", txtmp.nLockTime)
|
||||
|
||||
# add sighash type
|
||||
|
|
@ -681,6 +691,7 @@ def SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, amount):
|
|||
hashSequence = 0
|
||||
hashIssuance = 0
|
||||
hashOutputs = 0
|
||||
hashRangeproofs = 0
|
||||
|
||||
if not (hashtype & SIGHASH_ANYONECANPAY):
|
||||
serialize_prevouts = bytes()
|
||||
|
|
@ -706,10 +717,24 @@ def SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, amount):
|
|||
for o in txTo.vout:
|
||||
serialize_outputs += o.serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
if hashtype & SIGHASH_RANGEPROOF:
|
||||
serialize_rangeproofs = bytes()
|
||||
for wit in txTo.wit.vtxoutwit:
|
||||
serialize_rangeproofs += ser_string(wit.vchRangeproof) + ser_string(wit.vchSurjectionproof)
|
||||
hashRangeproofs = uint256_from_str(hash256(serialize_rangeproofs))
|
||||
|
||||
elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
|
||||
serialize_outputs = txTo.vout[inIdx].serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
if hashtype & SIGHASH_RANGEPROOF:
|
||||
serialize_rangeproofs = b'\x00'
|
||||
if len(txTo.wit.vtxoutwit) > inIdx:
|
||||
wit = txTo.wit.vtxoutwit[inIdx]
|
||||
serialize_rangeproofs = ser_string(wit.vchRangeproof) + ser_string(wit.vchSurjectionproof)
|
||||
hashRangeproofs = uint256_from_str(hash256(serialize_rangeproofs))
|
||||
|
||||
ss = bytes()
|
||||
ss += struct.pack("<i", txTo.nVersion)
|
||||
ss += ser_uint256(hashPrevouts)
|
||||
|
|
@ -720,6 +745,8 @@ def SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, amount):
|
|||
ss += amount.serialize()
|
||||
ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
|
||||
ss += ser_uint256(hashOutputs)
|
||||
if hashtype & SIGHASH_RANGEPROOF:
|
||||
ss += ser_uint256(hashRangeproofs)
|
||||
ss += struct.pack("<i", txTo.nLockTime)
|
||||
ss += struct.pack("<I", hashtype)
|
||||
|
||||
|
|
|
|||
|
|
@ -608,3 +608,20 @@ def find_vout_for_address(node, txid, addr):
|
|||
if any([unblind_addr == a for a in tx["vout"][i]["scriptPubKey"]["addresses"]]):
|
||||
return i
|
||||
raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr))
|
||||
|
||||
def modinv(a, n):
|
||||
"""Compute the modular inverse of a modulo n using the extended Euclidean
|
||||
Algorithm. See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers.
|
||||
"""
|
||||
# TODO: Change to pow(a, -1, n) available in Python 3.8
|
||||
t1, t2 = 0, 1
|
||||
r1, r2 = n, a
|
||||
while r2 != 0:
|
||||
q = r1 // r2
|
||||
t1, t2 = t2, t1 - q * t2
|
||||
r1, r2 = r2, r1 - q * r2
|
||||
if r1 > 1:
|
||||
return None
|
||||
if t1 < 0:
|
||||
t1 += n
|
||||
return t1
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ BASE_SCRIPTS = [
|
|||
'feature_initial_reissuance_token.py',
|
||||
'feature_progress.py',
|
||||
'feature_dynafed.py',
|
||||
'feature_sighash_rangeproof.py',
|
||||
# Longest test should go first, to favor running tests in parallel
|
||||
'wallet_hd.py',
|
||||
'wallet_backup.py',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue