Replace CombineSignatures with ProduceSignature

Instead of using CombineSignatures to create the final scriptSig or
scriptWitness of an input, use ProduceSignature itself.

To allow for ProduceSignature to place signatures, pubkeys, and scripts
that it does not know about, we pass down the SignatureData to SignStep
which pulls out the information that it needs from the SignatureData.
This commit is contained in:
Andrew Chow 2018-06-07 21:12:25 -07:00
parent 0422beb9bd
commit ed94c8b556
4 changed files with 85 additions and 21 deletions

View file

@ -650,8 +650,6 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (!fHashSingle || (i < mergedTx.vout.size())) if (!fHashSingle || (i < mergedTx.vout.size()))
ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata); ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata);
// ... and merge in other signatures:
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i, coin.out));
UpdateInput(txin, sigdata); UpdateInput(txin, sigdata);
} }

View file

@ -736,17 +736,15 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
if (coin.IsSpent()) { if (coin.IsSpent()) {
throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent"); throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent");
} }
const CScript& prevPubKey = coin.out.scriptPubKey;
const CAmount& amount = coin.out.nValue;
SignatureData sigdata; SignatureData sigdata;
// ... and merge in other signatures: // ... and merge in other signatures:
for (const CMutableTransaction& txv : txVariants) { for (const CMutableTransaction& txv : txVariants) {
if (txv.vin.size() > i) { if (txv.vin.size() > i) {
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i, coin.out)); sigdata.MergeSignatureData(DataFromTransaction(txv, i, coin.out));
} }
} }
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&mergedTx, i, coin.out.nValue, 1), coin.out.scriptPubKey, sigdata);
UpdateInput(txin, sigdata); UpdateInput(txin, sigdata);
} }
@ -880,7 +878,6 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
if (!fHashSingle || (i < mtx.vout.size())) { if (!fHashSingle || (i < mtx.vout.size())) {
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata); ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
} }
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i, coin.out));
UpdateInput(txin, sigdata); UpdateInput(txin, sigdata);

View file

@ -33,6 +33,53 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
return true; return true;
} }
static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
{
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 GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
{
if (provider.GetPubKey(address, pubkey)) {
return true;
}
// 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 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;
}
if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
CPubKey pubkey;
GetPubKey(provider, sigdata, keyid, pubkey);
auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
assert(i.second);
return true;
}
return false;
}
/** /**
* Sign scriptPubKey using signature made with creator. * Sign scriptPubKey using signature made with creator.
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
@ -40,7 +87,7 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
* Returns false if scriptPubKey could not be completely satisfied. * Returns false if scriptPubKey could not be completely satisfied.
*/ */
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion) std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{ {
CScript scriptRet; CScript scriptRet;
uint160 h160; uint160 h160;
@ -58,20 +105,20 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TX_WITNESS_UNKNOWN: case TX_WITNESS_UNKNOWN:
return false; return false;
case TX_PUBKEY: case TX_PUBKEY:
if (!creator.CreateSig(provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false; if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig)); ret.push_back(std::move(sig));
return true; return true;
case TX_PUBKEYHASH: { case TX_PUBKEYHASH: {
CKeyID keyID = CKeyID(uint160(vSolutions[0])); CKeyID keyID = CKeyID(uint160(vSolutions[0]));
if (!creator.CreateSig(provider, sig, keyID, scriptPubKey, sigversion)) return false; if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig)); ret.push_back(std::move(sig));
CPubKey pubkey; CPubKey pubkey;
provider.GetPubKey(keyID, pubkey); GetPubKey(provider, sigdata, keyID, pubkey);
ret.push_back(ToByteVector(pubkey)); ret.push_back(ToByteVector(pubkey));
return true; return true;
} }
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
if (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())); ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true; return true;
} }
@ -82,7 +129,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
ret.push_back(valtype()); // workaround CHECKMULTISIG bug ret.push_back(valtype()); // workaround CHECKMULTISIG bug
for (size_t i = 1; i < vSolutions.size() - 1; ++i) { for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
CPubKey pubkey = CPubKey(vSolutions[i]); CPubKey pubkey = CPubKey(vSolutions[i]);
if (ret.size() < required + 1 && creator.CreateSig(provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) { if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) {
ret.push_back(std::move(sig)); ret.push_back(std::move(sig));
} }
} }
@ -98,7 +145,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TX_WITNESS_V0_SCRIPTHASH: case TX_WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
if (provider.GetCScript(h160, scriptRet)) { if (GetCScript(provider, sigdata, h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true; return true;
} }
@ -130,7 +177,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
std::vector<valtype> result; std::vector<valtype> result;
txnouttype whichType; txnouttype whichType;
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE); bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false; bool P2SH = false;
CScript subscript; CScript subscript;
sigdata.scriptWitness.stack.clear(); sigdata.scriptWitness.stack.clear();
@ -141,7 +188,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
// the final scriptSig is the signatures from that // the final scriptSig is the signatures from that
// and then the serialized subscript: // and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end()); subscript = CScript(result[0].begin(), result[0].end());
solved = solved && SignStep(provider, 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; P2SH = true;
} }
@ -150,15 +198,16 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
CScript witnessscript; CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
txnouttype subType; txnouttype subType;
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0); solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result; sigdata.scriptWitness.stack = result;
result.clear(); result.clear();
} }
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH) else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
{ {
CScript witnessscript(result[0].begin(), result[0].end()); CScript witnessscript(result[0].begin(), result[0].end());
sigdata.witness_script = witnessscript;
txnouttype subType; txnouttype subType;
solved = solved && SignStep(provider, 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())); result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result; sigdata.scriptWitness.stack = result;
result.clear(); result.clear();
@ -290,6 +339,22 @@ void UpdateInput(CTxIn& input, const SignatureData& data)
input.scriptWitness = data.scriptWitness; input.scriptWitness = data.scriptWitness;
} }
void SignatureData::MergeSignatureData(SignatureData sigdata)
{
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) bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
{ {
assert(nIn < txTo.vin.size()); assert(nIn < txTo.vin.size());
@ -485,6 +550,7 @@ public:
} }
const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(); const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator();
const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
bool IsSolvable(const SigningProvider& provider, const CScript& script) bool IsSolvable(const SigningProvider& provider, const CScript& script)
{ {

View file

@ -21,11 +21,13 @@ class SigningProvider
{ {
public: public:
virtual ~SigningProvider() {} virtual ~SigningProvider() {}
virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const =0; virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const =0; virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
virtual bool GetKey(const CKeyID &address, CKey& key) const =0; virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
}; };
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
/** Interface for signature creators. */ /** Interface for signature creators. */
class BaseSignatureCreator { class BaseSignatureCreator {
public: public:
@ -68,6 +70,7 @@ struct SignatureData {
SignatureData() {} SignatureData() {}
explicit SignatureData(const CScript& script) : scriptSig(script) {} explicit SignatureData(const CScript& script) : scriptSig(script) {}
void MergeSignatureData(SignatureData sigdata);
}; };
/** Produce a script signature using a generic signature creator. */ /** Produce a script signature using a generic signature creator. */