SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2) { txnouttype txType; std::vector<std::vector<unsigned char> > vSolutions; Solver(scriptPubKey, txType, vSolutions); return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SIGVERSION_BASE).Output(); }
static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const txnouttype txType, const std::vector<valtype>& vSolutions, Stacks sigs1, Stacks sigs2, SigVersion sigversion) { switch (txType) { case TX_NONSTANDARD: case TX_NULL_DATA: // Don't know anything about this, assume bigger one is correct: if (sigs1.script.size() >= sigs2.script.size()) return sigs1; return sigs2; case TX_PUBKEY: case TX_PUBKEYHASH: // Signatures are bigger than placeholders or empty scripts: if (sigs1.script.empty() || sigs1.script[0].empty()) return sigs2; return sigs1; case TX_WITNESS_V0_KEYHASH: // Signatures are bigger than placeholders or empty scripts: if (sigs1.witness.empty() || sigs1.witness[0].empty()) return sigs2; return sigs1; case TX_SCRIPTHASH: if (sigs1.script.empty() || sigs1.script.back().empty()) return sigs2; else if (sigs2.script.empty() || sigs2.script.back().empty()) return sigs1; else { // Recur to combine: valtype spk = sigs1.script.back(); CScript pubKey2(spk.begin(), spk.end()); txnouttype txType2; std::vector<std::vector<unsigned char> > vSolutions2; Solver(pubKey2, txType2, vSolutions2); sigs1.script.pop_back(); sigs2.script.pop_back(); Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion); result.script.push_back(spk); return result; } case TX_MULTISIG: return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, sigversion)); case TX_WITNESS_V0_SCRIPTHASH: if (sigs1.witness.empty() || sigs1.witness.back().empty()) return sigs2; else if (sigs2.witness.empty() || sigs2.witness.back().empty()) return sigs1; else { // Recur to combine: CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end()); txnouttype txType2; std::vector<valtype> vSolutions2; Solver(pubKey2, txType2, vSolutions2); sigs1.witness.pop_back(); sigs1.script = sigs1.witness; sigs1.witness.clear(); sigs2.witness.pop_back(); sigs2.script = sigs2.witness; sigs2.witness.clear(); Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SIGVERSION_WITNESS_V0); result.witness = result.script; result.script.clear(); result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end())); return result; } default: return Stacks(); } }
static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) { int nHashType = SIGHASH_ALL; if (flagStr.size() > 0) if (!findSighashFlags(nHashType, flagStr)) throw std::runtime_error("unknown sighash flag/sign option"); // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: CMutableTransaction mergedTx{tx}; const CMutableTransaction txv{tx}; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) throw std::runtime_error("privatekeys register variable must be set."); CBasicKeyStore tempKeystore; UniValue keysObj = registers["privatekeys"]; for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) throw std::runtime_error("privatekey not a std::string"); CKey key = DecodeSecret(keysObj[kidx].getValStr()); if (!key.IsValid()) { throw std::runtime_error("privatekey not valid"); } tempKeystore.AddKey(key); } // Add previous txouts given in the RPC call: if (!registers.count("prevtxs")) throw std::runtime_error("prevtxs register variable must be set."); UniValue prevtxsObj = registers["prevtxs"]; { for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) { UniValue prevOut = prevtxsObj[previdx]; if (!prevOut.isObject()) throw std::runtime_error("expected prevtxs internal object"); std::map<std::string, UniValue::VType> types = { {"txid", UniValue::VSTR}, {"vout", UniValue::VNUM}, {"scriptPubKey", UniValue::VSTR}, }; if (!prevOut.checkObject(types)) throw std::runtime_error("prevtxs internal object typecheck fail"); uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid"); int nOut = atoi(prevOut["vout"].getValStr()); if (nOut < 0) throw std::runtime_error("vout must be positive"); COutPoint out(txid, nOut); std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { const Coin& coin = view.AccessCoin(out); if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+ ScriptToAsmStr(scriptPubKey); throw std::runtime_error(err); } Coin newcoin; newcoin.out.scriptPubKey = scriptPubKey; newcoin.out.nValue = 0; if (prevOut.exists("amount")) { newcoin.out.nValue = AmountFromValue(prevOut["amount"]); } newcoin.nHeight = 1; view.AddCoin(out, std::move(newcoin), true); } // if redeemScript given and private keys given, // add redeemScript to the tempKeystore so it can be signed: if ((scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash()) && prevOut.exists("redeemScript")) { UniValue v = prevOut["redeemScript"]; std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } } } const CKeyStore& keystore = tempKeystore; bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; const Coin& coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { continue; } const CScript& prevPubKey = coin.out.scriptPubKey; const CAmount& amount = coin.out.nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) 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)); UpdateInput(txin, sigdata); } tx = mergedTx; }
bool MultisigDialog::signMultisigTx(CMutableTransaction& tx, string& errorOut, QVBoxLayout* keyList) { //will be set false if all inputs are not fully signed(valid) bool fComplete = true; //if keyslist is not default value AND has items in list then true bool fGivenKeys = (keyList != nullptr) && (keyList->count() > 0); try{ //copy of vin for reference before vin is mutated vector<CTxIn> oldVin(tx.vin); CBasicKeyStore privKeystore; //if keys were given, attempt to collect redeem and scriptpubkey if(fGivenKeys){ for(int i = 0; i < keyList->count(); i++){ QWidget* keyFrame = qobject_cast<QWidget*>(keyList->itemAt(i)->widget()); QLineEdit* key = keyFrame->findChild<QLineEdit*>("key"); CBitcoinSecret vchSecret; if (!vchSecret.SetString(key->text().toStdString())) throw runtime_error("Invalid private key"); CKey cKey = vchSecret.GetKey(); if (!cKey.IsValid()) throw runtime_error("Private key outside allowed range"); privKeystore.AddKey(cKey); } for(CTxIn& txin : tx.vin){ //get inputs CTransaction txVin; uint256 hashBlock; if (!GetTransaction(txin.prevout.hash, txVin, hashBlock, true)) throw runtime_error("txin could not be found"); if (hashBlock == 0) throw runtime_error("txin is unconfirmed"); //get pubkey from input CScript prevPubKey = txVin.vout[txin.prevout.n].scriptPubKey; //get payment destination CTxDestination address; if(!ExtractDestination(prevPubKey, address)){ throw runtime_error("Could not find address for destination."); } //get redeem script related to destination CScriptID hash = boost::get<CScriptID>(address); CScript redeemScript; if (!pwalletMain->GetCScript(hash, redeemScript)){ errorOut = "could not redeem"; } privKeystore.AddCScript(redeemScript); } }else{ if (model->getEncryptionStatus() == model->Locked) { if (!model->requestUnlock(true).isValid()) { // Unlock wallet was cancelled throw runtime_error("Error: Your wallet is locked. Please enter the wallet passphrase first."); } } } //choose between local wallet and provided const CKeyStore& keystore = fGivenKeys ? privKeystore : *pwalletMain; //attempt to sign each input from local wallet int nIn = 0; for(CTxIn& txin : tx.vin){ //get inputs CTransaction txVin; uint256 hashBlock; if (!GetTransaction(txin.prevout.hash, txVin, hashBlock, true)) throw runtime_error("txin could not be found"); if (hashBlock == 0) throw runtime_error("txin is unconfirmed"); txin.scriptSig.clear(); CScript prevPubKey = txVin.vout[txin.prevout.n].scriptPubKey; //sign what we can SignSignature(keystore, prevPubKey, tx, nIn); //merge in any previous signatures txin.scriptSig = CombineSignatures(prevPubKey, tx, nIn, txin.scriptSig, oldVin[nIn].scriptSig); if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&tx, nIn))){ fComplete = false; } nIn++; } ui->signButtonStatus->setText(buildMultisigTxStatusString(fComplete, tx)); }catch(const runtime_error& e){ errorOut = string(e.what()); fComplete = false; } return fComplete; }
void MultisigDialog::on_signTransactionButton_clicked() { ui->signedTransaction->clear(); if(!model) return; CWallet *wallet = model->getWallet(); // Decode the raw transaction std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString())); CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { ss >> tx; } catch(std::exception &e) { (void)e; return; } CTransaction mergedTx(tx); // Fetch previous transactions (inputs) std::map<COutPoint, CScript> mapPrevOut; for(unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTransaction tempTx; MapPrevTx mapPrevTx; CTxDB txdb("r"); std::map<uint256, CTxIndex> unused; bool fInvalid; tempTx.vin.push_back(mergedTx.vin[i]); tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); BOOST_FOREACH(const CTxIn& txin, tempTx.vin) { const uint256& prevHash = txin.prevout.hash; if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n) mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey; } } // Add the redeem scripts to the wallet keystore for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) { QString redeemScriptStr = entry->getRedeemScript(); if(redeemScriptStr.size() > 0) { std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString())); CScript redeemScript(scriptData.begin(), scriptData.end()); wallet->AddCScript(redeemScript); } } } WalletModel::UnlockContext ctx(model->requestUnlock()); if(!ctx.isValid()) return; // Sign what we can bool fComplete = true; for(unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; if(mapPrevOut.count(txin.prevout) == 0) { fComplete = false; continue; } const CScript& prevPubKey = mapPrevOut[txin.prevout]; txin.scriptSig.clear(); SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL); txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig); if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0)) { fComplete = false; } } CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << mergedTx; ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str()); if(fComplete) { ui->statusLabel->setText(tr("Transaction signature is complete")); ui->sendTransactionButton->setEnabled(true); } else { ui->statusLabel->setText(tr("Transaction is NOT completely signed")); ui->sendTransactionButton->setEnabled(false); } }
static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) { int nHashType = SIGHASH_ALL; if (flagStr.size() > 0) if (!findSighashFlags(nHashType, flagStr)) throw std::runtime_error("unknown sighash flag/sign option"); std::vector<CTransaction> txVariants; txVariants.push_back(tx); // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: CMutableTransaction mergedTx(txVariants[0]); bool fComplete = true; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) throw std::runtime_error("privatekeys register variable must be set."); CBasicKeyStore tempKeystore; UniValue keysObj = registers["privatekeys"]; for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) throw std::runtime_error("privatekey not a std::string"); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(keysObj[kidx].getValStr()); if (!fGood) throw std::runtime_error("privatekey not valid"); CKey key = vchSecret.GetKey(); tempKeystore.AddKey(key); } // Add previous txouts given in the RPC call: if (!registers.count("prevtxs")) throw std::runtime_error("prevtxs register variable must be set."); UniValue prevtxsObj = registers["prevtxs"]; { for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) { UniValue prevOut = prevtxsObj[previdx]; if (!prevOut.isObject()) throw std::runtime_error("expected prevtxs internal object"); std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR); if (!prevOut.checkObject(types)) throw std::runtime_error("prevtxs internal object typecheck fail"); uint256 txid = ParseHashUV(prevOut["txid"], "txid"); int nOut = atoi(prevOut["vout"].getValStr()); if (nOut < 0) throw std::runtime_error("vout must be positive"); std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { CCoinsModifier coins = view.ModifyCoins(txid); if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ ScriptToAsmStr(scriptPubKey); throw std::runtime_error(err); } if ((unsigned int)nOut >= coins->vout.size()) coins->vout.resize(nOut+1); coins->vout[nOut].scriptPubKey = scriptPubKey; coins->vout[nOut].nValue = 0; if (prevOut.exists("amount")) { coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]); } } // if redeemScript given and private keys given, // add redeemScript to the tempKeystore so it can be signed: if ((scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash()) && prevOut.exists("redeemScript")) { UniValue v = prevOut["redeemScript"]; std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } } } const CKeyStore& keystore = tempKeystore; bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; const CCoins* coins = view.AccessCoins(txin.prevout.hash); if (!coins || !coins->IsAvailable(txin.prevout.n)) { fComplete = false; continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; const CAmount& amount = coins->vout[txin.prevout.n].nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: BOOST_FOREACH(const CTransaction& txv, txVariants) 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))) fComplete = false; } if (fComplete) { // do nothing... for now // perhaps store this for later optional JSON output } tx = mergedTx; }
UniValue signrawtransaction(const JSONRPCRequest& request) { #ifdef ENABLE_WALLET CWallet * const pwallet = GetWalletForJSONRPCRequest(request); #endif if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" "\nSign inputs for raw transaction (serialized, hex-encoded).\n" "The second optional argument (may be null) is an array of previous transaction outputs that\n" "this transaction depends on but may not yet be in the block chain.\n" "The third optional argument (may be null) is an array of base58-encoded private\n" "keys that, if given, will be the only keys used to sign the transaction.\n" #ifdef ENABLE_WALLET + HelpRequiringPassphrase(pwallet) + "\n" #endif "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex string\n" "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" " [ (json array of json objects, or 'null' if none provided)\n" " {\n" " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n" " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" " \"amount\": value (numeric, required) The amount spent\n" " }\n" " ,...\n" " ]\n" "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n" " [ (json array of strings, or 'null' if none provided)\n" " \"privatekey\" (string) private key in base58-encoding\n" " ,...\n" " ]\n" "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" " \"ALL\"\n" " \"NONE\"\n" " \"SINGLE\"\n" " \"ALL|ANYONECANPAY\"\n" " \"NONE|ANYONECANPAY\"\n" " \"SINGLE|ANYONECANPAY\"\n" "\nResult:\n" "{\n" " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" " {\n" " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" " \"sequence\" : n, (numeric) Script sequence number\n" " \"error\" : \"text\" (string) Verification or signing error related to the input\n" " }\n" " ,...\n" " ]\n" "}\n" "\nExamples:\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + HelpExampleRpc("signrawtransaction", "\"myhex\"") ); ObserveSafeMode(); #ifdef ENABLE_WALLET LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else LOCK(cs_main); #endif RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); CMutableTransaction mtx; if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); // Fetch previous transactions (inputs): CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { LOCK(mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view for (const CTxIn& txin : mtx.vin) { view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail. } view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } bool fGivenKeys = false; CBasicKeyStore tempKeystore; if (!request.params[2].isNull()) { fGivenKeys = true; UniValue keys = request.params[2].get_array(); for (unsigned int idx = 0; idx < keys.size(); idx++) { UniValue k = keys[idx]; CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(k.get_str()); if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); CKey key = vchSecret.GetKey(); if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); tempKeystore.AddKey(key); } } #ifdef ENABLE_WALLET else if (pwallet) { EnsureWalletIsUnlocked(pwallet); } #endif // Add previous txouts given in the RPC call: if (!request.params[1].isNull()) { UniValue prevTxs = request.params[1].get_array(); for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { const UniValue& p = prevTxs[idx]; if (!p.isObject()) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); UniValue prevOut = p.get_obj(); RPCTypeCheckObj(prevOut, { {"txid", UniValueType(UniValue::VSTR)}, {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, }); uint256 txid = ParseHashO(prevOut, "txid"); int nOut = find_value(prevOut, "vout").get_int(); if (nOut < 0) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); COutPoint out(txid, nOut); std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { const Coin& coin = view.AccessCoin(out); if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+ ScriptToAsmStr(scriptPubKey); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } Coin newcoin; newcoin.out.scriptPubKey = scriptPubKey; newcoin.out.nValue = 0; if (prevOut.exists("amount")) { newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount")); } newcoin.nHeight = 1; view.AddCoin(out, std::move(newcoin), true); } // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { RPCTypeCheckObj(prevOut, { {"txid", UniValueType(UniValue::VSTR)}, {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, {"redeemScript", UniValueType(UniValue::VSTR)}, }); UniValue v = find_value(prevOut, "redeemScript"); if (!v.isNull()) { std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } } } } #ifdef ENABLE_WALLET const CKeyStore& keystore = ((fGivenKeys || !pwallet) ? tempKeystore : *pwallet); #else const CKeyStore& keystore = tempKeystore; #endif int nHashType = SIGHASH_ALL; if (!request.params[3].isNull()) { static std::map<std::string, int> mapSigHashValues = { {std::string("ALL"), int(SIGHASH_ALL)}, {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)}, {std::string("NONE"), int(SIGHASH_NONE)}, {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)}, {std::string("SINGLE"), int(SIGHASH_SINGLE)}, {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)}, }; std::string strHashType = request.params[3].get_str(); if (mapSigHashValues.count(strHashType)) nHashType = mapSigHashValues[strHashType]; else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); } bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); // Script verification errors UniValue vErrors(UniValue::VARR); // Use CTransaction for the constant parts of the // transaction to avoid rehashing. const CTransaction txConst(mtx); // Sign what we can: for (unsigned int i = 0; i < mtx.vin.size(); i++) { CTxIn& txin = mtx.vin[i]; const Coin& coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); continue; } const CScript& prevPubKey = coin.out.scriptPubKey; const CAmount& amount = coin.out.nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mtx.vout.size())) ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata); sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i)); UpdateTransaction(mtx, i, sigdata); ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } bool fComplete = vErrors.empty(); UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", EncodeHexTx(mtx))); result.push_back(Pair("complete", fComplete)); if (!vErrors.empty()) { result.push_back(Pair("errors", vErrors)); } return result; }
UniValue combinerawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "combinerawtransaction [\"hexstring\",...]\n" "\nCombine multiple partially signed transactions into one transaction.\n" "The combined transaction may be another partially signed transaction or a \n" "fully signed transaction." "\nArguments:\n" "1. \"txs\" (string) A json array of hex strings of partially signed transactions\n" " [\n" " \"hexstring\" (string) A transaction hash\n" " ,...\n" " ]\n" "\nResult:\n" "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" "\nExamples:\n" + HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]") ); UniValue txs = request.params[0].get_array(); std::vector<CMutableTransaction> txVariants(txs.size()); for (unsigned int idx = 0; idx < txs.size(); idx++) { if (!DecodeHexTx(txVariants[idx], txs[idx].get_str(), true)) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed for tx %d", idx)); } } if (txVariants.empty()) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transactions"); } // mergedTx will end up with all the signatures; it // starts as a clone of the rawtx: CMutableTransaction mergedTx(txVariants[0]); // Fetch previous transactions (inputs): CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { LOCK(cs_main); LOCK(mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view for (const CTxIn& txin : mergedTx.vin) { view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail. } view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } // Use CTransaction for the constant parts of the // transaction to avoid rehashing. const CTransaction txConst(mergedTx); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; const Coin& coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { 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; // ... and merge in other signatures: for (const CMutableTransaction& txv : txVariants) { if (txv.vin.size() > i) { sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); } } UpdateTransaction(mergedTx, i, sigdata); } return EncodeHexTx(mergedTx); }
void MultisigDialog::on_signTransactionButton_clicked() { ui->signedTransaction->clear(); if(!model) return; CWallet *wallet = model->getWallet(); // Decode the raw transaction std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString())); CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { ss >> tx; } catch(std::exception &e) { return; } CTransaction mergedTx(tx); // Fetch previous transactions (inputs) // duplicated in rpcrawtransaction.cpp:389 CCoinsView viewDummy; CCoinsViewCache view(viewDummy); { LOCK(mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { const uint256& prevHash = txin.prevout.hash; CCoins coins; view.GetCoins(prevHash, coins); // this is certainly allowed to fail } view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } // Add the redeem scripts to the wallet keystore for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) { QString redeemScriptStr = entry->getRedeemScript(); if(redeemScriptStr.size() > 0) { std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString())); CScript redeemScript(scriptData.begin(), scriptData.end()); wallet->AddCScript(redeemScript); } } } WalletModel::UnlockContext ctx(model->requestUnlock()); if(!ctx.isValid()) return; // Sign what we can: // mostly like rpcrawtransaction:503 bool fComplete = true; // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; CCoins coins; if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) { fComplete = false; continue; } const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey; txin.scriptSig.clear(); SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL); txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig); if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)) fComplete = false; } CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << mergedTx; ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str()); if(fComplete) { ui->statusLabel->setText(tr("Transaction signature is complete")); ui->sendTransactionButton->setEnabled(true); } else { ui->statusLabel->setText(tr("Transaction is NOT completely signed")); ui->sendTransactionButton->setEnabled(false); } }