Browse Source

BIP143: Signing logic

0.13
Pieter Wuille 9 years ago
parent
commit
605e8473a7
  1. 28
      src/bitcoin-tx.cpp
  2. 17
      src/rpc/rawtransaction.cpp
  3. 19
      src/script/ismine.cpp
  4. 275
      src/script/sign.cpp
  5. 34
      src/script/sign.h
  6. 41
      src/script/standard.cpp
  7. 3
      src/script/standard.h
  8. 4
      src/test/DoS_tests.cpp
  9. 2
      src/test/multisig_tests.cpp
  10. 11
      src/test/script_P2SH_tests.cpp
  11. 100
      src/test/script_tests.cpp
  12. 10
      src/wallet/wallet.cpp

28
src/bitcoin-tx.cpp

@ -363,6 +363,18 @@ vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
return ParseHexUV(o[strKey], strKey); return ParseHexUV(o[strKey], strKey);
} }
static CAmount AmountFromValue(const UniValue& value)
{
if (!value.isNum() && !value.isStr())
throw runtime_error("Amount is not a number or string");
CAmount amount;
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
throw runtime_error("Invalid amount");
if (!MoneyRange(amount))
throw runtime_error("Amount out of range");
return amount;
}
static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
{ {
int nHashType = SIGHASH_ALL; int nHashType = SIGHASH_ALL;
@ -434,7 +446,10 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
if ((unsigned int)nOut >= coins->vout.size()) if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1); coins->vout.resize(nOut+1);
coins->vout[nOut].scriptPubKey = scriptPubKey; coins->vout[nOut].scriptPubKey = scriptPubKey;
coins->vout[nOut].nValue = 0; // we don't know the actual output value coins->vout[nOut].nValue = 0;
if (prevOut.exists("amount")) {
coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]);
}
} }
// if redeemScript given and private keys given, // if redeemScript given and private keys given,
@ -464,15 +479,16 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
const CAmount& amount = coins->vout[txin.prevout.n].nValue; const CAmount& amount = coins->vout[txin.prevout.n].nValue;
txin.scriptSig.clear(); SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output: // Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size())) if (!fHashSingle || (i < mergedTx.vout.size()))
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
// ... and merge in other signatures: // ... and merge in other signatures:
BOOST_FOREACH(const CTransaction& txv, txVariants) { BOOST_FOREACH(const CTransaction& txv, txVariants)
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, amount, txin.scriptSig, txv.vin[i].scriptSig); sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
} UpdateTransaction(mergedTx, i, sigdata);
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount)))
fComplete = false; fComplete = false;
} }

17
src/rpc/rawtransaction.cpp

@ -592,7 +592,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
" \"txid\":\"id\", (string, required) The transaction id\n" " \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n" " \"vout\":n, (numeric, required) The output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script key\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n"
" \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" " \"redeemScript\": \"hex\", (string, required for P2SH) redeem script\n"
" \"amount\": value (numeric, required) The amount spent\n"
" }\n" " }\n"
" ,...\n" " ,...\n"
" ]\n" " ]\n"
@ -735,7 +736,10 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
if ((unsigned int)nOut >= coins->vout.size()) if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1); coins->vout.resize(nOut+1);
coins->vout[nOut].scriptPubKey = scriptPubKey; coins->vout[nOut].scriptPubKey = scriptPubKey;
coins->vout[nOut].nValue = 0; // we don't know the actual output value coins->vout[nOut].nValue = 0;
if (prevOut.exists("amount")) {
coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount"));
}
} }
// if redeemScript given and not using the local wallet (private keys // if redeemScript given and not using the local wallet (private keys
@ -801,15 +805,18 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
const CAmount& amount = coins->vout[txin.prevout.n].nValue; const CAmount& amount = coins->vout[txin.prevout.n].nValue;
txin.scriptSig.clear(); SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output: // Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size())) if (!fHashSingle || (i < mergedTx.vout.size()))
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
// ... and merge in other signatures: // ... and merge in other signatures:
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
txin.scriptSig = CombineSignatures(prevPubKey, txConst, i, amount, txin.scriptSig, txv.vin[i].scriptSig); sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
} }
UpdateTransaction(mergedTx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK; ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));

19
src/script/ismine.cpp

