BOOST_FOREACH(const COutput& out, vOutputs) { // unselect already spent, very unlikely scenario, this could happen // when selected are spent elsewhere, like rpc or another computer uint256 txhash = out.tx->GetHash(); COutPoint outpt(txhash, out.i); if (model->isSpent(outpt)) { coinControl->UnSelect(outpt); continue; } // Quantity nQuantity++; // Amount nAmount += out.tx->vout[out.i].nValue; // Priority dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); // Bytes CTxDestination address; if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { CPubKey pubkey; CKeyID *keyid = boost::get<CKeyID>(&address); if (keyid && model->getPubKey(*keyid, pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); if (!pubkey.IsCompressed()) nQuantityUncompressed++; } else nBytesInputs += 148; // in all error cases, simply assume 148 here } else nBytesInputs += 148; }
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type) { switch (type) { case OutputType::LEGACY: return key.GetID(); case OutputType::P2SH_SEGWIT: case OutputType::BECH32: { if (!key.IsCompressed()) return key.GetID(); CTxDestination witdest = WitnessV0KeyHash(key.GetID()); CScript witprog = GetScriptForDestination(witdest); if (type == OutputType::P2SH_SEGWIT) { return CScriptID(witprog); } else { return witdest; } } default: assert(false); } }
BOOST_FOREACH(const COutput& out, vOutputs) { // Quantity nQuantity++; // Amount nAmount += out.tx->vout[out.i].nValue; // Priority dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); // Bytes CTxDestination address; if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { CPubKey pubkey; CKeyID *keyid = boost::get< CKeyID >(&address); if (keyid && model->getPubKey(*keyid, pubkey)) nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); else nBytesInputs += 148; // in all error cases, simply assume 148 here } else nBytesInputs += 148; }
void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) { AssertLockHeld(cs_KeyStore); CKeyID key_id = pubkey.GetID(); // We must actually know about this key already. assert(HaveKey(key_id) || mapWatchKeys.count(key_id)); // This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH // outputs. Technically P2WPKH outputs don't have a redeemscript to be // spent. However, our current IsMine logic requires the corresponding // P2SH-P2WPKH redeemscript to be present in the wallet in order to accept // payment even to P2WPKH outputs. // Also note that having superfluous scripts in the keystore never hurts. // They're only used to guide recursion in signing and IsMine logic - if // a script is present but we can't do anything with it, it has no effect. // "Implicitly" refers to fact that scripts are derived automatically from // existing keys, and are present in memory, even without being explicitly // loaded (e.g. from a file). if (pubkey.IsCompressed()) { CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id)); // This does not use AddCScript, as it may be overridden. CScriptID id(script); mapScripts[id] = std::move(script); } }
Value smsglocalkeys(const Array& params, bool fHelp) { if (fHelp || params.size() > 3) throw runtime_error( "smsglocalkeys [whitelist|all|wallet|recv <+/-> <address>|anon <+/-> <address>]\n" "List and manage keys."); if (!fSecMsgEnabled) throw runtime_error("Secure messaging is disabled."); Object result; std::string mode = "whitelist"; if (params.size() > 0) { mode = params[0].get_str(); }; char cbuf[256]; if (mode == "whitelist" || mode == "all") { uint32_t nKeys = 0; int all = mode == "all" ? 1 : 0; for (std::vector<SecMsgAddress>::iterator it = smsgAddresses.begin(); it != smsgAddresses.end(); ++it) { if (!all && !it->fReceiveEnabled) continue; CBitcoinAddress coinAddress(it->sAddress); if (!coinAddress.IsValid()) continue; std::string sPublicKey; CKeyID keyID; if (!coinAddress.GetKeyID(keyID)) continue; CPubKey pubKey; if (!pwalletMain->GetPubKey(keyID, pubKey)) continue; if (!pubKey.IsValid() || !pubKey.IsCompressed()) { continue; }; sPublicKey = EncodeBase58(pubKey.Raw()); std::string sLabel = pwalletMain->mapAddressBook[keyID]; std::string sInfo; if (all) sInfo = std::string("Receive ") + (it->fReceiveEnabled ? "on, " : "off, "); sInfo += std::string("Anon ") + (it->fReceiveAnon ? "on" : "off"); result.push_back(Pair("key", it->sAddress + " - " + sPublicKey + " " + sInfo + " - " + sLabel)); nKeys++; }; snprintf(cbuf, sizeof(cbuf), "%u keys listed.", nKeys); result.push_back(Pair("result", std::string(cbuf))); } else if (mode == "recv") { if (params.size() < 3) { result.push_back(Pair("result", "Too few parameters.")); result.push_back(Pair("expected", "recv <+/-> <address>")); return result; }; std::string op = params[1].get_str(); std::string addr = params[2].get_str(); std::vector<SecMsgAddress>::iterator it; for (it = smsgAddresses.begin(); it != smsgAddresses.end(); ++it) { if (addr != it->sAddress) continue; break; }; if (it == smsgAddresses.end()) { result.push_back(Pair("result", "Address not found.")); return result; }; if (op == "+" || op == "on" || op == "add" || op == "a") { it->fReceiveEnabled = true; } else if (op == "-" || op == "off" || op == "rem" || op == "r") { it->fReceiveEnabled = false; } else { result.push_back(Pair("result", "Unknown operation.")); return result; }; std::string sInfo; sInfo = std::string("Receive ") + (it->fReceiveEnabled ? "on, " : "off,"); sInfo += std::string("Anon ") + (it->fReceiveAnon ? "on" : "off"); result.push_back(Pair("result", "Success.")); result.push_back(Pair("key", it->sAddress + " " + sInfo)); return result; } else if (mode == "anon") { if (params.size() < 3) { result.push_back(Pair("result", "Too few parameters.")); result.push_back(Pair("expected", "anon <+/-> <address>")); return result; }; std::string op = params[1].get_str(); std::string addr = params[2].get_str(); std::vector<SecMsgAddress>::iterator it; for (it = smsgAddresses.begin(); it != smsgAddresses.end(); ++it) { if (addr != it->sAddress) continue; break; }; if (it == smsgAddresses.end()) { result.push_back(Pair("result", "Address not found.")); return result; }; if (op == "+" || op == "on" || op == "add" || op == "a") { it->fReceiveAnon = true; } else if (op == "-" || op == "off" || op == "rem" || op == "r") { it->fReceiveAnon = false; } else { result.push_back(Pair("result", "Unknown operation.")); return result; }; std::string sInfo; sInfo = std::string("Receive ") + (it->fReceiveEnabled ? "on, " : "off,"); sInfo += std::string("Anon ") + (it->fReceiveAnon ? "on" : "off"); result.push_back(Pair("result", "Success.")); result.push_back(Pair("key", it->sAddress + " " + sInfo)); return result; } else if (mode == "wallet") { uint32_t nKeys = 0; BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& entry, pwalletMain->mapAddressBook) { if (!IsMine(*pwalletMain, entry.first)) continue; CBitcoinAddress coinAddress(entry.first); if (!coinAddress.IsValid()) continue; std::string address; std::string sPublicKey; address = coinAddress.ToString(); CKeyID keyID; if (!coinAddress.GetKeyID(keyID)) continue; CPubKey pubKey; if (!pwalletMain->GetPubKey(keyID, pubKey)) continue; if (!pubKey.IsValid() || !pubKey.IsCompressed()) { continue; }; sPublicKey = EncodeBase58(pubKey.Raw()); result.push_back(Pair("key", address + " - " + sPublicKey + " - " + entry.second)); nKeys++; }; snprintf(cbuf, sizeof(cbuf), "%u keys listed from wallet.", nKeys); result.push_back(Pair("result", std::string(cbuf))); } else
void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { if (!model) return; // nPayAmount CAmount nPayAmount = 0; bool fDust = false; CMutableTransaction txDummy; for (const CAmount &amount : CoinControlDialog::payAmounts) { nPayAmount += amount; if (amount > 0) { CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0)); txDummy.vout.push_back(txout); fDust |= IsDust(txout, ::dustRelayFee); } } CAmount nAmount = 0; CAmount nPayFee = 0; CAmount nAfterFee = 0; CAmount nChange = 0; unsigned int nBytes = 0; unsigned int nBytesInputs = 0; unsigned int nQuantity = 0; bool fWitness = false; std::vector<COutPoint> vCoinControl; std::vector<COutput> vOutputs; coinControl->ListSelected(vCoinControl); model->getOutputs(vCoinControl, vOutputs); for (const COutput& out : vOutputs) { // unselect already spent, very unlikely scenario, this could happen // when selected are spent elsewhere, like rpc or another computer uint256 txhash = out.tx->GetHash(); COutPoint outpt(txhash, out.i); if (model->isSpent(outpt)) { coinControl->UnSelect(outpt); continue; } // Quantity nQuantity++; // Amount nAmount += out.tx->tx->vout[out.i].nValue; // Bytes CTxDestination address; int witnessversion = 0; std::vector<unsigned char> witnessprogram; if (out.tx->tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); fWitness = true; } else if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address)) { CPubKey pubkey; CKeyID *keyid = boost::get<CKeyID>(&address); if (keyid && model->getPubKey(*keyid, pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); } else nBytesInputs += 148; // in all error cases, simply assume 148 here } else nBytesInputs += 148; } // calculation if (nQuantity > 0) { // Bytes nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here if (fWitness) { // there is some fudging in these numbers related to the actual virtual transaction size calculation that will keep this estimate from being exact. // usually, the result will be an overestimate within a couple of satoshis so that the confirmation dialog ends up displaying a slightly smaller fee. // also, the witness stack size value is a variable sized integer. usually, the number of stack items will be well under the single byte var int limit. nBytes += 2; // account for the serialized marker and flag bytes nBytes += nQuantity; // account for the witness byte that holds the number of stack items for each input. } // in the subtract fee from amount case, we can tell if zero change already and subtract the bytes, so that fee calculation afterwards is accurate if (CoinControlDialog::fSubtractFeeFromAmount) if (nAmount - nPayAmount == 0) nBytes -= 34; // Fee nPayFee = CWallet::GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); if (nPayAmount > 0) { nChange = nAmount - nPayAmount; if (!CoinControlDialog::fSubtractFeeFromAmount) nChange -= nPayFee; // Never create dust outputs; if we would, just add the dust to the fee. if (nChange > 0 && nChange < MIN_CHANGE) { CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0)); if (IsDust(txout, ::dustRelayFee)) { nPayFee += nChange; nChange = 0; if (CoinControlDialog::fSubtractFeeFromAmount) nBytes -= 34; // we didn't detect lack of change above } } if (nChange == 0 && !CoinControlDialog::fSubtractFeeFromAmount) nBytes -= 34; } // after fee nAfterFee = std::max<CAmount>(nAmount - nPayFee, 0); } // actually update labels int nDisplayUnit = WiFicoinUnits::WFC; if (model && model->getOptionsModel()) nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity"); QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount"); QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee"); QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee"); QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes"); QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput"); QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange"); // enable/disable "dust" and "change" dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0); dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0); dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0); dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0); // stats l1->setText(QString::number(nQuantity)); // Quantity l2->setText(WiFicoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount l3->setText(WiFicoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee l4->setText(WiFicoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes l7->setText(fDust ? tr("yes") : tr("no")); // Dust l8->setText(WiFicoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change if (nPayFee > 0) { l3->setText(ASYMP_UTF8 + l3->text()); l4->setText(ASYMP_UTF8 + l4->text()); if (nChange > 0 && !CoinControlDialog::fSubtractFeeFromAmount) l8->setText(ASYMP_UTF8 + l8->text()); } // turn label red when dust l7->setStyleSheet((fDust) ? "color:red;" : ""); // tool tips QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); // how many satoshis the estimated fee can vary per byte we guess wrong double dFeeVary = (double)nPayFee / nBytes; QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); l3->setToolTip(toolTip4); l4->setToolTip(toolTip4); l7->setToolTip(toolTipDust); l8->setToolTip(toolTip4); dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip()); dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip()); dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); // Insufficient funds QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds"); if (label) label->setVisible(nChange < 0); }
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { std::vector<valtype> vSolutions; txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) { if (keystore.HaveWatchOnly(scriptPubKey)) return ISMINE_WATCH_UNSOLVABLE; return ISMINE_NO; } CKeyID keyID; switch (whichType) { case TX_NONSTANDARD: case TX_NULL_DATA: break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) { isInvalid = true; return ISMINE_NO; } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; case TX_WITNESS_V0_KEYHASH: { if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { // We do not support bare witness outputs unless the P2SH version of it would be // acceptable as well. This protects against matching before segwit activates. // This also applies to the P2WSH case. break; } isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; break; } case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (sigversion != SIGVERSION_BASE) { CPubKey pubkey; if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { isInvalid = true; return ISMINE_NO; } } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; case TX_SCRIPTHASH: { CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { isminetype ret = IsMine(keystore, subscript, isInvalid); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; } case TX_WITNESS_V0_SCRIPTHASH: { if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { break; } 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, isInvalid, SIGVERSION_WITNESS_V0); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; } case TX_MULTISIG: { // Only consider transactions "mine" if we own ALL the // keys involved. Multi-signature transactions that are // partially owned (somebody else has a key that can spend // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); if (sigversion != SIGVERSION_BASE) { for (size_t i = 0; i < keys.size(); i++) { if (keys[i].size() != 33) { isInvalid = true; return ISMINE_NO; } } } if (HaveKeys(keys, keystore) == keys.size()) return ISMINE_SPENDABLE; break; } } if (keystore.HaveWatchOnly(scriptPubKey)) { // TODO: This could be optimized some by doing some work after the above solver SignatureData sigs; return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; } return ISMINE_NO; }