Merge #13491: Improve handling of INVALID in IsMine
bb582a59c
Add P2WSH destination helper and use it instead of manual hashing (Pieter Wuille)eaba1c111
Add additional unit tests for invalid IsMine combinations (Pieter Wuille)e6b9730c4
Do not expose invalidity from IsMine (Pieter Wuille) Pull request description: This improves the handling of INVALID in IsMine: * Extra INVALID conditions were added to `IsMine` (following https://github.com/bitcoin/bitcoin/pull/13142/files#r185349057), but these were untested. Add unit tests for them. * In https://github.com/bitcoin/bitcoin/pull/13142#issuecomment-386396975 it was suggested to merge `isInvalid` into the return status. This PR takes a different approach, and removes the `isInvalid` entirely. It was only ever used inside tests, as normal users of IsMine don't care about the reason for non-mine-ness, only whether it is or not. As the unit tests are extensive enough, it seems sufficient to have a black box text (with tests for both compressed and uncompressed keys). Some addition code simplification is done as well. Tree-SHA512: 3267f8846f3fa4e994f57504b155b0e1bbdf13808c4c04dab7c6886c2c0b88716169cee9c5b350513297e0ca2a00812e3401acf30ac9cde5d892f9fb59ad7fef
This commit is contained in:
commit
61a044a86a
7 changed files with 125 additions and 161 deletions
|
@ -637,9 +637,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
|
||||||
} else {
|
} else {
|
||||||
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
|
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
|
||||||
// Newer segwit program versions should be considered when then become available.
|
// Newer segwit program versions should be considered when then become available.
|
||||||
uint256 scriptHash;
|
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
|
||||||
CSHA256().Write(script.data(), script.size()).Finalize(scriptHash.begin());
|
|
||||||
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(scriptHash));
|
|
||||||
}
|
}
|
||||||
ScriptPubKeyToUniv(segwitScr, sr, true);
|
ScriptPubKeyToUniv(segwitScr, sr, true);
|
||||||
sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
|
sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
|
||||||
|
|
|
@ -38,7 +38,7 @@ enum class IsMineResult
|
||||||
NO = 0, //! Not ours
|
NO = 0, //! Not ours
|
||||||
WATCH_ONLY = 1, //! Included in watch-only balance
|
WATCH_ONLY = 1, //! Included in watch-only balance
|
||||||
SPENDABLE = 2, //! Included in all balances
|
SPENDABLE = 2, //! Included in all balances
|
||||||
INVALID = 3, //! Not spendable by anyone
|
INVALID = 3, //! Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
|
||||||
};
|
};
|
||||||
|
|
||||||
bool PermitsUncompressed(IsMineSigVersion sigversion)
|
bool PermitsUncompressed(IsMineSigVersion sigversion)
|
||||||
|
@ -173,12 +173,10 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid)
|
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
|
||||||
{
|
{
|
||||||
isInvalid = false;
|
|
||||||
switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
|
switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
|
||||||
case IsMineResult::INVALID:
|
case IsMineResult::INVALID:
|
||||||
isInvalid = true;
|
|
||||||
case IsMineResult::NO:
|
case IsMineResult::NO:
|
||||||
return ISMINE_NO;
|
return ISMINE_NO;
|
||||||
case IsMineResult::WATCH_ONLY:
|
case IsMineResult::WATCH_ONLY:
|
||||||
|
@ -189,12 +187,6 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool&
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
|
|
||||||
{
|
|
||||||
bool isInvalid = false;
|
|
||||||
return IsMine(keystore, scriptPubKey, isInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
|
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
|
||||||
{
|
{
|
||||||
CScript script = GetScriptForDestination(dest);
|
CScript script = GetScriptForDestination(dest);
|
||||||
|
|
|
@ -24,12 +24,6 @@ enum isminetype
|
||||||
/** used for bitflags of isminetype */
|
/** used for bitflags of isminetype */
|
||||||
typedef uint8_t isminefilter;
|
typedef uint8_t isminefilter;
|
||||||
|
|
||||||
/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
|
|
||||||
* and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
|
|
||||||
* different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed
|
|
||||||
* keys in SigVersion::WITNESS_V0 script, but could also be used in similar cases in the future
|
|
||||||
*/
|
|
||||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid);
|
|
||||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
|
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
|
||||||
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
|
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <script/standard.h>
|
#include <script/standard.h>
|
||||||
|
|
||||||
|
#include <crypto/sha256.h>
|
||||||
#include <pubkey.h>
|
#include <pubkey.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
@ -18,6 +19,11 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
|
||||||
|
|
||||||
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
|
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
|
||||||
|
|
||||||
|
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
|
||||||
|
{
|
||||||
|
CSHA256().Write(in.data(), in.size()).Finalize(begin());
|
||||||
|
}
|
||||||
|
|
||||||
const char* GetTxnOutputType(txnouttype t)
|
const char* GetTxnOutputType(txnouttype t)
|
||||||
{
|
{
|
||||||
switch (t)
|
switch (t)
|
||||||
|
@ -329,9 +335,7 @@ CScript GetScriptForWitness(const CScript& redeemscript)
|
||||||
return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
|
return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint256 hash;
|
return GetScriptForDestination(WitnessV0ScriptHash(redeemscript));
|
||||||
CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin());
|
|
||||||
return GetScriptForDestination(WitnessV0ScriptHash(hash));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidDestination(const CTxDestination& dest) {
|
bool IsValidDestination(const CTxDestination& dest) {
|
||||||
|
|
|
@ -77,6 +77,7 @@ struct WitnessV0ScriptHash : public uint256
|
||||||
{
|
{
|
||||||
WitnessV0ScriptHash() : uint256() {}
|
WitnessV0ScriptHash() : uint256() {}
|
||||||
explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
|
explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
|
||||||
|
explicit WitnessV0ScriptHash(const CScript& script);
|
||||||
using uint256::uint256;
|
using uint256::uint256;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -398,106 +398,149 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
|
|
||||||
CScript scriptPubKey;
|
CScript scriptPubKey;
|
||||||
isminetype result;
|
isminetype result;
|
||||||
bool isInvalid;
|
|
||||||
|
|
||||||
// P2PK compressed
|
// P2PK compressed
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
scriptPubKey.clear();
|
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
|
||||||
scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
|
|
||||||
|
|
||||||
// Keystore does not have key
|
// Keystore does not have key
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has key
|
// Keystore has key
|
||||||
keystore.AddKey(keys[0]);
|
keystore.AddKey(keys[0]);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2PK uncompressed
|
// P2PK uncompressed
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
scriptPubKey.clear();
|
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
|
||||||
scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG;
|
|
||||||
|
|
||||||
// Keystore does not have key
|
// Keystore does not have key
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has key
|
// Keystore has key
|
||||||
keystore.AddKey(uncompressedKey);
|
keystore.AddKey(uncompressedKey);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2PKH compressed
|
// P2PKH compressed
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
scriptPubKey.clear();
|
scriptPubKey = GetScriptForDestination(pubkeys[0].GetID());
|
||||||
scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
|
||||||
|
|
||||||
// Keystore does not have key
|
// Keystore does not have key
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has key
|
// Keystore has key
|
||||||
keystore.AddKey(keys[0]);
|
keystore.AddKey(keys[0]);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2PKH uncompressed
|
// P2PKH uncompressed
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
scriptPubKey.clear();
|
scriptPubKey = GetScriptForDestination(uncompressedPubkey.GetID());
|
||||||
scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
|
||||||
|
|
||||||
// Keystore does not have key
|
// Keystore does not have key
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has key
|
// Keystore has key
|
||||||
keystore.AddKey(uncompressedKey);
|
keystore.AddKey(uncompressedKey);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2SH
|
// P2SH
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
|
|
||||||
CScript redeemScript;
|
CScript redeemScript = GetScriptForDestination(pubkeys[0].GetID());
|
||||||
redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
|
||||||
|
|
||||||
scriptPubKey.clear();
|
|
||||||
scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
|
|
||||||
|
|
||||||
// Keystore does not have redeemScript or key
|
// Keystore does not have redeemScript or key
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has redeemScript but no key
|
// Keystore has redeemScript but no key
|
||||||
keystore.AddCScript(redeemScript);
|
keystore.AddCScript(redeemScript);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has redeemScript and key
|
// Keystore has redeemScript and key
|
||||||
keystore.AddKey(keys[0]);
|
keystore.AddKey(keys[0]);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
}
|
||||||
|
|
||||||
|
// (P2PKH inside) P2SH inside P2SH (invalid)
|
||||||
|
{
|
||||||
|
CBasicKeyStore keystore;
|
||||||
|
|
||||||
|
CScript redeemscript_inner = GetScriptForDestination(pubkeys[0].GetID());
|
||||||
|
CScript redeemscript = GetScriptForDestination(CScriptID(redeemscript_inner));
|
||||||
|
scriptPubKey = GetScriptForDestination(CScriptID(redeemscript));
|
||||||
|
|
||||||
|
keystore.AddCScript(redeemscript);
|
||||||
|
keystore.AddCScript(redeemscript_inner);
|
||||||
|
keystore.AddCScript(scriptPubKey);
|
||||||
|
keystore.AddKey(keys[0]);
|
||||||
|
result = IsMine(keystore, scriptPubKey);
|
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// (P2PKH inside) P2SH inside P2WSH (invalid)
|
||||||
|
{
|
||||||
|
CBasicKeyStore keystore;
|
||||||
|
|
||||||
|
CScript redeemscript = GetScriptForDestination(pubkeys[0].GetID());
|
||||||
|
CScript witnessscript = GetScriptForDestination(CScriptID(redeemscript));
|
||||||
|
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
|
||||||
|
|
||||||
|
keystore.AddCScript(witnessscript);
|
||||||
|
keystore.AddCScript(redeemscript);
|
||||||
|
keystore.AddCScript(scriptPubKey);
|
||||||
|
keystore.AddKey(keys[0]);
|
||||||
|
result = IsMine(keystore, scriptPubKey);
|
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2WPKH inside P2WSH (invalid)
|
||||||
|
{
|
||||||
|
CBasicKeyStore keystore;
|
||||||
|
|
||||||
|
CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
|
||||||
|
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
|
||||||
|
|
||||||
|
keystore.AddCScript(witnessscript);
|
||||||
|
keystore.AddCScript(scriptPubKey);
|
||||||
|
keystore.AddKey(keys[0]);
|
||||||
|
result = IsMine(keystore, scriptPubKey);
|
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// (P2PKH inside) P2WSH inside P2WSH (invalid)
|
||||||
|
{
|
||||||
|
CBasicKeyStore keystore;
|
||||||
|
|
||||||
|
CScript witnessscript_inner = GetScriptForDestination(pubkeys[0].GetID());
|
||||||
|
CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner));
|
||||||
|
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
|
||||||
|
|
||||||
|
keystore.AddCScript(witnessscript_inner);
|
||||||
|
keystore.AddCScript(witnessscript);
|
||||||
|
keystore.AddCScript(scriptPubKey);
|
||||||
|
keystore.AddKey(keys[0]);
|
||||||
|
result = IsMine(keystore, scriptPubKey);
|
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2WPKH compressed
|
// P2WPKH compressed
|
||||||
|
@ -505,14 +548,12 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
keystore.AddKey(keys[0]);
|
keystore.AddKey(keys[0]);
|
||||||
|
|
||||||
scriptPubKey.clear();
|
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
|
||||||
scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID());
|
|
||||||
|
|
||||||
// Keystore implicitly has key and P2SH redeemScript
|
// Keystore implicitly has key and P2SH redeemScript
|
||||||
keystore.AddCScript(scriptPubKey);
|
keystore.AddCScript(scriptPubKey);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2WPKH uncompressed
|
// P2WPKH uncompressed
|
||||||
|
@ -520,56 +561,45 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
keystore.AddKey(uncompressedKey);
|
keystore.AddKey(uncompressedKey);
|
||||||
|
|
||||||
scriptPubKey.clear();
|
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey.GetID()));
|
||||||
scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID());
|
|
||||||
|
|
||||||
// Keystore has key, but no P2SH redeemScript
|
// Keystore has key, but no P2SH redeemScript
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has key and P2SH redeemScript
|
// Keystore has key and P2SH redeemScript
|
||||||
keystore.AddCScript(scriptPubKey);
|
keystore.AddCScript(scriptPubKey);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// scriptPubKey multisig
|
// scriptPubKey multisig
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
|
|
||||||
scriptPubKey.clear();
|
scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||||
scriptPubKey << OP_2 <<
|
|
||||||
ToByteVector(uncompressedPubkey) <<
|
|
||||||
ToByteVector(pubkeys[1]) <<
|
|
||||||
OP_2 << OP_CHECKMULTISIG;
|
|
||||||
|
|
||||||
// Keystore does not have any keys
|
// Keystore does not have any keys
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has 1/2 keys
|
// Keystore has 1/2 keys
|
||||||
keystore.AddKey(uncompressedKey);
|
keystore.AddKey(uncompressedKey);
|
||||||
|
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has 2/2 keys
|
// Keystore has 2/2 keys
|
||||||
keystore.AddKey(keys[1]);
|
keystore.AddKey(keys[1]);
|
||||||
|
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has 2/2 keys and the script
|
// Keystore has 2/2 keys and the script
|
||||||
keystore.AddCScript(scriptPubKey);
|
keystore.AddCScript(scriptPubKey);
|
||||||
|
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2SH multisig
|
// P2SH multisig
|
||||||
|
@ -578,25 +608,17 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
keystore.AddKey(uncompressedKey);
|
keystore.AddKey(uncompressedKey);
|
||||||
keystore.AddKey(keys[1]);
|
keystore.AddKey(keys[1]);
|
||||||
|
|
||||||
CScript redeemScript;
|
CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||||
redeemScript << OP_2 <<
|
scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
|
||||||
ToByteVector(uncompressedPubkey) <<
|
|
||||||
ToByteVector(pubkeys[1]) <<
|
|
||||||
OP_2 << OP_CHECKMULTISIG;
|
|
||||||
|
|
||||||
scriptPubKey.clear();
|
|
||||||
scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
|
|
||||||
|
|
||||||
// Keystore has no redeemScript
|
// Keystore has no redeemScript
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has redeemScript
|
// Keystore has redeemScript
|
||||||
keystore.AddCScript(redeemScript);
|
keystore.AddCScript(redeemScript);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2WSH multisig with compressed keys
|
// P2WSH multisig with compressed keys
|
||||||
|
@ -605,35 +627,22 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
keystore.AddKey(keys[0]);
|
keystore.AddKey(keys[0]);
|
||||||
keystore.AddKey(keys[1]);
|
keystore.AddKey(keys[1]);
|
||||||
|
|
||||||
CScript witnessScript;
|
CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
|
||||||
witnessScript << OP_2 <<
|
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
|
||||||
ToByteVector(pubkeys[0]) <<
|
|
||||||
ToByteVector(pubkeys[1]) <<
|
|
||||||
OP_2 << OP_CHECKMULTISIG;
|
|
||||||
|
|
||||||
uint256 scriptHash;
|
|
||||||
CSHA256().Write(&witnessScript[0], witnessScript.size())
|
|
||||||
.Finalize(scriptHash.begin());
|
|
||||||
|
|
||||||
scriptPubKey.clear();
|
|
||||||
scriptPubKey << OP_0 << ToByteVector(scriptHash);
|
|
||||||
|
|
||||||
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
||||||
keystore.AddCScript(witnessScript);
|
keystore.AddCScript(witnessScript);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has keys, witnessScript, P2SH redeemScript
|
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||||
keystore.AddCScript(scriptPubKey);
|
keystore.AddCScript(scriptPubKey);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2WSH multisig with uncompressed key
|
// P2WSH multisig with uncompressed key
|
||||||
|
@ -642,75 +651,47 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
keystore.AddKey(uncompressedKey);
|
keystore.AddKey(uncompressedKey);
|
||||||
keystore.AddKey(keys[1]);
|
keystore.AddKey(keys[1]);
|
||||||
|
|
||||||
CScript witnessScript;
|
CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||||
witnessScript << OP_2 <<
|
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
|
||||||
ToByteVector(uncompressedPubkey) <<
|
|
||||||
ToByteVector(pubkeys[1]) <<
|
|
||||||
OP_2 << OP_CHECKMULTISIG;
|
|
||||||
|
|
||||||
uint256 scriptHash;
|
|
||||||
CSHA256().Write(&witnessScript[0], witnessScript.size())
|
|
||||||
.Finalize(scriptHash.begin());
|
|
||||||
|
|
||||||
scriptPubKey.clear();
|
|
||||||
scriptPubKey << OP_0 << ToByteVector(scriptHash);
|
|
||||||
|
|
||||||
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
||||||
keystore.AddCScript(witnessScript);
|
keystore.AddCScript(witnessScript);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has keys, witnessScript, P2SH redeemScript
|
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||||
keystore.AddCScript(scriptPubKey);
|
keystore.AddCScript(scriptPubKey);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2WSH multisig wrapped in P2SH
|
// P2WSH multisig wrapped in P2SH
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore;
|
CBasicKeyStore keystore;
|
||||||
|
|
||||||
CScript witnessScript;
|
CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
|
||||||
witnessScript << OP_2 <<
|
CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
|
||||||
ToByteVector(pubkeys[0]) <<
|
scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
|
||||||
ToByteVector(pubkeys[1]) <<
|
|
||||||
OP_2 << OP_CHECKMULTISIG;
|
|
||||||
|
|
||||||
uint256 scriptHash;
|
|
||||||
CSHA256().Write(&witnessScript[0], witnessScript.size())
|
|
||||||
.Finalize(scriptHash.begin());
|
|
||||||
|
|
||||||
CScript redeemScript;
|
|
||||||
redeemScript << OP_0 << ToByteVector(scriptHash);
|
|
||||||
|
|
||||||
scriptPubKey.clear();
|
|
||||||
scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
|
|
||||||
|
|
||||||
// Keystore has no witnessScript, P2SH redeemScript, or keys
|
// Keystore has no witnessScript, P2SH redeemScript, or keys
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has witnessScript and P2SH redeemScript, but no keys
|
// Keystore has witnessScript and P2SH redeemScript, but no keys
|
||||||
keystore.AddCScript(redeemScript);
|
keystore.AddCScript(redeemScript);
|
||||||
keystore.AddCScript(witnessScript);
|
keystore.AddCScript(witnessScript);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
|
|
||||||
// Keystore has keys, witnessScript, P2SH redeemScript
|
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||||
keystore.AddKey(keys[0]);
|
keystore.AddKey(keys[0]);
|
||||||
keystore.AddKey(keys[1]);
|
keystore.AddKey(keys[1]);
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OP_RETURN
|
// OP_RETURN
|
||||||
|
@ -721,9 +702,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
scriptPubKey.clear();
|
scriptPubKey.clear();
|
||||||
scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
|
scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
|
||||||
|
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// witness unspendable
|
// witness unspendable
|
||||||
|
@ -734,9 +714,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
scriptPubKey.clear();
|
scriptPubKey.clear();
|
||||||
scriptPubKey << OP_0 << ToByteVector(ParseHex("aabb"));
|
scriptPubKey << OP_0 << ToByteVector(ParseHex("aabb"));
|
||||||
|
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// witness unknown
|
// witness unknown
|
||||||
|
@ -747,9 +726,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
scriptPubKey.clear();
|
scriptPubKey.clear();
|
||||||
scriptPubKey << OP_16 << ToByteVector(ParseHex("aabb"));
|
scriptPubKey << OP_16 << ToByteVector(ParseHex("aabb"));
|
||||||
|
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nonstandard
|
// Nonstandard
|
||||||
|
@ -760,9 +738,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
|
||||||
scriptPubKey.clear();
|
scriptPubKey.clear();
|
||||||
scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
|
scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
|
||||||
|
|
||||||
result = IsMine(keystore, scriptPubKey, isInvalid);
|
result = IsMine(keystore, scriptPubKey);
|
||||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||||
BOOST_CHECK(!isInvalid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4538,9 +4538,7 @@ CTxDestination CWallet::AddAndGetDestinationForScript(const CScript& script, Out
|
||||||
return CScriptID(script);
|
return CScriptID(script);
|
||||||
case OutputType::P2SH_SEGWIT:
|
case OutputType::P2SH_SEGWIT:
|
||||||
case OutputType::BECH32: {
|
case OutputType::BECH32: {
|
||||||
WitnessV0ScriptHash hash;
|
CTxDestination witdest = WitnessV0ScriptHash(script);
|
||||||
CSHA256().Write(script.data(), script.size()).Finalize(hash.begin());
|
|
||||||
CTxDestination witdest = hash;
|
|
||||||
CScript witprog = GetScriptForDestination(witdest);
|
CScript witprog = GetScriptForDestination(witdest);
|
||||||
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
|
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
|
||||||
if (!IsSolvable(*this, witprog)) return CScriptID(script);
|
if (!IsSolvable(*this, witprog)) return CScriptID(script);
|
||||||
|
|
Loading…
Reference in a new issue