@ -57,6 +57,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
return ISMINE_SPENDABLE; return ISMINE_SPENDABLE;
break; break;
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
case TX_WITNESS_V0_KEYHASH:
keyID = CKeyID(uint160(vSolutions[0])); keyID = CKeyID(uint160(vSolutions[0]));
if (keystore.HaveKey(keyID)) if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE; return ISMINE_SPENDABLE;
@ -72,6 +73,20 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
} }
break; break;
} }
case TX_WITNESS_V0_SCRIPTHASH:
{
uint160 hash;
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
CScriptID scriptID = CScriptID(hash);
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
isminetype ret = IsMine(keystore, subscript);
if (ret == ISMINE_SPENDABLE)
return ret;
}
break;
}
case TX_MULTISIG: case TX_MULTISIG:
{ {
// Only consider transactions "mine" if we own ALL the // Only consider transactions "mine" if we own ALL the
@ -88,8 +103,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
if (keystore.HaveWatchOnly(scriptPubKey)) { if (keystore.HaveWatchOnly(scriptPubKey)) {
// TODO: This could be optimized some by doing some work after the above solver // TODO: This could be optimized some by doing some work after the above solver
CScript scriptSig; SignatureData sigs;
return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, scriptSig) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE;
} }
return ISMINE_NO; return ISMINE_NO;
} }

275
src/script/sign.cpp

