Merge pull request #91 from jc23424/segwit-tests

Add more tests on FORKID (replay) and modified segwit spends
This commit is contained in:
BlueSilver22 2018-02-25 16:58:44 -06:00 committed by GitHub
commit 7f2662522c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 275 additions and 47 deletions

View File

@ -216,6 +216,9 @@ public:
bool operator()(const CKeyID& id) const { return addr->Set(id); }
bool operator()(const CScriptID& id) const { return addr->Set(id); }
bool operator()(const WitnessV0ScriptHash& w) const { return false; }
bool operator()(const WitnessV0KeyHash& w) const { return false; }
bool operator()(const WitnessUnknown& w) const { return false; }
bool operator()(const CNoDestination& no) const { return false; }
};
@ -386,4 +389,3 @@ libzcash::SpendingKey CZCSpendingKey::Get() const
ss >> ret;
return ret;
}

View File

@ -145,6 +145,47 @@ public:
}
return obj;
}
UniValue operator()(const WitnessV0KeyHash& id) const
{
UniValue obj(UniValue::VOBJ);
CPubKey pubkey;
obj.pushKV("isscript", false);
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
//if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
//obj.pushKV("pubkey", HexStr(pubkey));
//}
return obj;
}
UniValue operator()(const WitnessV0ScriptHash& id) const
{
UniValue obj(UniValue::VOBJ);
CScript subscript;
obj.pushKV("isscript", true);
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
CRIPEMD160 hasher;
uint160 hash;
hasher.Write(id.begin(), 32).Finalize(hash.begin());
//if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
//ProcessSubScript(subscript, obj);
//}
return obj;
}
UniValue operator()(const WitnessUnknown& id) const
{
UniValue obj(UniValue::VOBJ);
CScript subscript;
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", (int)id.version);
obj.pushKV("witness_program", HexStr(id.program, id.program + id.length));
return obj;
}
};
#endif

View File

@ -26,13 +26,13 @@ const char* GetTxnOutputType(txnouttype t)
{
case TX_NONSTANDARD: return "nonstandard";
case TX_PUBKEY: return "pubkey";
case TX_PUBKEY_REPLAY: return "pubkeyreplay";
case TX_PUBKEYHASH: return "pubkeyhash";
case TX_PUBKEYHASH_REPLAY: return "pubkeyhashreplay";
case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig";
case TX_MULTISIG_REPLAY: return "multisigreplay";
case TX_NULL_DATA: return "nulldata";
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
case TX_WITNESS_UNKNOWN: return "witness_unknown";
}
return NULL;
}
@ -48,15 +48,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
{
// Standard tx, sender provides pubkey, receiver adds signature
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
mTemplates.insert(make_pair(TX_PUBKEY_REPLAY, CScript() << OP_PUBKEY << OP_CHECKSIG << OP_SMALLDATA << OP_SMALLDATA << OP_NOP5));
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
mTemplates.insert(make_pair(TX_PUBKEYHASH_REPLAY, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG << OP_SMALLDATA << OP_SMALLDATA << OP_NOP5));
// Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
mTemplates.insert(make_pair(TX_MULTISIG_REPLAY, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG << OP_SMALLDATA << OP_SMALLDATA << OP_NOP5));
// Empty, provably prunable, data-carrying output
if (GetBoolArg("-datacarrier", true))
@ -301,6 +298,27 @@ public:
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true;
}
bool operator()(const WitnessV0KeyHash& id) const
{
script->clear();
*script << OP_0 << ToByteVector(id);
return true;
}
bool operator()(const WitnessV0ScriptHash& id) const
{
script->clear();
*script << OP_0 << ToByteVector(id);
return true;
}
bool operator()(const WitnessUnknown& id) const
{
script->clear();
*script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
return true;
}
};
}
@ -322,3 +340,21 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
return script;
}
CScript GetScriptForWitness(const CScript& redeemscript)
{
CScript ret;
txnouttype typ;
std::vector<std::vector<unsigned char> > vSolutions;
if (Solver(redeemscript, typ, vSolutions)) {
if (typ == TX_PUBKEY) {
return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end())));
} else if (typ == TX_PUBKEYHASH) {
return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
}
}
uint256 hash;
CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin());
return GetScriptForDestination(WitnessV0ScriptHash(hash));
}

