void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { txnouttype type; std::vector<CTxDestination> addresses; int nRequired; out.pushKV("asm", ScriptToAsmStr(scriptPubKey)); if (fIncludeHex) out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())); if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { out.pushKV("type", GetTxnOutputType(type)); return; } out.pushKV("reqSigs", nRequired); out.pushKV("type", GetTxnOutputType(type)); UniValue a(UniValue::VARR); for (const CTxDestination& addr : addresses) a.push_back(CBitcoinAddress(addr).ToString()); out.pushKV("addresses", a); }
bool MultisigDialog::createMultisigTransaction(vector<CTxIn> vUserIn, vector<CTxOut> vUserOut, string& feeStringRet, string& errorRet) { try{ //attempt to access the given inputs CCoinsViewCache view = getInputsCoinsViewCache(vUserIn); //retrieve total input val and change dest CAmount totalIn = 0; vector<CAmount> vInputVals; CScript changePubKey; bool fFirst = true; for(CTxIn in : vUserIn){ const CCoins* coins = view.AccessCoins(in.prevout.hash); if(!coins->IsAvailable(in.prevout.n) || coins == NULL){ continue; } CTxOut prevout = coins->vout[in.prevout.n]; CScript privKey = prevout.scriptPubKey; vInputVals.push_back(prevout.nValue); totalIn += prevout.nValue; if(!fFirst){ if(privKey != changePubKey){ throw runtime_error("Address mismatch! Inputs must originate from the same multisignature address."); } }else{ fFirst = false; changePubKey = privKey; } } CAmount totalOut = 0; //retrieve total output val for(CTxOut out : vUserOut){ totalOut += out.nValue; } if(totalIn < totalOut){ throw runtime_error("Not enough PIV provided as input to complete transaction (including fee)."); } //calculate change amount CAmount changeAmount = totalIn - totalOut; CTxOut change(changeAmount, changePubKey); //generate random position for change unsigned int changeIndex = rand() % (vUserOut.size() + 1); //insert change into random position if(changeIndex < vUserOut.size()){ vUserOut.insert(vUserOut.begin() + changeIndex, change); }else{ vUserOut.emplace_back(change); } //populate tx CMutableTransaction tx; tx.vin = vUserIn; tx.vout = vUserOut; const CCoins* coins = view.AccessCoins(tx.vin[0].prevout.hash); if(coins == NULL || !coins->IsAvailable(tx.vin[0].prevout.n)){ throw runtime_error("Coins unavailable (unconfirmed/spent)"); } CScript prevPubKey = coins->vout[tx.vin[0].prevout.n].scriptPubKey; //get payment destination CTxDestination address; if(!ExtractDestination(prevPubKey, address)){ throw runtime_error("Could not find address for destination."); } CScriptID hash = boost::get<CScriptID>(address); CScript redeemScript; if (!pwalletMain->GetCScript(hash, redeemScript)){ throw runtime_error("could not redeem"); } txnouttype type; vector<CTxDestination> addresses; int nReq; if(!ExtractDestinations(redeemScript, type, addresses, nReq)){ throw runtime_error("Could not extract destinations from redeem script."); } for(CTxIn& in : tx.vin){ in.scriptSig.clear(); //scale estimate to account for multisig scriptSig for(unsigned int i = 0; i < 50*(nReq+addresses.size()); i++){ in.scriptSig << INT64_MAX; } } //calculate fee unsigned int nBytes = tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION); CAmount fee = ::minRelayTxFee.GetFee(nBytes); if(tx.vout.at(changeIndex).nValue > fee){ tx.vout.at(changeIndex).nValue -= fee; feeStringRet = strprintf("%d",((double)fee)/COIN).c_str(); }else{ throw runtime_error("Not enough PIV provided to cover fee"); } //clear junk from script sigs for(CTxIn& in : tx.vin){ in.scriptSig.clear(); } multisigTx = tx; }catch(const runtime_error& e){ errorRet = e.what(); return false; } return true; }