@ -20,29 +20,29 @@ typedef std::vector<unsigned char> valtype;
TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), 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) const bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{ {
CKey key; CKey key;
if (!keystore->GetKey(address, key)) if (!keystore->GetKey(address, key))
return false; return false;
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, SIGVERSION_BASE); uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
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);
return true; return true;
} }
static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
{ {
vector<unsigned char> vchSig; vector<unsigned char> vchSig;
if (!creator.CreateSig(vchSig, address, scriptCode)) if (!creator.CreateSig(vchSig, address, scriptCode, sigversion))
return false; return false;
scriptSigRet << vchSig; ret.push_back(vchSig);
return true; return true;
} }
static bool SignN(const vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) static bool SignN(const vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
{ {
int nSigned = 0; int nSigned = 0;
int nRequired = multisigdata.front()[0]; int nRequired = multisigdata.front()[0];
@ -50,7 +50,7 @@ static bool SignN(const vector<valtype>& multisigdata, const BaseSignatureCreato
{ {
const valtype& pubkey = multisigdata[i]; const valtype& pubkey = multisigdata[i];
CKeyID keyID = CPubKey(pubkey).GetID(); CKeyID keyID = CPubKey(pubkey).GetID();
if (Sign1(keyID, creator, scriptCode, scriptSigRet)) if (Sign1(keyID, creator, scriptCode, ret, sigversion))
++nSigned; ++nSigned;
} }
return nSigned==nRequired; return nSigned==nRequired;
@ -63,9 +63,11 @@ static bool SignN(const vector<valtype>& multisigdata, const BaseSignatureCreato
* Returns false if scriptPubKey could not be completely satisfied. * Returns false if scriptPubKey could not be completely satisfied.
*/ */
static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey,
CScript& scriptSigRet, txnouttype& whichTypeRet) std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion)
{ {
scriptSigRet.clear(); CScript scriptRet;
uint160 h160;
ret.clear();
vector<valtype> vSolutions; vector<valtype> vSolutions;
if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
@ -79,62 +81,142 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
return false; return false;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, creator, scriptPubKey, scriptSigRet); return Sign1(keyID, creator, scriptPubKey, ret, sigversion);
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0])); keyID = CKeyID(uint160(vSolutions[0]));
if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion))
return false; return false;
else else
{ {
CPubKey vch; CPubKey vch;
creator.KeyStore().GetPubKey(keyID, vch); creator.KeyStore().GetPubKey(keyID, vch);
scriptSigRet << ToByteVector(vch); ret.push_back(ToByteVector(vch));
} }
return true; return true;
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); if (creator.KeyStore().GetCScript(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:
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug ret.push_back(valtype()); // workaround CHECKMULTISIG bug
return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion));
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.KeyStore().GetCScript(h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
} }
return false; return false;
default:
return false;
}
} }
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, CScript& scriptSig) static CScript PushAll(const vector<valtype>& values)
{ {
CScript result;
BOOST_FOREACH(const valtype& v, values) {
if (v.size() == 0) {
result << OP_0;
} else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) {
result << CScript::EncodeOP_N(v[0]);
} else {
result << v;
}
}
return result;
}
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
{
CScript script = fromPubKey;
bool solved = true;
std::vector<valtype> result;
txnouttype whichType; txnouttype whichType;
if (!SignStep(creator, fromPubKey, scriptSig, whichType)) solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE);
return false; bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
if (whichType == TX_SCRIPTHASH) if (solved && whichType == TX_SCRIPTHASH)
{ {
// Solver returns the subscript that need to be evaluated; // Solver returns the subscript that needs 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 = scriptSig; script = subscript = CScript(result[0].begin(), result[0].end());
solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH;
P2SH = true;
}
if (solved && whichType == TX_WITNESS_V0_KEYHASH)
{
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);
sigdata.scriptWitness.stack = result;
result.clear();
}
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
{
CScript witnessscript(result[0].begin(), result[0].end());
txnouttype subType; txnouttype subType;
bool fSolved = solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
SignStep(creator, subscript, scriptSig, subType) && subType != TX_SCRIPTHASH; result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
// Append serialized subscript whether or not it is completely signed: sigdata.scriptWitness.stack = result;
scriptSig << valtype(subscript.begin(), subscript.end()); result.clear();
if (!fSolved) return false; }
if (P2SH) {
result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end()));
} }
sigdata.scriptSig = PushAll(result);
// Test solution // Test solution
return VerifyScript(scriptSig, fromPubKey, NULL, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
}
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
{
SignatureData data;
assert(tx.vin.size() > nIn);
data.scriptSig = tx.vin[nIn].scriptSig;
if (tx.wit.vtxinwit.size() > nIn) {
data.scriptWitness = tx.wit.vtxinwit[nIn].scriptWitness;
}
return data;
}
void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data)
{
assert(tx.vin.size() > nIn);
tx.vin[nIn].scriptSig = data.scriptSig;
if (!data.scriptWitness.IsNull() || tx.wit.vtxinwit.size() > nIn) {
tx.wit.vtxinwit.resize(tx.vin.size());
tx.wit.vtxinwit[nIn].scriptWitness = data.scriptWitness;
}
} }
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
{ {
assert(nIn < txTo.vin.size()); assert(nIn < txTo.vin.size());
CTxIn& txin = txTo.vin[nIn];
CTransaction txToConst(txTo); CTransaction txToConst(txTo);
TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType); TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType);
return ProduceSignature(creator, fromPubKey, txin.scriptSig); SignatureData sigdata;
bool ret = ProduceSignature(creator, fromPubKey, sigdata);
UpdateTransaction(txTo, nIn, sigdata);
return ret;
} }
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,20 +226,12 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutab
assert(txin.prevout.n < txFrom.vout.size()); assert(txin.prevout.n < txFrom.vout.size());
const CTxOut& txout = txFrom.vout[txin.prevout.n]; const CTxOut& txout = txFrom.vout[txin.prevout.n];
return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
} }
static CScript PushAll(const vector<valtype>& values) static vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
{
CScript result;
BOOST_FOREACH(const valtype& v, values)
result << v;
return result;
}
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, SigVersion sigversion)
{ {
// Combine all the signatures we've got: // Combine all the signatures we've got:
set<valtype> allsigs; set<valtype> allsigs;
@ -185,7 +259,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC
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 (checker.CheckSig(sig, pubkey, scriptPubKey, SIGVERSION_BASE)) if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion))
{ {
sigs[pubkey] = sig; sigs[pubkey] = sig;
break; break;
@ -194,87 +268,126 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC
} }
// Now build a merged CScript: // Now build a merged CScript:
unsigned int nSigsHave = 0; unsigned int nSigsHave = 0;
CScript result; result << OP_0; // pop-one-too-many workaround std::vector<valtype> result; result.push_back(valtype()); // pop-one-too-many workaround
for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++)
{ {
if (sigs.count(vSolutions[i+1])) if (sigs.count(vSolutions[i+1]))
{ {
result << sigs[vSolutions[i+1]]; result.push_back(sigs[vSolutions[i+1]]);
++nSigsHave; ++nSigsHave;
} }
} }
// Fill any missing with OP_0: // Fill any missing with OP_0:
for (unsigned int i = nSigsHave; i < nSigsRequired; i++) for (unsigned int i = nSigsHave; i < nSigsRequired; i++)
result << OP_0; result.push_back(valtype());
return result; return result;
} }
static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, namespace
{
struct Stacks
{
std::vector<valtype> script;
std::vector<valtype> witness;
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);
}
SignatureData Output() const {
SignatureData result;
result.scriptSig = PushAll(script);
result.scriptWitness.stack = witness;
return result;
}
};
}
static Stacks 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) Stacks sigs1, Stacks sigs2, SigVersion sigversion)
{ {
switch (txType) switch (txType)
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA: case TX_NULL_DATA:
// Don't know anything about this, assume bigger one is correct: // Don't know anything about this, assume bigger one is correct:
if (sigs1.size() >= sigs2.size()) if (sigs1.script.size() >= sigs2.script.size())
return PushAll(sigs1); return sigs1;
return PushAll(sigs2); return sigs2;
case TX_PUBKEY: case TX_PUBKEY:
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
// Signatures are bigger than placeholders or empty scripts: // Signatures are bigger than placeholders or empty scripts:
if (sigs1.empty() || sigs1[0].empty()) if (sigs1.script.empty() || sigs1.script[0].empty())
return PushAll(sigs2); return sigs2;
return PushAll(sigs1); 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: case TX_SCRIPTHASH:
if (sigs1.empty() || sigs1.back().empty()) if (sigs1.script.empty() || sigs1.script.back().empty())
return PushAll(sigs2); return sigs2;
else if (sigs2.empty() || sigs2.back().empty()) else if (sigs2.script.empty() || sigs2.script.back().empty())
return PushAll(sigs1); return sigs1;
else else
{ {
// Recur to combine: // Recur to combine:
valtype spk = sigs1.back(); valtype spk = sigs1.script.back();
CScript pubKey2(spk.begin(), spk.end()); CScript pubKey2(spk.begin(), spk.end());
txnouttype txType2; txnouttype txType2;
vector<vector<unsigned char> > vSolutions2; vector<vector<unsigned char> > vSolutions2;
Solver(pubKey2, txType2, vSolutions2); Solver(pubKey2, txType2, vSolutions2);
sigs1.pop_back(); sigs1.script.pop_back();
sigs2.pop_back(); sigs2.script.pop_back();
CScript result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion);
result << spk; result.script.push_back(spk);
return result; return result;
} }
case TX_MULTISIG: case TX_MULTISIG:
return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); 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;
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;
} }
default:
return CScript(); return Stacks();
} }
CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CAmount& amount,
const CScript& scriptSig1, const CScript& scriptSig2)
{
TransactionSignatureChecker checker(&txTo, nIn, amount);
return CombineSignatures(scriptPubKey, checker, scriptSig1, scriptSig2);
} }
CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
const CScript& scriptSig1, const CScript& scriptSig2) const SignatureData& scriptSig1, const SignatureData& scriptSig2)
{ {
txnouttype txType; txnouttype txType;
vector<vector<unsigned char> > vSolutions; vector<vector<unsigned char> > vSolutions;
Solver(scriptPubKey, txType, vSolutions); Solver(scriptPubKey, txType, vSolutions);
vector<valtype> stack1; return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SIGVERSION_BASE).Output();
EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
vector<valtype> stack2;
EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2);
} }
namespace { namespace {
@ -297,7 +410,7 @@ const BaseSignatureChecker& DummySignatureCreator::Checker() const
return dummyChecker; return dummyChecker;
} }
bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const
{ {
// Create a dummy signature that is a valid DER-encoding // Create a dummy signature that is a valid DER-encoding
vchSig.assign(72, '\000'); vchSig.assign(72, '\000');

34
src/script/sign.h

@ -27,7 +27,7 @@ public:
virtual const BaseSignatureChecker& Checker() const =0; virtual const BaseSignatureChecker& Checker() const =0;
/** Create a singular (non-script) signature. */ /** Create a singular (non-script) signature. */
virtual bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const =0; virtual bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
}; };
/** A signature creator for transactions. */ /** A signature creator for transactions. */
@ -41,7 +41,14 @@ class TransactionSignatureCreator : public BaseSignatureCreator {
public: public:
TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL);
const BaseSignatureChecker& Checker() const { return checker; } const BaseSignatureChecker& Checker() const { return checker; }
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const;
};
class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
CTransaction tx;
public:
MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {}
}; };
/** A signature creator that just produces 72-byte empty signatyres. */ /** A signature creator that just produces 72-byte empty signatyres. */
@ -49,20 +56,29 @@ class DummySignatureCreator : public BaseSignatureCreator {
public: public:
DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {}
const BaseSignatureChecker& Checker() const; const BaseSignatureChecker& Checker() const;
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const;
};
struct SignatureData {
CScript scriptSig;
CScriptWitness scriptWitness;
SignatureData() {}
explicit SignatureData(const CScript& script) : scriptSig(script) {}
}; };
/** Produce a script signature using a generic signature creator. */ /** Produce a script signature using a generic signature creator. */
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, CScript& scriptSig); bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
/** Produce a script signature for a transaction. */ /** 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, const CAmount& amount, int nHashType);
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);
/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ /** 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); SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2);
/** Combine two script signatures on transactions. */ /** Extract signature data from a transaction, and insert it. */
CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CAmount& amount, const CScript& scriptSig1, const CScript& scriptSig2); SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data);
#endif // BITCOIN_SCRIPT_SIGN_H #endif // BITCOIN_SCRIPT_SIGN_H