View File

@ -63,13 +63,13 @@ enum txnouttype
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEY_REPLAY,
TX_PUBKEYHASH,
TX_PUBKEYHASH_REPLAY,
TX_SCRIPTHASH,
TX_MULTISIG,
TX_MULTISIG_REPLAY,
TX_NULL_DATA,
TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH,
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
class CNoDestination {
@ -78,6 +78,42 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
struct WitnessV0ScriptHash : public uint256
{
WitnessV0ScriptHash() : uint256() {}
explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
using uint256::uint256;
};
struct WitnessV0KeyHash : public uint160
{
WitnessV0KeyHash() : uint160() {}
explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {}
using uint160::uint160;
};
//! CTxDestination subtype to encode any future Witness version
struct WitnessUnknown
{
unsigned int version;
unsigned int length;
unsigned char program[40];
friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) {
if (w1.version != w2.version) return false;
if (w1.length != w2.length) return false;
return std::equal(w1.program, w1.program + w1.length, w2.program);
}
friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) {
if (w1.version < w2.version) return true;
if (w1.version > w2.version) return false;
if (w1.length < w2.length) return true;
if (w1.length > w2.length) return false;
return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length);
}
};
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
@ -85,7 +121,7 @@ public:
* * CScriptID: TX_SCRIPTHASH destination
* A CTxDestination is the internal data type encoded in a CBitcoinAddress
*/
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
const char* GetTxnOutputType(txnouttype t);
@ -97,5 +133,6 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
CScript GetScriptForDestination(const CTxDestination& dest);
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
CScript GetScriptForWitness(const CScript& redeemscript);
#endif // BITCOIN_SCRIPT_STANDARD_H

View File

@ -92,6 +92,9 @@ public:
{
return (exp_addrType == "none");
}
template<typename X>
bool operator()(const X &none) const { return false; }
};
// Visitor to check address payload
@ -115,6 +118,9 @@ public:
{
return exp_payload.size() == 0;
}
template<typename X>
bool operator()(const X &none) { return false; }
};
// Goal: check that parsed keys match test payload
@ -271,4 +277,3 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
BOOST_AUTO_TEST_SUITE_END()

View File

