From 18051c7fbd224e32d9a5fea96f1083210cea3a14 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 4 Nov 2014 10:06:20 -0800 Subject: [PATCH] Abstract out Ctransaction-specific signing into TransactionSignatureCreator --- src/script/sign.cpp | 89 +++++++++++++++++++++++++++------------------ src/script/sign.h | 41 +++++++++++++++++++-- 2 files changed, 90 insertions(+), 40 deletions(-) diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 14119f7e2c..eab629cd91 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -17,22 +17,31 @@ using namespace std; typedef vector valtype; -bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn) {} + +bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode) const { CKey key; - if (!keystore.GetKey(address, key)) + if (!keystore->GetKey(address, key)) return false; - vector vchSig; + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType); if (!key.Sign(hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); - scriptSigRet << vchSig; + return true; +} +static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) +{ + vector vchSig; + if (!creator.CreateSig(vchSig, address, scriptCode)) + return false; + scriptSigRet << vchSig; return true; } -bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -40,20 +49,20 @@ bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint2 { const valtype& pubkey = multisigdata[i]; CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + if (Sign1(keyID, creator, scriptCode, scriptSigRet)) ++nSigned; } return nSigned==nRequired; } /** - * Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. + * Sign scriptPubKey using signature made with creator. * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. * Returns false if scriptPubKey could not be completely satisfied. */ -bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, - CScript& scriptSigRet, txnouttype& whichTypeRet) +static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, + CScript& scriptSigRet, txnouttype& whichTypeRet) { scriptSigRet.clear(); @@ -69,39 +78,32 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); + return Sign1(keyID, creator, scriptPubKey, scriptSigRet); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) return false; else { CPubKey vch; - keystore.GetPubKey(keyID, vch); + creator.KeyStore().GetPubKey(keyID, vch); scriptSigRet << ToByteVector(vch); } return true; case TX_SCRIPTHASH: - return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); + return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); case TX_MULTISIG: scriptSigRet << OP_0; // workaround CHECKMULTISIG bug - return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); + return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); } return false; } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, CScript& scriptSig) { - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - - // Leave out the signature from the hash, since a signature can't sign itself. - // The checksig op will also drop the signatures from its hash. - uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); - txnouttype whichType; - if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) + if (!SignStep(creator, fromPubKey, scriptSig, whichType)) return false; if (whichType == TX_SCRIPTHASH) @@ -109,21 +111,29 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutabl // Solver returns the subscript that need to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - CScript subscript = txin.scriptSig; - - // Recompute txn hash using subscript in place of scriptPubKey: - uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + CScript subscript = scriptSig; txnouttype subType; bool fSolved = - Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; + SignStep(creator, subscript, scriptSig, subType) && subType != TX_SCRIPTHASH; // Append serialized subscript whether or not it is completely signed: - txin.scriptSig << static_cast(subscript); + scriptSig << static_cast(subscript); if (!fSolved) return false; } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&txTo, nIn)); + return VerifyScript(scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); +} + +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + + CTransaction txToConst(txTo); + TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType); + + return ProduceSignature(creator, fromPubKey, txin.scriptSig); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) @@ -144,7 +154,7 @@ static CScript PushAll(const vector& values) return result; } -static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, +static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const vector& vSolutions, const vector& sigs1, const vector& sigs2) { @@ -174,7 +184,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - if (TransactionSignatureChecker(&txTo, nIn).CheckSig(sig, pubkey, scriptPubKey)) + if (checker.CheckSig(sig, pubkey, scriptPubKey)) { sigs[pubkey] = sig; break; @@ -199,7 +209,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& return result; } -static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, +static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const txnouttype txType, const vector& vSolutions, vector& sigs1, vector& sigs2) { @@ -233,12 +243,12 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction Solver(pubKey2, txType2, vSolutions2); sigs1.pop_back(); sigs2.pop_back(); - CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); + CScript result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); result << spk; return result; } case TX_MULTISIG: - return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); + return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); } return CScript(); @@ -246,6 +256,13 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2) +{ + TransactionSignatureChecker checker(&txTo, nIn); + return CombineSignatures(scriptPubKey, checker, scriptSig1, scriptSig2); +} + +CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, + const CScript& scriptSig1, const CScript& scriptSig2) { txnouttype txType; vector > vSolutions; @@ -256,5 +273,5 @@ CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, vector stack2; EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); - return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); + return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2); } diff --git a/src/script/sign.h b/src/script/sign.h index e197d5fab3..0c4cf61e5e 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -8,19 +8,52 @@ #include "script/interpreter.h" +class CKeyID; class CKeyStore; class CScript; class CTransaction; struct CMutableTransaction; +/** Virtual base class for signature creators. */ +class BaseSignatureCreator { +protected: + const CKeyStore* keystore; + +public: + BaseSignatureCreator(const CKeyStore* keystoreIn) : keystore(keystoreIn) {} + const CKeyStore& KeyStore() const { return *keystore; }; + virtual ~BaseSignatureCreator() {} + virtual const BaseSignatureChecker& Checker() const =0; + + /** Create a singular (non-script) signature. */ + virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const =0; +}; + +/** A signature creator for transactions. */ +class TransactionSignatureCreator : public BaseSignatureCreator { + const CTransaction* txTo; + unsigned int nIn; + int nHashType; + const TransactionSignatureChecker checker; + +public: + TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL); + const BaseSignatureChecker& Checker() const { return checker; } + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; +}; + +/** Produce a script signature using a generic signature creator. */ +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, CScript& scriptSig); + +/** Produce a script signature for a transaction. */ bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -/** - * Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, - * combine them intelligently and return the result. - */ +/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ +CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const CScript& scriptSig1, const CScript& scriptSig2); + +/** Combine two script signatures on transactions. */ CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); #endif // BITCOIN_SCRIPT_SIGN_H