41
src/script/standard.cpp

@ -31,6 +31,8 @@ const char* GetTxnOutputType(txnouttype t)
case TX_SCRIPTHASH: return "scripthash"; case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig"; case TX_MULTISIG: return "multisig";
case TX_NULL_DATA: return "nulldata"; case TX_NULL_DATA: return "nulldata";
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
} }
return NULL; return NULL;
} }
@ -66,6 +68,22 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
return true; return true;
} }
int witnessversion;
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
if (witnessversion == 0 && witnessprogram.size() == 20) {
typeRet = TX_WITNESS_V0_KEYHASH;
vSolutionsRet.push_back(witnessprogram);
return true;
}
if (witnessversion == 0 && witnessprogram.size() == 32) {
typeRet = TX_WITNESS_V0_SCRIPTHASH;
vSolutionsRet.push_back(witnessprogram);
return true;
}
return false;
}
// Provably prunable, data-carrying output // Provably prunable, data-carrying output
// //
// So long as script passes the IsUnspendable() test and all but the first // So long as script passes the IsUnspendable() test and all but the first
@ -282,3 +300,26 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
return script; return script;
} }
CScript GetScriptForWitness(const CScript& redeemscript)
{
CScript ret;
txnouttype typ;
std::vector<std::vector<unsigned char> > vSolutions;
if (Solver(redeemscript, typ, vSolutions)) {
if (typ == TX_PUBKEY) {
unsigned char h160[20];
CHash160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160);
ret << OP_0 << std::vector<unsigned char>(&h160[0], &h160[20]);
return ret;
} else if (typ == TX_PUBKEYHASH) {
ret << OP_0 << vSolutions[0];
return ret;
}
}
uint256 hash;
CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin());
ret << OP_0 << ToByteVector(hash);
return ret;
}

