From b8deecdc0934a1408066e4ef8679160019d0d60c Mon Sep 17 00:00:00 2001 From: miketout Date: Thu, 20 Sep 2018 03:08:36 -0700 Subject: [PATCH] Sapling transaction testing --- src/base58.cpp | 8 ++++ src/base58.h | 1 + src/chainparams.cpp | 4 +- src/key_io.cpp | 18 ++++---- src/main.cpp | 3 ++ src/rpc/misc.cpp | 16 ++++++- src/script/standard.cpp | 14 ++++++- src/script/standard.h | 4 +- src/wallet/asyncrpcoperation_sendmany.cpp | 25 +++++++---- src/wallet/asyncrpcoperation_sendmany.h | 2 +- .../asyncrpcoperation_shieldcoinbase.cpp | 2 +- src/wallet/rpcwallet.cpp | 8 ++-- src/wallet/wallet.cpp | 6 +++ src/wallet/wallet.h | 42 +++++++++++++++++++ 14 files changed, 124 insertions(+), 29 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index 50697fb9e..12978fa1c 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -213,6 +213,7 @@ public: CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} bool operator()(const CKeyID& id) const { return addr->Set(id); } + bool operator()(const CPubKey& key) const { return addr->Set(key); } bool operator()(const CScriptID& id) const { return addr->Set(id); } bool operator()(const CNoDestination& no) const { return false; } }; @@ -225,6 +226,13 @@ bool CBitcoinAddress::Set(const CKeyID& id) return true; } +bool CBitcoinAddress::Set(const CPubKey& key) +{ + CKeyID id = key.GetID(); + SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); + return true; +} + bool CBitcoinAddress::Set(const CScriptID& id) { SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); diff --git a/src/base58.h b/src/base58.h index 1f9002e0f..66cb7e7a0 100644 --- a/src/base58.h +++ b/src/base58.h @@ -116,6 +116,7 @@ public: class CBitcoinAddress : public CBase58Data { public: bool Set(const CKeyID &id); + bool Set(const CPubKey &key); bool Set(const CScriptID &id); bool Set(const CTxDestination &dest); bool IsValid() const; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 03ee98ceb..d4b9772f8 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -115,9 +115,9 @@ public: consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170005; - consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = 227520; + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = 1; consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion = 170007; - consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = 227520; + consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = 1; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000281b32ff3198a1"); diff --git a/src/key_io.cpp b/src/key_io.cpp index 636163638..e31b8d737 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -34,6 +34,14 @@ public: return EncodeBase58Check(data); } + std::string operator()(const CPubKey& key) const + { + std::vector data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + CKeyID id = key.GetID(); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } + std::string operator()(const CScriptID& id) const { std::vector data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); @@ -286,10 +294,7 @@ libzcash::PaymentAddress DecodePaymentAddress(const std::string& str) } data.clear(); auto bech = bech32::Decode(str); - bool allowSapling = Params().NetworkIDString() == "regtest" || ( - Params().NetworkIDString() == "test" && - GetBoolArg("-experimentalfeatures", false) && - GetBoolArg("-developersapling", false)); + bool allowSapling = true; if (allowSapling && bech.first == Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS) && bech.second.size() == ConvertedSaplingPaymentAddressSize) { // Bech32 decoding @@ -356,10 +361,7 @@ libzcash::SpendingKey DecodeSpendingKey(const std::string& str) } data.clear(); auto bech = bech32::Decode(str); - bool allowSapling = Params().NetworkIDString() == "regtest" || ( - Params().NetworkIDString() == "test" && - GetBoolArg("-experimentalfeatures", false) && - GetBoolArg("-developersapling", false)); + bool allowSapling = true; if (allowSapling && bech.first == Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY) && bech.second.size() == ConvertedSaplingExtendedSpendingKeySize) { // Bech32 decoding diff --git a/src/main.cpp b/src/main.cpp index 40ed9d6a9..3248a6e2d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -870,6 +870,8 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, txnouttype whichType; // get the scriptPubKey corresponding to this input: const CScript& prevScript = prev.scriptPubKey; + //printf("Previous script: %s\n", prevScript.ToString().c_str()); + if (!Solver(prevScript, whichType, vSolutions)) return false; int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); @@ -883,6 +885,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, // IsStandardTx() will have already returned false // and this method isn't called. vector > stack; + //printf("Checking script: %s\n", tx.vin[i].scriptSig.ToString().c_str()); if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), consensusBranchId)) return false; diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 4e406bbda..2613fa3aa 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -224,12 +224,26 @@ public: CPubKey vchPubKey; obj.push_back(Pair("isscript", false)); if (pwalletMain && pwalletMain->GetPubKey(keyID, vchPubKey)) { - obj.push_back(Pair("pubkey", HexStr(vchPubKey))); + obj.push_back(Pair("pubkey", HexStr(vchPubKey))); // should return pubkeyhash, but not sure about compatibility impact obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); } return obj; } + UniValue operator()(const CPubKey &key) const { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("isscript", false)); + if (pwalletMain && key.IsValid()) { + obj.push_back(Pair("pubkey", HexStr(key))); + obj.push_back(Pair("iscompressed", key.IsCompressed())); + } + else + { + obj.push_back(Pair("pubkey", "invalid")); + } + return obj; + } + UniValue operator()(const CScriptID &scriptID) const { UniValue obj(UniValue::VOBJ); CScript subscript; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 88c89ad39..673c4ce20 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -234,7 +234,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) return whichType != TX_NONSTANDARD; } -bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet) +bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet, bool returnPubKey) { vector vSolutions; txnouttype whichType; @@ -262,9 +262,13 @@ bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet return false; } - addressRet = pubKey.GetID(); + if (returnPubKey) + addressRet = pubKey; + else + addressRet = pubKey.GetID(); return true; } + else if (whichType == TX_PUBKEYHASH) { addressRet = CKeyID(uint160(vSolutions[0])); @@ -355,6 +359,12 @@ public: return false; } + bool operator()(const CPubKey &key) const { + script->clear(); + *script << ToByteVector(key) << OP_CHECKSIG; + return true; + } + bool operator()(const CKeyID &keyID) const { script->clear(); *script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; diff --git a/src/script/standard.h b/src/script/standard.h index 4f6c80b48..10537041e 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -81,7 +81,7 @@ public: * * CScriptID: TX_SCRIPTHASH destination * A CTxDestination is the internal data type encoded in a bitcoin address */ -typedef boost::variant CTxDestination; +typedef boost::variant CTxDestination; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); @@ -91,7 +91,7 @@ const char* GetTxnOutputType(txnouttype t); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); -bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet, bool returnPubKey=false); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); CScript GetScriptForDestination(const CTxDestination& dest); diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 9d5ca7eb7..060b6196b 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -342,8 +342,10 @@ bool AsyncRPCOperation_sendmany::main_impl() { // update the transaction with these inputs if (isUsingBuilder_) { - CScript scriptPubKey = GetScriptForDestination(fromtaddr_); + CScript scriptPubKey; for (auto t : t_inputs_) { + scriptPubKey = GetScriptForDestination(std::get<4>(t)); + //printf("Checking new script: %s\n", scriptPubKey.ToString().c_str()); uint256 txid = std::get<0>(t); int vout = std::get<1>(t); CAmount amount = std::get<2>(t); @@ -993,17 +995,20 @@ void AsyncRPCOperation_sendmany::sign_send_raw_transaction(UniValue obj) tx_ = tx; } - bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) { std::set destinations; destinations.insert(fromtaddr_); + + //printf("Looking for %s\n", boost::apply_visitor(AddressVisitorString(), fromtaddr_).c_str()); + vector vecOutputs; LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->AvailableCoins(vecOutputs, false, NULL, true, fAcceptCoinbase); BOOST_FOREACH(const COutput& out, vecOutputs) { + CTxDestination dest; + if (!out.fSpendable) { continue; } @@ -1012,13 +1017,15 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) { continue; } + const CScript &scriptPubKey = out.tx->vout[out.i].scriptPubKey; + if (destinations.size()) { - CTxDestination address; - if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { + if (!ExtractDestination(scriptPubKey, dest)) { continue; } - if (!destinations.count(address)) { + //printf("%s\n", boost::apply_visitor(AddressVisitorString(), dest).c_str()); + if (!destinations.count(dest)) { continue; } } @@ -1029,8 +1036,12 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) { continue; } + if (!ExtractDestination(scriptPubKey, dest, true)) + continue; + CAmount nValue = out.tx->vout[out.i].nValue; - SendManyInputUTXO utxo(out.tx->GetHash(), out.i, nValue, isCoinbase); + + SendManyInputUTXO utxo(out.tx->GetHash(), out.i, nValue, isCoinbase, dest); t_inputs_.push_back(utxo); } diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index a6ae51432..35bdb9740 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -29,7 +29,7 @@ using namespace libzcash; typedef std::tuple SendManyRecipient; // Input UTXO is a tuple (quadruple) of txid, vout, amount, coinbase) -typedef std::tuple SendManyInputUTXO; +typedef std::tuple SendManyInputUTXO; // Input JSOP is a tuple of JSOutpoint, note and amount typedef std::tuple SendManyInputJSOP; diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 736e483c7..ff873ab03 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -77,7 +77,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( auto address = DecodePaymentAddress(toAddress); if (IsValidPaymentAddress(address)) { // TODO: Add Sapling support. For now, ensure we can freely convert. - assert(boost::get(&address) != nullptr); + // assert(boost::get(&address) != nullptr); tozaddr_ = address; } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid to address"); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e4fa941db..4acbe5550 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3451,7 +3451,9 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - std::string defaultType = ADDR_TYPE_SPROUT; + bool allowSapling = (Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight <= chainActive.LastTip()->nHeight); + + std::string defaultType = allowSapling ? ADDR_TYPE_SAPLING : ADDR_TYPE_SPROUT; if (fHelp || params.size() > 1) throw runtime_error( @@ -3478,10 +3480,6 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) addrType = params[0].get_str(); } - bool allowSapling = Params().NetworkIDString() == "regtest" || ( - Params().NetworkIDString() == "test" && - GetBoolArg("-experimentalfeatures", false) && - GetBoolArg("-developersapling", false)); if (addrType == ADDR_TYPE_SPROUT) { return EncodePaymentAddress(pwalletMain->GenerateNewZKey()); } else if (addrType == ADDR_TYPE_SAPLING && allowSapling) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f67a22cf0..c609f5c46 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4406,6 +4406,12 @@ public: vKeys.push_back(keyId); } + void operator()(const CPubKey &key) { + CKeyID keyId = key.GetID(); + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + void operator()(const CScriptID &scriptId) { CScript script; if (keystore.GetCScript(scriptId, script)) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9248aed12..c254f98ea 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1387,4 +1387,46 @@ public: boost::optional operator()(const libzcash::InvalidEncoding& no) const; }; +class GetPubKeyForPubKey : public boost::static_visitor { +private: + const CKeyStore &keystore; + +public: + GetPubKeyForPubKey(const CKeyStore &keystoreIn) : keystore(keystoreIn) {} + + CPubKey operator()(const CKeyID &id) const { + return CPubKey(); + } + + CPubKey operator()(const CPubKey &key) const { + return key; + } + + CPubKey operator()(const CScriptID &sid) const { + return CPubKey(); + } + + CPubKey operator()(const CNoDestination &no) const { + return CPubKey(); + } +}; + +class AddressVisitorString : public boost::static_visitor +{ +public: + std::string operator()(const CNoDestination &dest) const { return ""; } + + std::string operator()(const CKeyID &keyID) const { + return "key hash: " + keyID.ToString(); + } + + std::string operator()(const CPubKey &key) const { + return "public key: " + HexStr(key); + } + + std::string operator()(const CScriptID &scriptID) const { + return "script hash: " + scriptID.ToString(); + } +}; + #endif // BITCOIN_WALLET_WALLET_H