Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid) { LOCK2(cs_main, wallet->cs_wallet); if (!errors.empty()) { return Result::MISC_ERROR; } auto it = txid.IsNull() ? wallet->mapWallet.end() : wallet->mapWallet.find(txid); if (it == wallet->mapWallet.end()) { errors.push_back("Invalid or non-wallet transaction id"); return Result::MISC_ERROR; } CWalletTx& oldWtx = it->second; // make sure the transaction still has no descendants and hasn't been mined in the meantime Result result = PreconditionChecks(wallet, oldWtx, errors); if (result != Result::OK) { return result; } CWalletTx wtxBumped(wallet, MakeTransactionRef(std::move(mtx))); // commit/broadcast the tx CReserveKey reservekey(wallet); wtxBumped.mapValue = oldWtx.mapValue; wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString(); wtxBumped.vOrderForm = oldWtx.vOrderForm; wtxBumped.strFromAccount = oldWtx.strFromAccount; wtxBumped.fTimeReceivedIsTxTime = true; wtxBumped.fFromMe = true; CValidationState state; if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason())); return Result::WALLET_ERROR; } bumped_txid = wtxBumped.GetHash(); if (state.IsInvalid()) { // This can happen if the mempool rejected the transaction. Report // what happened in the "errors" response. errors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); } // mark the original tx as bumped if (!wallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) { // TODO: see if JSON-RPC has a standard way of returning a response // along with an exception. It would be good to return information about // wtxBumped to the caller even if marking the original transaction // replaced does not succeed for some reason. errors.push_back("Created new bumpfee transaction but could not mark the original transaction as replaced"); } return Result::OK; }
bool CFeeBumper::commit(CWallet *pWallet) { AssertLockHeld(pWallet->cs_wallet); if (!vErrors.empty() || currentResult != BumpFeeResult::OK) { return false; } if (txid.IsNull() || !pWallet->mapWallet.count(txid)) { vErrors.push_back("Invalid or non-wallet transaction id"); currentResult = BumpFeeResult::MISC_ERROR; return false; } CWalletTx& oldWtx = pWallet->mapWallet[txid]; // make sure the transaction still has no descendants and hasen't been mined in the meantime if (!preconditionChecks(pWallet, oldWtx)) { return false; } CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx))); // commit/broadcast the tx CReserveKey reservekey(pWallet); wtxBumped.mapValue = oldWtx.mapValue; wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString(); wtxBumped.vOrderForm = oldWtx.vOrderForm; wtxBumped.strFromAccount = oldWtx.strFromAccount; wtxBumped.fTimeReceivedIsTxTime = true; wtxBumped.fFromMe = true; CValidationState state; if (!pWallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. vErrors.push_back(strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); return false; } bumpedTxid = wtxBumped.GetHash(); if (state.IsInvalid()) { // This can happen if the mempool rejected the transaction. Report // what happened in the "errors" response. vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); } // mark the original tx as bumped if (!pWallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) { // TODO: see if JSON-RPC has a standard way of returning a response // along with an exception. It would be good to return information about // wtxBumped to the caller even if marking the original transaction // replaced does not succeed for some reason. vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); } return true; }
CWalletTx& AddTx(CRecipient recipient) { CWalletTx wtx; CReserveKey reservekey(wallet.get()); CAmount fee; int changePos = -1; std::string error; CCoinControl dummy; BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error, dummy)); CValidationState state; BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state)); auto it = wallet->mapWallet.find(wtx.GetHash()); BOOST_CHECK(it != wallet->mapWallet.end()); CreateAndProcessBlock({CMutableTransaction(*it->second.tx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); it->second.SetMerkleBranch(chainActive.Tip(), 1); return it->second; }
void NewAccountDialog::showSyncQr() { WalletModel::UnlockContext ctx(walletModel->requestUnlock()); if (ctx.isValid()) { newAccount = pwalletMain->GenerateNewAccount(ui->newAccountName->text().toStdString(), AccountType::Normal, AccountSubType::Mobi); LOCK(pwalletMain->cs_wallet); { int64_t currentTime = newAccount->getEarliestPossibleCreationTime(); std::string payoutAddress; CReserveKey reservekey(pwalletMain, newAccount, KEYCHAIN_CHANGE); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey)) return; payoutAddress = CBitcoinAddress(vchPubKey.GetID()).ToString(); QString qrString = QString::fromStdString("guldensync:" + CBitcoinSecretExt(*newAccount->GetAccountMasterPrivKey()).ToString(QString::number(currentTime).toStdString(), payoutAddress)); ui->scanQRCode->setCode(qrString); disconnect(this, SLOT(showSyncQr())); } } }
UniValue gobject(const JSONRPCRequest& request) { std::string strCommand; if (request.params.size() >= 1) strCommand = request.params[0].get_str(); if (request.fHelp || ( #ifdef ENABLE_WALLET strCommand != "prepare" && #endif // ENABLE_WALLET strCommand != "vote-many" && strCommand != "vote-conf" && strCommand != "vote-name" && strCommand != "submit" && strCommand != "count" && strCommand != "deserialize" && strCommand != "get" && strCommand != "getvotes" && strCommand != "getcurrentvotes" && strCommand != "list" && strCommand != "diff" && strCommand != "check" )) throw std::runtime_error( "gobject \"command\"...\n" "Manage governance objects\n" "\nAvailable commands:\n" " check - Validate governance object data (proposal only)\n" #ifdef ENABLE_WALLET " prepare - Prepare governance object by signing and creating tx\n" #endif // ENABLE_WALLET " submit - Submit governance object to network\n" " deserialize - Deserialize governance object from hex string to JSON\n" " count - Count governance objects and votes (additional param: 'json' or 'all', default: 'json')\n" " get - Get governance object by hash\n" " getvotes - Get all votes for a governance object hash (including old votes)\n" " getcurrentvotes - Get only current (tallying) votes for a governance object hash (does not include old votes)\n" " list - List governance objects (can be filtered by signal and/or object type)\n" " diff - List differences since last diff\n" " vote-name - Vote on a governance object by masternode name (using masternode.conf setup)\n" " vote-conf - Vote on a governance object by masternode configured in syscoin.conf\n" " vote-many - Vote on a governance object by all masternodes (using masternode.conf setup)\n" ); if(strCommand == "count") { std::string strMode{"json"}; if (request.params.size() == 2) { strMode = request.params[1].get_str(); } if (request.params.size() > 2 || (strMode != "json" && strMode != "all")) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject count ( \"json\"|\"all\" )'"); } return strMode == "json" ? governance.ToJson() : governance.ToString(); } /* ------ Example Governance Item ------ gobject submit 6e622bb41bad1fb18e7f23ae96770aeb33129e18bd9efe790522488e580a0a03 0 1 1464292854 "beer-reimbursement" 5b5b22636f6e7472616374222c207b2270726f6a6563745f6e616d65223a20225c22626565722d7265696d62757273656d656e745c22222c20227061796d656e745f61646472657373223a20225c225879324c4b4a4a64655178657948726e34744744514238626a6876464564615576375c22222c2022656e645f64617465223a202231343936333030343030222c20226465736372697074696f6e5f75726c223a20225c227777772e646173687768616c652e6f72672f702f626565722d7265696d62757273656d656e745c22222c2022636f6e74726163745f75726c223a20225c22626565722d7265696d62757273656d656e742e636f6d2f3030312e7064665c22222c20227061796d656e745f616d6f756e74223a20223233342e323334323232222c2022676f7665726e616e63655f6f626a6563745f6964223a2037342c202273746172745f64617465223a202231343833323534303030227d5d5d1 */ // DEBUG : TEST DESERIALIZATION OF GOVERNANCE META DATA if(strCommand == "deserialize") { if (request.params.size() != 2) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject deserialize <data-hex>'"); } std::string strHex = request.params[1].get_str(); std::vector<unsigned char> v = ParseHex(strHex); std::string s(v.begin(), v.end()); UniValue u(UniValue::VOBJ); u.read(s); return u.write().c_str(); } // VALIDATE A GOVERNANCE OBJECT PRIOR TO SUBMISSION if(strCommand == "check") { if (request.params.size() != 2) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject check <data-hex>'"); } // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS uint256 hashParent; int nRevision = 1; int64_t nTime = GetAdjustedTime(); std::string strDataHex = request.params[1].get_str(); CGovernanceObject govobj(hashParent, nRevision, nTime, uint256(), strDataHex); if(govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { CProposalValidator validator(strDataHex); if(!validator.Validate()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid object type, only proposals can be validated"); } UniValue objResult(UniValue::VOBJ); objResult.push_back(Pair("Object status", "OK")); return objResult; } #ifdef ENABLE_WALLET // PREPARE THE GOVERNANCE OBJECT BY CREATING A COLLATERAL TRANSACTION if(strCommand == "prepare") { if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; if (request.params.size() != 5) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject prepare <parent-hash> <revision> <time> <data-hex>'"); } // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS uint256 hashParent; // -- attach to root node (root node doesn't really exist, but has a hash of zero) if(request.params[1].get_str() == "0") { hashParent = uint256(); } else { hashParent = ParseHashV(request.params[1], "fee-txid, parameter 1"); } std::string strRevision = request.params[2].get_str(); std::string strTime = request.params[3].get_str(); int nRevision = atoi(strRevision); int64_t nTime = atoi64(strTime); std::string strDataHex = request.params[4].get_str(); // CREATE A NEW COLLATERAL TRANSACTION FOR THIS SPECIFIC OBJECT CGovernanceObject govobj(hashParent, nRevision, nTime, uint256(), strDataHex); if(govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { CProposalValidator validator(strDataHex); if(!validator.Validate()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); } } if(govobj.GetObjectType() == GOVERNANCE_OBJECT_TRIGGER) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Trigger objects need not be prepared (however only masternodes can create them)"); } if(govobj.GetObjectType() == GOVERNANCE_OBJECT_WATCHDOG) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Watchdogs are deprecated"); } { LOCK(cs_main); std::string strError = ""; if (!govobj.IsValidLocally(strError, false)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Governance object is not valid - " + govobj.GetHash().ToString() + " - " + strError); } EnsureWalletIsUnlocked(); CWalletTx wtx; if(!pwalletMain->GetBudgetSystemCollateralTX(wtx, govobj.GetHash(), govobj.GetMinCollateralFee(), false)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Error making collateral transaction for governance object. Please check your wallet balance and make sure your wallet is unlocked."); } // -- make our change address CReserveKey reservekey(pwalletMain); // -- send the tx to the network CValidationState state; if (!pwalletMain->CommitTransaction(wtx, reservekey, g_connman.get(), state, NetMsgType::TX)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "CommitTransaction failed! Reason given: " + state.GetRejectReason()); } DBG( std::cout << "gobject: prepare " << " GetDataAsPlainString = " << govobj.GetDataAsPlainString() << ", hash = " << govobj.GetHash().GetHex() << ", txidFee = " << wtx.GetHash().GetHex() << std::endl; ); return wtx.GetHash().ToString(); }
void ReceiveCoinsDialog::generateRequest() { CReserveKey reservekey(pwalletMain, model->getActiveAccount(), KEYCHAIN_EXTERNAL); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey)) { return; } reservekey.KeepKey(); ui->receiveCoinsStackedWidget->setCurrentIndex(3); ui->accountRequestPaymentButtonComposite->setVisible(false); ui->accountBuyGuldenButton->setVisible(false); ui->accountSaveQRButtonComposite->setVisible(true); ui->accountCopyToClipboardButtonComposite->setVisible(true); ui->cancelButton->setVisible(false); ui->closeButton->setVisible(true); ui->cancelButtonGroup->setVisible(true); ui->generateRequestButton->setVisible(false); ui->generateAnotherRequestButton->setVisible(true); ui->accountBuyButton->setVisible(false); ui->accountCopyToClipboardButton->setText(tr("Copy request to clipboard")); CAmount amount = ui->requestAmount->value(); if (amount > 0) { ui->labelPaymentRequestHeading->setText(tr("Request %1 Gulden").arg(BitcoinUnits::format(BitcoinUnits::BTC, amount, false, BitcoinUnits::separatorStandard, 2))); } else { ui->labelPaymentRequestHeading->setText(tr("Request Gulden")); } QString args; QString label = QUrl::toPercentEncoding(ui->requestLabel->text()); QString strAmount; if (!label.isEmpty()) { label = "label=" + label; } if (amount > 0) { strAmount = "amount=" + QUrl::toPercentEncoding(BitcoinUnits::format(BitcoinUnits::BTC, amount, false, BitcoinUnits::separatorNever, -1)); while (strAmount.endsWith("0")) { strAmount.chop(1); } if (strAmount.endsWith(".")) { strAmount.chop(1); } } if (!strAmount.isEmpty() && !label.isEmpty()) { args = "?" + label + "&" + strAmount; } else if (!strAmount.isEmpty()) { args = "?" + strAmount; } else if (!label.isEmpty()) { args = "?" + label; } QString uri = QString("Gulden:") + QString::fromStdString(CBitcoinAddress(vchPubKey.GetID()).ToString()) + args; ui->labelPaymentRequest->setText(uri); if (!uri.isEmpty()) { if (uri.length() > MAX_URI_LENGTH) { ui->requestQRImage->setFixedWidth(900); ui->requestQRImage->setText(tr("Resulting URI too long, try to reduce the text for the label.")); } else { ui->requestQRImage->setText(""); ui->requestQRImage->setFixedWidth(ui->requestQRImage->height()); ui->requestQRImage->setCode(uri); } } pwalletMain->SetAddressBook(CBitcoinAddress(vchPubKey.GetID()).ToString(), ui->requestLabel->text().toStdString(), "receive"); }
WalletModel::SendCoinsReturn WalletModel::createRawTransaction( const QList<SendCoinsRecipient> &recipients, CTransaction& txNew, const CCoinControl *coinControl, bool isMultiSig) { qint64 total = 0; QSet<QString> setAddress; QString hex; if(recipients.empty()) { return OK; } // Pre-check input data for validity foreach(const SendCoinsRecipient &rcp, recipients) { if(!validateAddress(rcp.address)) { return InvalidAddress; } setAddress.insert(rcp.address); if(rcp.amount <= 0) { return InvalidAmount; } total += rcp.amount; } if(recipients.size() > setAddress.size()) { return DuplicateAddress; } int64 nBalance; if ( isMultiSig ) nBalance = getSharedBalance(coinControl); else nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } if((total + nTransactionFee) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); } { LOCK2(cs_main, wallet->cs_wallet); // Sendmany std::vector<std::pair<CScript, int64> > vecSend; foreach(const SendCoinsRecipient &rcp, recipients) { CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); } int64 nFeeRequired = 0; std::string strFailReason; CReserveKey reservekey(wallet); bool fCreated = wallet->CreateRawTransaction(vecSend, txNew, nFeeRequired, strFailReason, isMultiSig, reservekey, coinControl); if(!fCreated) { if((total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } /*if(!uiInterface.ThreadSafeAskFee(nFeeRequired)) { return Aborted; }*/ hex = QString::fromStdString(txNew.GetHash().GetHex()); }