3
src/script/standard.h

@ -51,6 +51,8 @@ enum txnouttype
TX_SCRIPTHASH, TX_SCRIPTHASH,
TX_MULTISIG, TX_MULTISIG,
TX_NULL_DATA, TX_NULL_DATA,
TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH,
}; };
class CNoDestination { class CNoDestination {
@ -77,5 +79,6 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
CScript GetScriptForDestination(const CTxDestination& dest); CScript GetScriptForDestination(const CTxDestination& dest);
CScript GetScriptForRawPubKey(const CPubKey& pubkey); CScript GetScriptForRawPubKey(const CPubKey& pubkey);
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys); CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
CScript GetScriptForWitness(const CScript& redeemscript);
#endif // BITCOIN_SCRIPT_STANDARD_H #endif // BITCOIN_SCRIPT_STANDARD_H

4
src/test/DoS_tests.cpp

@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout.resize(1); tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT; tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
SignSignature(keystore, txPrev, tx, 0); SignSignature(keystore, txPrev, tx, 0, SIGHASH_ALL);
AddOrphanTx(tx, i); AddOrphanTx(tx, i);
} }
@ -168,7 +168,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vin[j].prevout.n = j; tx.vin[j].prevout.n = j;
tx.vin[j].prevout.hash = txPrev.GetHash(); tx.vin[j].prevout.hash = txPrev.GetHash();
} }
SignSignature(keystore, txPrev, tx, 0); SignSignature(keystore, txPrev, tx, 0, SIGHASH_ALL);
// Re-use same signature for other inputs // Re-use same signature for other inputs
// (they don't have to be valid for this test) // (they don't have to be valid for this test)
for (unsigned int j = 1; j < tx.vin.size(); j++) for (unsigned int j = 1; j < tx.vin.size(); j++)

2
src/test/multisig_tests.cpp

@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(multisig_Sign)
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
} }
} }

11
src/test/script_P2SH_tests.cpp

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "core_io.h"
#include "key.h" #include "key.h"
#include "keystore.h" #include "keystore.h"
#include "main.h" #include "main.h"
@ -102,7 +103,7 @@ BOOST_AUTO_TEST_CASE(sign)
} }
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
} }
// All of the above should be OK, and the txTos have valid signatures // All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig: // Check to make sure signature verification fails if we use the wrong ScriptSig:
@ -197,7 +198,7 @@ BOOST_AUTO_TEST_CASE(set)
} }
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL), strprintf("SignSignature %d", i));
BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i)); BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i));
} }
} }
@ -326,9 +327,9 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txTo.vin[i].prevout.n = i; txTo.vin[i].prevout.n = i;
txTo.vin[i].prevout.hash = txFrom.GetHash(); txTo.vin[i].prevout.hash = txFrom.GetHash();
} }
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0)); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL));
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1)); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1, SIGHASH_ALL));
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2, SIGHASH_ALL));
// SignSignature doesn't know how to sign these. We're // SignSignature doesn't know how to sign these. We're
// not testing validating signatures, so just create // not testing validating signatures, so just create
// dummy signatures that DO include the correct P2SH scripts: // dummy signatures that DO include the correct P2SH scripts:

100
src/test/script_tests.cpp

