aboutsummaryrefslogtreecommitdiff
path: root/src/script/sign.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/sign.cpp')
-rw-r--r--src/script/sign.cpp720
1 files changed, 486 insertions, 234 deletions
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 910bb39ce6..d779910425 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -11,15 +11,14 @@
#include <script/standard.h>
#include <uint256.h>
-
typedef std::vector<unsigned char> valtype;
-TransactionSignatureCreator::TransactionSignatureCreator(const SigningProvider* provider, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(provider), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
-bool TransactionSignatureCreator::CreateSig(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) const
{
CKey key;
- if (!m_provider->GetKey(address, key))
+ if (!provider.GetKey(address, key))
return false;
// Signing with uncompressed keys is disabled in witness scripts
@@ -33,27 +32,62 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig,
return true;
}
-static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
+static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
{
- std::vector<unsigned char> vchSig;
- if (!creator.CreateSig(vchSig, address, scriptCode, sigversion))
- return false;
- ret.push_back(vchSig);
- return true;
+ if (provider.GetCScript(scriptid, script)) {
+ return true;
+ }
+ // Look for scripts in SignatureData
+ if (CScriptID(sigdata.redeem_script) == scriptid) {
+ script = sigdata.redeem_script;
+ return true;
+ } else if (CScriptID(sigdata.witness_script) == scriptid) {
+ script = sigdata.witness_script;
+ return true;
+ }
+ return false;
}
-static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
+static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
{
- int nSigned = 0;
- int nRequired = multisigdata.front()[0];
- for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++)
- {
- const valtype& pubkey = multisigdata[i];
- CKeyID keyID = CPubKey(pubkey).GetID();
- if (Sign1(keyID, creator, scriptCode, ret, sigversion))
- ++nSigned;
+ // Look for pubkey in all partial sigs
+ const auto it = sigdata.signatures.find(address);
+ if (it != sigdata.signatures.end()) {
+ pubkey = it->second.first;
+ return true;
}
- return nSigned==nRequired;
+ // Look for pubkey in pubkey list
+ const auto& pk_it = sigdata.misc_pubkeys.find(address);
+ if (pk_it != sigdata.misc_pubkeys.end()) {
+ pubkey = pk_it->second.first;
+ return true;
+ }
+ // Query the underlying provider
+ if (provider.GetPubKey(address, pubkey)) {
+ KeyOriginInfo info;
+ if (provider.GetKeyOrigin(address, info)) {
+ sigdata.misc_pubkeys.emplace(address, std::make_pair(pubkey, std::move(info)));
+ }
+ return true;
+ }
+ return false;
+}
+
+static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion)
+{
+ const auto it = sigdata.signatures.find(keyid);
+ if (it != sigdata.signatures.end()) {
+ sig_out = it->second.second;
+ return true;
+ }
+ CPubKey pubkey;
+ GetPubKey(provider, sigdata, keyid, pubkey);
+ if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
+ auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
+ assert(i.second);
+ return true;
+ }
+ return false;
}
/**
@@ -62,18 +96,17 @@ static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureC
* unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
* Returns false if scriptPubKey could not be completely satisfied.
*/
-static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey,
- std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion)
+static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
+ std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{
CScript scriptRet;
uint160 h160;
ret.clear();
+ std::vector<unsigned char> sig;
std::vector<valtype> vSolutions;
- if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
- return false;
+ whichTypeRet = Solver(scriptPubKey, vSolutions);
- CKeyID keyID;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
@@ -81,37 +114,47 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
case TX_WITNESS_UNKNOWN:
return false;
case TX_PUBKEY:
- keyID = CPubKey(vSolutions[0]).GetID();
- return Sign1(keyID, creator, scriptPubKey, ret, sigversion);
- case TX_PUBKEYHASH:
- keyID = CKeyID(uint160(vSolutions[0]));
- if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion))
- return false;
- else
- {
- CPubKey vch;
- creator.Provider().GetPubKey(keyID, vch);
- ret.push_back(ToByteVector(vch));
- }
+ if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
return true;
+ case TX_PUBKEYHASH: {
+ CKeyID keyID = CKeyID(uint160(vSolutions[0]));
+ if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
+ CPubKey pubkey;
+ GetPubKey(provider, sigdata, keyID, pubkey);
+ ret.push_back(ToByteVector(pubkey));
+ return true;
+ }
case TX_SCRIPTHASH:
- if (creator.Provider().GetCScript(uint160(vSolutions[0]), scriptRet)) {
+ if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
return false;
- case TX_MULTISIG:
+ case TX_MULTISIG: {
+ size_t required = vSolutions.front()[0];
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
- return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion));
-
+ 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.GetID(), scriptPubKey, sigversion)) {
+ ret.push_back(std::move(sig));
+ }
+ }
+ bool ok = ret.size() == required + 1;
+ for (size_t i = 0; i + ret.size() < required + 1; ++i) {
+ ret.push_back(valtype());
+ }
+ return ok;
+ }
case TX_WITNESS_V0_KEYHASH:
ret.push_back(vSolutions[0]);
return true;
case TX_WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
- if (creator.Provider().GetCScript(h160, scriptRet)) {
+ if (GetCScript(provider, sigdata, h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -137,11 +180,13 @@ static CScript PushAll(const std::vector<valtype>& values)
return result;
}
-bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
+bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
{
+ if (sigdata.complete) return true;
+
std::vector<valtype> result;
txnouttype whichType;
- bool solved = SignStep(creator, fromPubKey, result, whichType, SigVersion::BASE);
+ bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
@@ -152,7 +197,8 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
// the final scriptSig is the signatures from that
// and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end());
- solved = solved && SignStep(creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH;
+ sigdata.redeem_script = subscript;
+ solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH;
P2SH = true;
}
@@ -161,18 +207,23 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
txnouttype subType;
- solved = solved && SignStep(creator, witnessscript, result, subType, SigVersion::WITNESS_V0);
+ solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
+ sigdata.witness = true;
result.clear();
}
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
{
CScript witnessscript(result[0].begin(), result[0].end());
+ sigdata.witness_script = witnessscript;
txnouttype subType;
- solved = solved && SignStep(creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && 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) && 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;
result.clear();
+ } else if (solved && whichType == TX_WITNESS_UNKNOWN) {
+ sigdata.witness = true;
}
if (P2SH) {
@@ -181,15 +232,162 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
sigdata.scriptSig = PushAll(result);
// Test solution
- return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
+ sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
+ return sigdata.complete;
+}
+
+bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash)
+{
+ // if this input has a final scriptsig or scriptwitness, don't do anything with it
+ if (!input.final_script_sig.empty() || !input.final_script_witness.IsNull()) {
+ return true;
+ }
+
+ // Fill SignatureData with input info
+ SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+
+ // Get UTXO
+ bool require_witness_sig = false;
+ CTxOut utxo;
+ if (input.non_witness_utxo) {
+ // If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
+ if (input.non_witness_utxo->GetHash() != tx.vin[index].prevout.hash) return false;
+ // If both witness and non-witness UTXO are provided, verify that they match. This check shouldn't
+ // matter, as the PSBT deserializer enforces only one of both is provided, and the only way both
+ // can be present is when they're added simultaneously by FillPSBT (in which case they always match).
+ // Still, check in order to not rely on callers to enforce this.
+ if (!input.witness_utxo.IsNull() && input.non_witness_utxo->vout[tx.vin[index].prevout.n] != input.witness_utxo) return false;
+ utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
+ } else if (!input.witness_utxo.IsNull()) {
+ utxo = input.witness_utxo;
+ // When we're taking our information from a witness UTXO, we can't verify it is actually data from
+ // the output being spent. This is safe in case a witness signature is produced (which includes this
+ // information directly in the hash), but not for non-witness signatures. Remember that we require
+ // a witness signature in this situation.
+ require_witness_sig = true;
+ } else {
+ return false;
+ }
+
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ sigdata.witness = false;
+ bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
+ // Verify that a witness signature was produced in case one was required.
+ if (require_witness_sig && !sigdata.witness) return false;
+ input.FromSignatureData(sigdata);
+
+ // If both UTXO types are present, drop the unnecessary one.
+ if (input.non_witness_utxo && !input.witness_utxo.IsNull()) {
+ if (sigdata.witness) {
+ input.non_witness_utxo = nullptr;
+ } else {
+ input.witness_utxo.SetNull();
+ }
+ }
+
+ return sig_complete;
+}
+
+class SignatureExtractorChecker final : public BaseSignatureChecker
+{
+private:
+ SignatureData& sigdata;
+ BaseSignatureChecker& checker;
+
+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 SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
+{
+ if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
+ CPubKey pubkey(vchPubKey);
+ sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
+ return true;
+ }
+ return false;
}
-SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
+namespace
+{
+struct Stacks
+{
+ std::vector<valtype> script;
+ std::vector<valtype> witness;
+
+ Stacks() = delete;
+ Stacks(const Stacks&) = delete;
+ explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
+ EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
+ }
+};
+}
+
+// Extracts signatures and scripts from incomplete scriptSigs. Please do not extend this, use PSBT instead
+SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout)
{
SignatureData data;
assert(tx.vin.size() > nIn);
data.scriptSig = tx.vin[nIn].scriptSig;
data.scriptWitness = tx.vin[nIn].scriptWitness;
+ Stacks stack(data);
+
+ // Get signatures
+ MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue);
+ SignatureExtractorChecker extractor_checker(data, tx_checker);
+ if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) {
+ data.complete = true;
+ return data;
+ }
+
+ // Get scripts
+ std::vector<std::vector<unsigned char>> solutions;
+ txnouttype script_type = Solver(txout.scriptPubKey, solutions);
+ SigVersion sigversion = SigVersion::BASE;
+ CScript next_script = txout.scriptPubKey;
+
+ if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
+ // Get the redeemScript
+ CScript redeem_script(stack.script.back().begin(), stack.script.back().end());
+ data.redeem_script = redeem_script;
+ next_script = std::move(redeem_script);
+
+ // Get redeemScript type
+ script_type = Solver(next_script, solutions);
+ stack.script.pop_back();
+ }
+ if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
+ // Get the witnessScript
+ CScript witness_script(stack.witness.back().begin(), stack.witness.back().end());
+ data.witness_script = witness_script;
+ next_script = std::move(witness_script);
+
+ // Get witnessScript type
+ script_type = Solver(next_script, solutions);
+ stack.witness.pop_back();
+ stack.script = std::move(stack.witness);
+ stack.witness.clear();
+ sigversion = SigVersion::WITNESS_V0;
+ }
+ if (script_type == TX_MULTISIG && !stack.script.empty()) {
+ // Build a map of pubkey -> signature by matching sigs to pubkeys:
+ assert(solutions.size() > 1);
+ unsigned int num_pubkeys = solutions.size()-2;
+ unsigned int last_success_key = 0;
+ for (const valtype& sig : stack.script) {
+ 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)) {
+ last_success_key = i + 1;
+ break;
+ }
+ }
+ }
+ }
+
return data;
}
@@ -199,22 +397,31 @@ void UpdateInput(CTxIn& input, const SignatureData& data)
input.scriptWitness = data.scriptWitness;
}
-void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data)
+void SignatureData::MergeSignatureData(SignatureData sigdata)
{
- assert(tx.vin.size() > nIn);
- UpdateInput(tx.vin[nIn], data);
+ if (complete) return;
+ if (sigdata.complete) {
+ *this = std::move(sigdata);
+ return;
+ }
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+ signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end()));
}
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
{
assert(nIn < txTo.vin.size());
- CTransaction txToConst(txTo);
- TransactionSignatureCreator creator(&provider, &txToConst, nIn, amount, nHashType);
+ MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType);
SignatureData sigdata;
- bool ret = ProduceSignature(creator, fromPubKey, sigdata);
- UpdateTransaction(txTo, nIn, sigdata);
+ bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata);
+ UpdateInput(txTo.vin.at(nIn), sigdata);
return ret;
}
@@ -228,219 +435,264 @@ bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom,
return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
}
-static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const std::vector<valtype>& vSolutions,
- const std::vector<valtype>& sigs1, const std::vector<valtype>& sigs2, SigVersion sigversion)
+namespace {
+/** Dummy signature checker which accepts all signatures. */
+class DummySignatureChecker final : public BaseSignatureChecker
{
- // Combine all the signatures we've got:
- std::set<valtype> allsigs;
- for (const valtype& v : sigs1)
- {
- if (!v.empty())
- allsigs.insert(v);
- }
- for (const valtype& v : sigs2)
+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; }
+};
+const DummySignatureChecker DUMMY_CHECKER;
+
+class DummySignatureCreator final : public BaseSignatureCreator {
+private:
+ char m_r_len = 32;
+ char m_s_len = 32;
+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
{
- if (!v.empty())
- allsigs.insert(v);
+ // Create a dummy signature that is a valid DER-encoding
+ vchSig.assign(m_r_len + m_s_len + 7, '\000');
+ vchSig[0] = 0x30;
+ vchSig[1] = m_r_len + m_s_len + 4;
+ vchSig[2] = 0x02;
+ vchSig[3] = m_r_len;
+ vchSig[4] = 0x01;
+ vchSig[4 + m_r_len] = 0x02;
+ vchSig[5 + m_r_len] = m_s_len;
+ vchSig[6 + m_r_len] = 0x01;
+ vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL;
+ return true;
}
+};
- // Build a map of pubkey -> signature by matching sigs to pubkeys:
- assert(vSolutions.size() > 1);
- unsigned int nSigsRequired = vSolutions.front()[0];
- unsigned int nPubKeys = vSolutions.size()-2;
- std::map<valtype, valtype> sigs;
- for (const valtype& sig : allsigs)
- {
- for (unsigned int i = 0; i < nPubKeys; i++)
- {
- const valtype& pubkey = vSolutions[i+1];
- if (sigs.count(pubkey))
- continue; // Already got a sig for this pubkey
-
- if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion))
- {
- sigs[pubkey] = sig;
- break;
- }
- }
+template<typename M, typename K, typename V>
+bool LookupHelper(const M& map, const K& key, V& value)
+{
+ auto it = map.find(key);
+ if (it != map.end()) {
+ value = it->second;
+ return true;
}
- // Now build a merged CScript:
- unsigned int nSigsHave = 0;
- std::vector<valtype> result; result.push_back(valtype()); // pop-one-too-many workaround
- for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++)
- {
- if (sigs.count(vSolutions[i+1]))
- {
- result.push_back(sigs[vSolutions[i+1]]);
- ++nSigsHave;
- }
+ return false;
+}
+
+}
+
+const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(32, 32);
+const BaseSignatureCreator& DUMMY_MAXIMUM_SIGNATURE_CREATOR = DummySignatureCreator(33, 32);
+const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
+
+bool IsSolvable(const SigningProvider& provider, const CScript& script)
+{
+ // This check is to make sure that the script we created can actually be solved for and signed by us
+ // if we were to have the private keys. This is just to make sure that the script is valid and that,
+ // if found in a transaction, we would still accept and relay that transaction. In particular,
+ // it will reject witness outputs that require signing with an uncompressed public key.
+ SignatureData sigs;
+ // Make sure that STANDARD_SCRIPT_VERIFY_FLAGS includes SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, the most
+ // important property this function is designed to test for.
+ static_assert(STANDARD_SCRIPT_VERIFY_FLAGS & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, "IsSolvable requires standard script flags to include WITNESS_PUBKEYTYPE");
+ if (ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, script, sigs)) {
+ // VerifyScript check is just defensive, and should never fail.
+ bool verified = VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, DUMMY_CHECKER);
+ assert(verified);
+ return true;
}
- // Fill any missing with OP_0:
- for (unsigned int i = nSigsHave; i < nSigsRequired; i++)
- result.push_back(valtype());
+ return false;
+}
- return result;
+bool PartiallySignedTransaction::IsNull() const
+{
+ return !tx && inputs.empty() && outputs.empty() && unknown.empty();
}
-namespace
+void PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
{
-struct Stacks
+ for (unsigned int i = 0; i < inputs.size(); ++i) {
+ inputs[i].Merge(psbt.inputs[i]);
+ }
+ for (unsigned int i = 0; i < outputs.size(); ++i) {
+ outputs[i].Merge(psbt.outputs[i]);
+ }
+ unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
+}
+
+bool PartiallySignedTransaction::IsSane() const
{
- std::vector<valtype> script;
- std::vector<valtype> witness;
+ for (PSBTInput input : inputs) {
+ if (!input.IsSane()) return false;
+ }
+ return true;
+}
- Stacks() {}
- explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {}
- explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
- EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
+bool PSBTInput::IsNull() const
+{
+ return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
+}
+
+void PSBTInput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!final_script_sig.empty()) {
+ sigdata.scriptSig = final_script_sig;
+ sigdata.complete = true;
+ }
+ if (!final_script_witness.IsNull()) {
+ sigdata.scriptWitness = final_script_witness;
+ sigdata.complete = true;
+ }
+ if (sigdata.complete) {
+ return;
}
- SignatureData Output() const {
- SignatureData result;
- result.scriptSig = PushAll(script);
- result.scriptWitness.stack = witness;
- return result;
+ sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
+ }
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
-};
}
-static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const txnouttype txType, const std::vector<valtype>& vSolutions,
- Stacks sigs1, Stacks sigs2, SigVersion sigversion)
+void PSBTInput::FromSignatureData(const SignatureData& sigdata)
{
- switch (txType)
- {
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
- // Don't know anything about this, assume bigger one is correct:
- if (sigs1.script.size() >= sigs2.script.size())
- return sigs1;
- return sigs2;
- case TX_PUBKEY:
- case TX_PUBKEYHASH:
- // Signatures are bigger than placeholders or empty scripts:
- if (sigs1.script.empty() || sigs1.script[0].empty())
- return sigs2;
- return sigs1;
- case TX_WITNESS_V0_KEYHASH:
- // Signatures are bigger than placeholders or empty scripts:
- if (sigs1.witness.empty() || sigs1.witness[0].empty())
- return sigs2;
- return sigs1;
- case TX_SCRIPTHASH:
- if (sigs1.script.empty() || sigs1.script.back().empty())
- return sigs2;
- else if (sigs2.script.empty() || sigs2.script.back().empty())
- return sigs1;
- else
- {
- // Recur to combine:
- valtype spk = sigs1.script.back();
- CScript pubKey2(spk.begin(), spk.end());
-
- txnouttype txType2;
- std::vector<std::vector<unsigned char> > vSolutions2;
- Solver(pubKey2, txType2, vSolutions2);
- sigs1.script.pop_back();
- sigs2.script.pop_back();
- Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion);
- result.script.push_back(spk);
- return result;
+ if (sigdata.complete) {
+ partial_sigs.clear();
+ hd_keypaths.clear();
+ redeem_script.clear();
+ witness_script.clear();
+
+ if (!sigdata.scriptSig.empty()) {
+ final_script_sig = sigdata.scriptSig;
}
- case TX_MULTISIG:
- return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, sigversion));
- case TX_WITNESS_V0_SCRIPTHASH:
- if (sigs1.witness.empty() || sigs1.witness.back().empty())
- return sigs2;
- else if (sigs2.witness.empty() || sigs2.witness.back().empty())
- return sigs1;
- else
- {
- // Recur to combine:
- CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end());
- txnouttype txType2;
- std::vector<valtype> vSolutions2;
- Solver(pubKey2, txType2, vSolutions2);
- sigs1.witness.pop_back();
- sigs1.script = sigs1.witness;
- sigs1.witness.clear();
- sigs2.witness.pop_back();
- sigs2.script = sigs2.witness;
- sigs2.witness.clear();
- Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SigVersion::WITNESS_V0);
- result.witness = result.script;
- result.script.clear();
- result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end()));
- return result;
+ if (!sigdata.scriptWitness.IsNull()) {
+ final_script_witness = sigdata.scriptWitness;
}
- default:
- return Stacks();
+ return;
+ }
+
+ partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+ for (const auto& entry : sigdata.misc_pubkeys) {
+ hd_keypaths.emplace(entry.second);
}
}
-SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const SignatureData& scriptSig1, const SignatureData& scriptSig2)
+void PSBTInput::Merge(const PSBTInput& input)
{
- txnouttype txType;
- std::vector<std::vector<unsigned char> > vSolutions;
- Solver(scriptPubKey, txType, vSolutions);
+ if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
+ if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
+ witness_utxo = input.witness_utxo;
+ non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
+ }
- return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SigVersion::BASE).Output();
+ partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
+ hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
+ unknown.insert(input.unknown.begin(), input.unknown.end());
+
+ if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
+ if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
+ if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
+ if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
}
-namespace {
-/** Dummy signature checker which accepts all signatures. */
-class DummySignatureChecker : public BaseSignatureChecker
+bool PSBTInput::IsSane() const
{
-public:
- DummySignatureChecker() {}
+ // Cannot have both witness and non-witness utxos
+ if (!witness_utxo.IsNull() && non_witness_utxo) return false;
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
- {
- return true;
+ // If we have a witness_script or a scriptWitness, we must also have a witness utxo
+ if (!witness_script.empty() && witness_utxo.IsNull()) return false;
+ if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
+
+ return true;
+}
+
+void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
}
-};
-const DummySignatureChecker dummyChecker;
-} // namespace
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
+ }
+}
-const BaseSignatureChecker& DummySignatureCreator::Checker() const
+void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
{
- return dummyChecker;
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+ for (const auto& entry : sigdata.misc_pubkeys) {
+ hd_keypaths.emplace(entry.second);
+ }
}
-bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const
+bool PSBTOutput::IsNull() const
{
- // Create a dummy signature that is a valid DER-encoding
- vchSig.assign(72, '\000');
- vchSig[0] = 0x30;
- vchSig[1] = 69;
- vchSig[2] = 0x02;
- vchSig[3] = 33;
- vchSig[4] = 0x01;
- vchSig[4 + 33] = 0x02;
- vchSig[5 + 33] = 32;
- vchSig[6 + 33] = 0x01;
- vchSig[6 + 33 + 32] = SIGHASH_ALL;
- return true;
+ return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
}
-bool IsSolvable(const SigningProvider& provider, const CScript& script)
+void PSBTOutput::Merge(const PSBTOutput& output)
{
- // This check is to make sure that the script we created can actually be solved for and signed by us
- // if we were to have the private keys. This is just to make sure that the script is valid and that,
- // if found in a transaction, we would still accept and relay that transaction. In particular,
- // it will reject witness outputs that require signing with an uncompressed public key.
- DummySignatureCreator creator(&provider);
- SignatureData sigs;
- // Make sure that STANDARD_SCRIPT_VERIFY_FLAGS includes SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, the most
- // important property this function is designed to test for.
- static_assert(STANDARD_SCRIPT_VERIFY_FLAGS & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, "IsSolvable requires standard script flags to include WITNESS_PUBKEYTYPE");
- if (ProduceSignature(creator, script, sigs)) {
- // VerifyScript check is just defensive, and should never fail.
- assert(VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()));
- return true;
- }
- return false;
+ hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
+ unknown.insert(output.unknown.begin(), output.unknown.end());
+
+ if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
+ if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
+}
+
+bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
+{
+ return m_provider->GetCScript(scriptid, script);
+}
+
+bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
+{
+ return m_provider->GetPubKey(keyid, pubkey);
+}
+
+bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
+{
+ if (m_hide_secret) return false;
+ return m_provider->GetKey(keyid, key);
+}
+
+bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
+{
+ if (m_hide_origin) return false;
+ return m_provider->GetKeyOrigin(keyid, info);
+}
+
+bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
+bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
+bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
+
+FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
+{
+ FlatSigningProvider ret;
+ ret.scripts = a.scripts;
+ ret.scripts.insert(b.scripts.begin(), b.scripts.end());
+ ret.pubkeys = a.pubkeys;
+ ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
+ ret.keys = a.keys;
+ ret.keys.insert(b.keys.begin(), b.keys.end());
+ return ret;
}