@ -28,8 +28,7 @@ Serialize(const CScript& s)
return sSerialized;
}
static bool
Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err)
CMutableTransaction BuildTransaction(const CScript& scriptSig, const CScript& scriptPubKey)
{
// Create dummy to/from transactions:
CMutableTransaction txFrom;
@ -44,7 +43,14 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri
txTo.vin[0].scriptSig = scriptSig;
txTo.vout[0].nValue = 1;
return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err);
return txTo;
}
static bool
Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err)
{
auto txTo = BuildTransaction(scriptSig, scriptPubKey);
return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err);
}
@ -124,39 +130,110 @@ BOOST_AUTO_TEST_CASE(sign)
}
}
BOOST_AUTO_TEST_CASE(segwitlock)
BOOST_AUTO_TEST_CASE(segwitspend_wsh)
{
std::vector<bool> expects = {true, false};
for(auto it = expects.begin(); it != expects.end(); it++) {
bool expect = *it;
ScriptError err;
CScript unwrappedPubKey,scriptSig;
unwrappedPubKey << OP_12 << OP_EQUAL;
if(expect)
scriptSig << OP_12;
else
scriptSig << OP_11;
CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey));
CScript p2shScriptSig = scriptSig;
p2shScriptSig << Serialize(unwrappedPubKey);
CScript segwitPubKey = GetScriptForWitness(unwrappedPubKey);
CScript segwitScriptSig = scriptSig;
segwitScriptSig << Serialize(unwrappedPubKey);
CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey));
CScript p2shsegwitScriptSig = segwitScriptSig;
p2shsegwitScriptSig << Serialize(segwitPubKey);
BOOST_CHECK(Verify(scriptSig, unwrappedPubKey, true, err) == expect);
BOOST_CHECK(Verify(p2shScriptSig, p2shPubKey, true, err) == expect);
BOOST_CHECK(p2shPubKey.IsPayToScriptHash());
BOOST_CHECK(segwitPubKey.IsPayToWitnessScriptHash());
BOOST_CHECK(p2shsegwitPubKey.IsPayToScriptHash());
BOOST_CHECK(Verify(segwitScriptSig, segwitPubKey, true, err) == expect);
BOOST_CHECK(Verify(p2shsegwitScriptSig, p2shsegwitPubKey, true, err) == expect);
}
}
void BuildPKTransaction(const CScript& scriptPubKey, CKey signingKey, CKey pushKey, CScript additionalSig, bool expect, ScriptError_t expect_err, const CScript& scriptToSign)
{
CMutableTransaction txTo = BuildTransaction(CScript(), scriptPubKey);
auto lenR = 32;
auto lenS = 32;
uint256 hash = SignatureHash(scriptToSign, txTo, 0, SIGHASH_ALL);
std::vector<unsigned char> vchSig;
signingKey.Sign(hash, vchSig, 0);
vchSig.push_back(static_cast<unsigned char>(SIGHASH_ALL));
txTo.vin[0].scriptSig << vchSig << ToByteVector(pushKey.GetPubKey());
txTo.vin[0].scriptSig += additionalSig;
ScriptError err;
BOOST_CHECK(Verify(txTo.vin[0].scriptSig, scriptPubKey, true, err) == expect);
// any scriptSig will do here
CScript scriptSig;
scriptSig << ToByteVector(uint256());
if(!expect)
BOOST_CHECK_MESSAGE(err == expect_err, ScriptErrorString(err));
}
uint160 hash20;
CScript p2wpkh;
p2wpkh << OP_0 << ToByteVector(hash20);
BOOST_CHECK(!Verify(scriptSig, p2wpkh, true, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err));
BOOST_AUTO_TEST_CASE(segwitspend_wpkh)
{
std::vector<bool> expects = {true, false};
CScript p2shp2wpkh = GetScriptForDestination(CScriptID(p2wpkh));
CScript p2shp2wpkhsig = scriptSig;
p2shp2wpkhsig << Serialize(p2wpkh);
CKey key1,key2;
key1.MakeNewKey(true);
key2.MakeNewKey(true);
BOOST_CHECK(!Verify(p2shp2wpkhsig, p2shp2wpkh, true, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err));
CScript unwrappedPubKey;
unwrappedPubKey << OP_DUP << OP_HASH160 << ToByteVector(key1.GetPubKey().GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
uint256 hash32;
CScript p2wsh;
p2wsh << OP_0 << ToByteVector(hash32);
BOOST_CHECK(!Verify(scriptSig, p2wsh, true, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err));
for(auto it = expects.begin(); it != expects.end(); it++) {
bool expect = *it;
CScript p2shp2wsh = GetScriptForDestination(CScriptID(p2wsh));
CScript p2shp2wshsig = scriptSig;
p2shp2wshsig << Serialize(p2wsh);
CKey k = expect ? key1 : key2;
BOOST_CHECK(!Verify(p2shp2wshsig, p2shp2wsh, true, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SEGWIT_LOCKED, ScriptErrorString(err));
CScript p2shPubKey = GetScriptForDestination(CScriptID(unwrappedPubKey));
CScript segwitPubKey = GetScriptForWitness(unwrappedPubKey);
CScript p2shsegwitPubKey = GetScriptForDestination(CScriptID(segwitPubKey));
BOOST_CHECK(p2shPubKey.IsPayToScriptHash());
BOOST_CHECK(segwitPubKey.IsPayToWitnessPubKeyHash());
BOOST_CHECK(p2shsegwitPubKey.IsPayToScriptHash());
CScript p2shScriptSig;
p2shScriptSig << Serialize(unwrappedPubKey);
CScript segwitScriptSig = CScript();
CScript p2shsegwitScriptSig = segwitScriptSig;
p2shsegwitScriptSig << Serialize(segwitPubKey);
BuildPKTransaction(unwrappedPubKey, key1, k, CScript(), expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey);
BuildPKTransaction(p2shPubKey, key1, k, p2shScriptSig, expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey);
BuildPKTransaction(segwitPubKey, key1, k, segwitScriptSig, expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey);
BuildPKTransaction(p2shsegwitPubKey, key1, k, p2shsegwitScriptSig, expect, SCRIPT_ERR_EQUALVERIFY, unwrappedPubKey);
if(!expect) {
BuildPKTransaction(unwrappedPubKey, k, key1, CScript(), expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey);
BuildPKTransaction(p2shPubKey, k, key1, p2shScriptSig, expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey);
BuildPKTransaction(segwitPubKey, k, key1, segwitScriptSig, expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey);
BuildPKTransaction(p2shsegwitPubKey, k, key1, p2shsegwitScriptSig, expect, SCRIPT_ERR_EVAL_FALSE, unwrappedPubKey);
}
}
}
BOOST_AUTO_TEST_CASE(norecurse)

View File

@ -99,6 +99,17 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bo
stream << tx2;
BOOST_CHECK_MESSAGE(zcashconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message);
#endif
// exercise the FORKID hashtype requirement
opcodetype op;
auto back = scriptPubKey.end() - 1;
if(scriptPubKey.GetOp(back, op) &&
op >= OP_CHECKSIG && op <= OP_CHECKSIGVERIFY) {
if(expect && scriptPubKey.GetSigOpCount(scriptSig) > 0) {
BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, flags | SCRIPT_VERIFY_FORKID, MutableTransactionSignatureChecker(&tx, 0), &err), "FORKID" + message);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_HASHTYPE, std::string(ScriptErrorString(err)) + ": " + message);
}
}
}
void static NegateSignatureS(std::vector<unsigned char>& vchSig) {
@ -787,7 +798,7 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
}
}
BOOST_AUTO_TEST_CASE(script_combineSigs)
{

View File

@ -169,7 +169,7 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const vector<unsigned char> &vchCryptedSecret)
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
if (!fFileBacked)
@ -477,7 +477,7 @@ bool CWallet::Verify(const string& walletFile, string& warningString, string& er
} catch (const boost::filesystem::filesystem_error&) {
// failure is ok (well, not really, but it's not worse than what we started with)
}
// try again
if (!bitdb.Open(GetDataDir())) {
// if it still fails, it probably means we can't even create the database env
@ -486,14 +486,14 @@ bool CWallet::Verify(const string& walletFile, string& warningString, string& er
return true;
}
}
if (GetBoolArg("-salvagewallet", false))
{
// Recover readable keypairs:
if (!CWalletDB::Recover(bitdb, walletFile, true))
return false;
}
if (boost::filesystem::exists(GetDataDir() / walletFile))
{
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover);
@ -507,7 +507,7 @@ bool CWallet::Verify(const string& walletFile, string& warningString, string& er
if (r == CDBEnv::RECOVER_FAIL)
errorString += _("wallet.dat corrupt, salvage failed");
}
return true;
}
@ -2993,7 +2993,7 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
/**
* Mark old keypool keys as used,
* and generate all new keys
* and generate all new keys
*/
bool CWallet::NewKeyPool()
{
@ -3406,7 +3406,26 @@ public:
Process(script);
}
void operator()(const CNoDestination &none) {}
void operator()(const WitnessV0ScriptHash& scriptID)
{
CScriptID id;
CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin());
CScript script;
if (keystore.GetCScript(id, script)) {
Process(script);
}
}
void operator()(const WitnessV0KeyHash& keyid)
{
CKeyID id(keyid);
if (keystore.HaveKey(id)) {
vKeys.push_back(id);
}
}
template<typename X>
void operator()(const X &none) {}
};
void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {