Merge pull request #5208

18051c7 Abstract out Ctransaction-specific signing into TransactionSignatureCreator (Pieter Wuille)
This commit is contained in:
Pieter Wuille 2015-03-24 05:54:36 -07:00
commit 2afd919f21
No known key found for this signature in database
GPG Key ID: 57896D2FF8F0B657
2 changed files with 91 additions and 41 deletions

View File

@ -17,22 +17,31 @@ using namespace std;
typedef vector<unsigned char> valtype; typedef vector<unsigned char> 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<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode) const
{ {
CKey key; CKey key;
if (!keystore.GetKey(address, key)) if (!keystore->GetKey(address, key))
return false; return false;
vector<unsigned char> vchSig; uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType);
if (!key.Sign(hash, vchSig)) if (!key.Sign(hash, vchSig))
return false; return false;
vchSig.push_back((unsigned char)nHashType); vchSig.push_back((unsigned char)nHashType);
scriptSigRet << vchSig;
return true; return true;
} }
bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet)
{
vector<unsigned char> vchSig;
if (!creator.CreateSig(vchSig, address, scriptCode))
return false;
scriptSigRet << vchSig;
return true;
}
static bool SignN(const vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet)
{ {
int nSigned = 0; int nSigned = 0;
int nRequired = multisigdata.front()[0]; int nRequired = multisigdata.front()[0];
@ -40,19 +49,19 @@ bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint2
{ {
const valtype& pubkey = multisigdata[i]; const valtype& pubkey = multisigdata[i];
CKeyID keyID = CPubKey(pubkey).GetID(); CKeyID keyID = CPubKey(pubkey).GetID();
if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) if (Sign1(keyID, creator, scriptCode, scriptSigRet))
++nSigned; ++nSigned;
} }
return nSigned==nRequired; 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), * 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. * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
* Returns false if scriptPubKey could not be completely satisfied. * Returns false if scriptPubKey could not be completely satisfied.
*/ */
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey,
CScript& scriptSigRet, txnouttype& whichTypeRet) CScript& scriptSigRet, txnouttype& whichTypeRet)
{ {
scriptSigRet.clear(); scriptSigRet.clear();
@ -69,39 +78,32 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
return false; return false;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); return Sign1(keyID, creator, scriptPubKey, scriptSigRet);
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0])); keyID = CKeyID(uint160(vSolutions[0]));
if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet))
return false; return false;
else else
{ {
CPubKey vch; CPubKey vch;
keystore.GetPubKey(keyID, vch); creator.KeyStore().GetPubKey(keyID, vch);
scriptSigRet << ToByteVector(vch); scriptSigRet << ToByteVector(vch);
} }
return true; return true;
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet);
case TX_MULTISIG: case TX_MULTISIG:
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet));
} }
return false; 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; txnouttype whichType;
if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) if (!SignStep(creator, fromPubKey, scriptSig, whichType))
return false; return false;
if (whichType == TX_SCRIPTHASH) 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; // Solver returns the subscript that need to be evaluated;
// 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:
CScript subscript = txin.scriptSig; CScript subscript = scriptSig;
// Recompute txn hash using subscript in place of scriptPubKey:
uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType);
txnouttype subType; txnouttype subType;
bool fSolved = 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: // Append serialized subscript whether or not it is completely signed:
txin.scriptSig << static_cast<valtype>(subscript); scriptSig << static_cast<valtype>(subscript);
if (!fSolved) return false; if (!fSolved) return false;
} }
// Test solution // 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) bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
@ -144,7 +154,7 @@ static CScript PushAll(const vector<valtype>& values)
return result; 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<valtype>& vSolutions, const vector<valtype>& vSolutions,
const vector<valtype>& sigs1, const vector<valtype>& sigs2) const vector<valtype>& sigs1, const vector<valtype>& sigs2)
{ {
@ -174,7 +184,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction&
if (sigs.count(pubkey)) if (sigs.count(pubkey))
continue; // Already got a sig for this 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; sigs[pubkey] = sig;
break; break;
@ -199,7 +209,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction&
return result; 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<valtype>& vSolutions, const txnouttype txType, const vector<valtype>& vSolutions,
vector<valtype>& sigs1, vector<valtype>& sigs2) vector<valtype>& sigs1, vector<valtype>& sigs2)
{ {
@ -233,12 +243,12 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction
Solver(pubKey2, txType2, vSolutions2); Solver(pubKey2, txType2, vSolutions2);
sigs1.pop_back(); sigs1.pop_back();
sigs2.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; result << spk;
return result; return result;
} }
case TX_MULTISIG: case TX_MULTISIG:
return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2);
} }
return CScript(); 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, CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
const CScript& scriptSig1, const CScript& scriptSig2) 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; txnouttype txType;
vector<vector<unsigned char> > vSolutions; vector<vector<unsigned char> > vSolutions;
@ -256,5 +273,5 @@ CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo,
vector<valtype> stack2; vector<valtype> stack2;
EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker());
return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2);
} }

View File

@ -8,19 +8,52 @@
#include "script/interpreter.h" #include "script/interpreter.h"
class CKeyID;
class CKeyStore; class CKeyStore;
class CScript; class CScript;
class CTransaction; class CTransaction;
struct CMutableTransaction; 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<unsigned char>& 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<unsigned char>& 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 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); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
/** /** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */
* Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const CScript& scriptSig1, const CScript& scriptSig2);
* combine them intelligently and return the result.
*/ /** Combine two script signatures on transactions. */
CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2);
#endif // BITCOIN_SCRIPT_SIGN_H #endif // BITCOIN_SCRIPT_SIGN_H