@ -152,16 +152,16 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, co
flags |= SCRIPT_VERIFY_WITNESS; flags |= SCRIPT_VERIFY_WITNESS;
} }
ScriptError err; ScriptError err;
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)); CMutableTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit);
CMutableTransaction tx2 = tx; CMutableTransaction tx2 = tx;
static const CAmount amountZero = 0; BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, NULL, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message);
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, NULL, flags, MutableTransactionSignatureChecker(&tx, 0, amountZero), &err) == expect, message);
BOOST_CHECK_MESSAGE(err == scriptError, std::string(FormatScriptError(err)) + " where " + std::string(FormatScriptError((ScriptError_t)scriptError)) + " expected: " + message); BOOST_CHECK_MESSAGE(err == scriptError, std::string(FormatScriptError(err)) + " where " + std::string(FormatScriptError((ScriptError_t)scriptError)) + " expected: " + message);
#if defined(HAVE_CONSENSUS_LIB) #if defined(HAVE_CONSENSUS_LIB)
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << tx2; stream << tx2;
if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) { if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), amountZero, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect, message); BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect, message);
} else { } else {
BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect, message); BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect, message);
BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message);
@ -896,7 +896,7 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
BOOST_AUTO_TEST_CASE(script_combineSigs) BOOST_AUTO_TEST_CASE(script_combineSigs)
{ {
// Test the CombineSignatures function // Test the CombineSignatures function
CAmount amount; CAmount amount = 0;
CBasicKeyStore keystore; CBasicKeyStore keystore;
vector<CKey> keys; vector<CKey> keys;
vector<CPubKey> pubkeys; vector<CPubKey> pubkeys;
@ -914,50 +914,50 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
CScript& scriptSig = txTo.vin[0].scriptSig; CScript& scriptSig = txTo.vin[0].scriptSig;
CScript empty; SignatureData empty;
CScript combined = CombineSignatures(scriptPubKey, txTo, 0, amount, empty, empty); SignatureData combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, empty);
BOOST_CHECK(combined.empty()); BOOST_CHECK(combined.scriptSig.empty());
// Single signature case: // Single signature case:
SignSignature(keystore, txFrom, txTo, 0); // changes scriptSig SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); // changes scriptSig
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, scriptSig, empty); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty);
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, empty, scriptSig); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig));
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
CScript scriptSigCopy = scriptSig; CScript scriptSigCopy = scriptSig;
// Signing again will give a different, valid signature: // Signing again will give a different, valid signature:
SignSignature(keystore, txFrom, txTo, 0); SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, scriptSigCopy, scriptSig); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig));
BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig);
// P2SH, single-signature case: // P2SH, single-signature case:
CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG; CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG;
keystore.AddCScript(pkSingle); keystore.AddCScript(pkSingle);
scriptPubKey = GetScriptForDestination(CScriptID(pkSingle)); scriptPubKey = GetScriptForDestination(CScriptID(pkSingle));
SignSignature(keystore, txFrom, txTo, 0); SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, scriptSig, empty); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty);
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, empty, scriptSig); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig));
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
scriptSigCopy = scriptSig; scriptSigCopy = scriptSig;
SignSignature(keystore, txFrom, txTo, 0); SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, scriptSigCopy, scriptSig); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig));
BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig);
// dummy scriptSigCopy with placeholder, should always choose non-placeholder: // dummy scriptSigCopy with placeholder, should always choose non-placeholder:
scriptSigCopy = CScript() << OP_0 << vector<unsigned char>(pkSingle.begin(), pkSingle.end()); scriptSigCopy = CScript() << OP_0 << std::vector<unsigned char>(pkSingle.begin(), pkSingle.end());
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, scriptSigCopy, scriptSig); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig));
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, scriptSig, scriptSigCopy); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), SignatureData(scriptSigCopy));
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
// Hardest case: Multisig 2-of-3 // Hardest case: Multisig 2-of-3
scriptPubKey = GetScriptForMultisig(2, pubkeys); scriptPubKey = GetScriptForMultisig(2, pubkeys);
keystore.AddCScript(scriptPubKey); keystore.AddCScript(scriptPubKey);
SignSignature(keystore, txFrom, txTo, 0); SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, scriptSig, empty); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty);
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, empty, scriptSig); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig));
BOOST_CHECK(combined == scriptSig); BOOST_CHECK(combined.scriptSig == scriptSig);
// A couple of partially-signed versions: // A couple of partially-signed versions:
vector<unsigned char> sig1; vector<unsigned char> sig1;
@ -985,22 +985,22 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
CScript complete13 = CScript() << OP_0 << sig1 << sig3; CScript complete13 = CScript() << OP_0 << sig1 << sig3;
CScript complete23 = CScript() << OP_0 << sig2 << sig3; CScript complete23 = CScript() << OP_0 << sig2 << sig3;
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial1a, partial1b); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial1b));
BOOST_CHECK(combined == partial1a); BOOST_CHECK(combined.scriptSig == partial1a);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial1a, partial2a); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial2a));
BOOST_CHECK(combined == complete12); BOOST_CHECK(combined.scriptSig == complete12);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial2a, partial1a); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial1a));
BOOST_CHECK(combined == complete12); BOOST_CHECK(combined.scriptSig == complete12);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial1b, partial2b); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1b), SignatureData(partial2b));
BOOST_CHECK(combined == complete12); BOOST_CHECK(combined.scriptSig == complete12);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial3b, partial1b); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial1b));
BOOST_CHECK(combined == complete13); BOOST_CHECK(combined.scriptSig == complete13);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial2a, partial3a); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial3a));
BOOST_CHECK(combined == complete23); BOOST_CHECK(combined.scriptSig == complete23);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial3b, partial2b); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial2b));
BOOST_CHECK(combined == complete23); BOOST_CHECK(combined.scriptSig == complete23);
combined = CombineSignatures(scriptPubKey, txTo, 0, amount, partial3b, partial3a); combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial3a));
BOOST_CHECK(combined == partial3c); BOOST_CHECK(combined.scriptSig == partial3c);
} }
BOOST_AUTO_TEST_CASE(script_standard_push) BOOST_AUTO_TEST_CASE(script_standard_push)

10
src/wallet/wallet.cpp

@ -2334,17 +2334,20 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
{ {
bool signSuccess; bool signSuccess;
const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey;
CScript& scriptSigRes = txNew.vin[nIn].scriptSig; SignatureData sigdata;
if (sign) if (sign)
signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes); signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
else else
signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, scriptSigRes); signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);
if (!signSuccess) if (!signSuccess)
{ {
strFailReason = _("Signing transaction failed"); strFailReason = _("Signing transaction failed");
return false; return false;
} else {
UpdateTransaction(txNew, nIn, sigdata);
} }
nIn++; nIn++;
} }
@ -2354,6 +2357,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
if (!sign) { if (!sign) {
BOOST_FOREACH (CTxIn& vin, txNew.vin) BOOST_FOREACH (CTxIn& vin, txNew.vin)
vin.scriptSig = CScript(); vin.scriptSig = CScript();
txNew.wit.SetNull();
} }
// Embed the constructed transaction data in wtxNew. // Embed the constructed transaction data in wtxNew.

Loading…
Cancel
Save