QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { AssertLockHeld(cs_main); if (!CheckFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height()); else return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); } else { int nDepth = wtx.GetDepthInMainChain(); if (nDepth < 0) return tr("conflicted"); else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) return tr("%1/offline").arg(nDepth); else if (nDepth < 6) return tr("%1/unconfirmed").arg(nDepth); else return tr("%1 confirmations").arg(nDepth); } }
/* Return positive answer if transaction should be shown in list. */ bool TransactionRecord::showTransaction(const CWalletTx &wtx) { if (wtx.IsCoinBase()) { // Ensures we show generated coins / mined transactions at depth 1 /* if (!wtx.IsInMainChain()) { return false; } */ } 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)); CMutableTransaction blocktx; { LOCK(wallet->cs_wallet); blocktx = CMutableTransaction(*wallet->mapWallet.at(wtx.GetHash()).tx); } CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); LOCK(wallet->cs_wallet); auto it = wallet->mapWallet.find(wtx.GetHash()); BOOST_CHECK(it != wallet->mapWallet.end()); it->second.SetMerkleBranch(chainActive.Tip(), 1); return it->second; }
/* * Decompose CWallet transaction to model kernel records. */ vector<KernelRecord> KernelRecord::decomposeOutput(const CWallet *wallet, const CWalletTx &wtx) { vector<KernelRecord> parts; ::int64_t nTime = wtx.GetTxTime(); uint256 hash = wtx.GetHash(); std::map<std::string, std::string> mapValue = wtx.mapValue; ::int64_t nDayWeight = (min((GetAdjustedTime() - nTime), (::int64_t)(nStakeMaxAge+nStakeMinAge)) - nStakeMinAge); // DayWeight * 86400, чтобы был // правильный расчёт CoinAge if (showTransaction(wtx)) { for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { CTxOut txOut = wtx.vout[nOut]; if( wallet->IsMine(txOut) ) { CTxDestination address; std::string addrStr; ::uint64_t coinAge = max( (txOut.nValue * nDayWeight) / (COIN * 86400), (::int64_t)0); if (ExtractDestination(txOut.scriptPubKey, address)) { // Sent to Bitcoin Address addrStr = CBitcoinAddress(address).ToString(); } else { // Sent to IP, or other non-address transaction like OP_EVAL addrStr = mapValue["to"]; } parts.push_back(KernelRecord(hash, nTime, addrStr, txOut.nValue, wtx.IsSpent(nOut), coinAge)); } } } return parts; }
bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx) { if (pWallet->HasWalletSpend(wtx.GetHash())) { vErrors.push_back("Transaction has descendants in the wallet"); currentResult = BumpFeeResult::INVALID_PARAMETER; return false; } { LOCK(mempool.cs); auto it_mp = mempool.mapTx.find(wtx.GetHash()); if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) { vErrors.push_back("Transaction has descendants in the mempool"); currentResult = BumpFeeResult::INVALID_PARAMETER; return false; } } if (wtx.GetDepthInMainChain() != 0) { vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); currentResult = BumpFeeResult::WALLET_ERROR; return false; } return true; }
/* * Decompose CWallet transaction to model transaction records. */ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; int64 nTime = wtx.GetTxTime(); int64 nCredit = wtx.GetCredit(true); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(); std::map<std::string, std::string> mapValue = wtx.mapValue; if (wtx.IsCoinStake()) { // Stake generation parts.append(TransactionRecord(hash, nTime, TransactionRecord::StakeMint, "", -nDebit, wtx.GetValueOut())); } else if (nNet > 0 || wtx.IsCoinBase()) { // // Credit // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (wallet->IsMine(txout)) { TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; sub.address = CBitcoinAddress(address).ToString(); } else { // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } if (wtx.IsCoinBase()) { // Generated sub.type = TransactionRecord::Generated; } parts.append(sub); } } }
/* * Decompose CWallet transaction to model transaction records. */ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; int64_t nTime = wtx.GetTxTime(); CAmount nCredit = wtx.GetCredit(ISMINE_ALL); CAmount nDebit = wtx.GetDebit(ISMINE_ALL); CAmount nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(); std::map<std::string, std::string> mapValue = wtx.mapValue; if (nNet > 0 || wtx.IsCoinBase()) { // // Credit // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { isminetype mine = wallet->IsMine(txout); if(mine) { TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { // Received by Moneta Address sub.type = TransactionRecord::RecvWithAddress; sub.address = CMonetaAddress(address).ToString(); } else { // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } if (wtx.IsCoinBase()) { // Generated sub.type = TransactionRecord::Generated; } parts.append(sub); } } }
QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { int nDepth = GetDepthInMainChain(wtx.GetTxID()); if ( (int64_t)wtx.nLockHeight > chainActive.Height() + 1) { return tr("Open for %n more block(s)", "", wtx.nLockHeight - chainActive.Height()); } else if( (int64_t)(wtx.nLockHeight + MIN_HISTORY) < chainActive.Height() && nDepth == -1){ return tr("Unknown"); } else { if (nDepth < 0) return tr("conflicted"); else if (nDepth == 0) return tr("unconfirmed"); else if (nDepth < 6) return tr("%1/confirming").arg(nDepth); else return tr("%1 confirmations").arg(nDepth); } }
WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients, const CCoinControl *coinControl) { qint64 total = 0; QSet<QString> setAddress; QString hex; std::string stxData; 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(!rcp.from.toStdString().empty()) { if (rcp.from.indexOf("@FROM=") != -1) { return InvalidMessage; } stxData = "@FROM=" + rcp.from.toStdString(); } if(!rcp.subject.toStdString().empty()) { if (rcp.subject.indexOf("@SUBJ=") != -1) { return InvalidMessage; } stxData += "@SUBJ=" + rcp.subject.toStdString(); } if(!rcp.message.toStdString().empty()) { if (rcp.message.indexOf("@MSG=") != -1) { return InvalidMessage; } stxData += "@MSG=" + rcp.message.toStdString(); } //printf("WalletModel::sendCoins: stxData [%s]\n", stxData.c_str()); //if (total < COIN) { return AmountExceedsBalance; } //if (!stxData.empty() && total < COIN) { return InvalidMessageAmount; } } if(recipients.size() > setAddress.size()) { return DuplicateAddress; } int64_t nBalance = 0; std::vector<COutput> vCoins; wallet->AvailableCoins(vCoins, true, coinControl); BOOST_FOREACH(const COutput& out, vCoins) nBalance += out.tx->vout[out.i].nValue; 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_t> > vecSend; foreach(const SendCoinsRecipient &rcp, recipients) { CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); } CWalletTx wtx; CReserveKey keyChange(wallet); int64_t nFeeRequired = 0; bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, stxData, coinControl); if(!fCreated) { if((total + nFeeRequired) > nBalance) // FIXME: could cause collisions in the future { return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); } return TransactionCreationFailed; } if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) { return Aborted; } if(!wallet->CommitTransaction(wtx, keyChange)) { return TransactionCommitFailed; } hex = QString::fromStdString(wtx.GetHash().GetHex()); }
QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit) { QString strHTML; LOCK2(cs_main, wallet->cs_wallet); strHTML.reserve(4000); strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; int64_t nTime = wtx.GetTxTime(); CAmount nCredit = wtx.GetCredit(ISMINE_ALL); CAmount nDebit = wtx.GetDebit(ISMINE_ALL); CAmount nNet = nCredit - nDebit; strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); int nRequests = wtx.GetRequestCount(); if (nRequests != -1) { if (nRequests == 0) strHTML += tr(", has not been successfully broadcast yet"); else if (nRequests > 0) strHTML += tr(", broadcast through %n node(s)", "", nRequests); } strHTML += "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; // // From // if (wtx.IsCoinBase()) { strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>"; } else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) { // Online transaction strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>"; } else { // Offline transaction if (nNet > 0) { // Credit if (CBeCoinAddress(rec->address).IsValid()) { CTxDestination address = CBeCoinAddress(rec->address).Get(); if (wallet->mapAddressBook.count(address)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> "; strHTML += GUIUtil::HtmlEscape(rec->address); QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only"); if (!wallet->mapAddressBook[address].name.empty()) strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; else strHTML += " (" + addressOwned + ")"; strHTML += "<br>"; } } } } // // To // if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) { // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "<b>" + tr("To") + ":</b> "; CTxDestination dest = CBeCoinAddress(strAddress).Get(); if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; } // // Amount // if (wtx.IsCoinBase() && nCredit == 0) { // // Coinbase // CAmount nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); strHTML += "<b>" + tr("Credit") + ":</b> "; if (wtx.IsInMainChain()) strHTML += BeCoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; else strHTML += "(" + tr("not accepted") + ")"; strHTML += "<br>"; }
bool CWalletDB::WriteTx(const CWalletTx& wtx) { nWalletDBUpdated++; return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction& transaction, const CCoinControl* coinControl) { CAmount total = 0; bool fSubtractFeeFromAmount = false; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<CRecipient> vecSend; if (recipients.empty()) { return OK; } if (isAnonymizeOnlyUnlocked()) { return AnonymizeOnlyUnlocked; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity foreach (const SendCoinsRecipient& rcp, recipients) { if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true; if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... CAmount subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr + out.script().size()); CAmount nAmount = out.amount(); CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered ion address / amount: if (!validateAddress(rcp.address)) { return InvalidAddress; } if (rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString())); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); total += rcp.amount; } } if (setAddress.size() != nAddresses) { return DuplicateAddress; } CAmount nBalance = getBalance(coinControl); if (total > nBalance) { return AmountExceedsBalance; } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; CWalletTx* newTx = transaction.getTransaction(); CReserveKey* keyChange = transaction.getPossibleKeyChange(); if (recipients[0].useSwiftTX && total > GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE) * COIN) { emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 ION.").arg(GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl, recipients[0].inputType, recipients[0].useSwiftTX); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && fCreated) transaction.reassignAmounts(nChangePosRet); if (recipients[0].useSwiftTX && newTx->GetValueOut() > GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE) * COIN) { emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 ION.").arg(GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } if (!fCreated) { if (!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } // reject insane fee if (nFeeRequired > ::minRelayTxFee.GetFee(transaction.getTransactionSize()) * 10000) return InsaneFee; } return SendCoinsReturn(OK); }
Value listsinceblock(const Array& params, bool fHelp) { if (fHelp) throw runtime_error( "listsinceblock [blockhash] [target-confirmations]\n" "Get all transactions in blocks since block [blockhash], or all transactions if omitted"); CBlockIndex *pindex = NULL; int target_confirms = 1; if (params.size() > 0) { uint256 blockId = 0; blockId.SetHex(params[0].get_str()); pindex = CBlockLocator(blockId).GetBlockIndex(); } if (params.size() > 1) { target_confirms = params[1].get_int(); if (target_confirms < 1) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; Array transactions; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) { CWalletTx tx = (*it).second; if (depth == -1 || tx.GetDepthInMainChain() < depth) ListTransactions(tx, "*", 0, true, transactions); } uint256 lastblock; if (target_confirms == 1) { lastblock = hashBestChain; } else { int target_height = pindexBest->nHeight + 1 - target_confirms; CBlockIndex *block; for (block = pindexBest; block && block->nHeight > target_height; block = block->pprev) { } lastblock = block ? block->GetBlockHash() : 0; } Object ret; ret.push_back(Pair("transactions", transactions)); ret.push_back(Pair("lastblock", lastblock.GetHex())); return ret; }
WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients, const CCoinControl *coinControl) { 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_t nBalance = 0; std::vector<COutput> vCoins; wallet->AvailableCoins(vCoins, true, coinControl); BOOST_FOREACH(const COutput& out, vCoins) nBalance += out.tx->vout[out.i].nValue; 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_t> > vecSend; std::string sNarr; foreach(const SendCoinsRecipient &rcp, recipients) { CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); sNarr = rcp.reference.toStdString(); if (sNarr.length() > 0) { if (sNarr.length() > 50) { printf("Reference is too long.\n"); return ReferenceTooLong; }; std::vector<uint8_t> vNarr(sNarr.c_str(), sNarr.c_str() + sNarr.length()); CScript scriptN = CScript() << OP_RETURN << vNarr; vecSend.push_back(make_pair(scriptN, 0)); } } CWalletTx wtx; if (sNarr.length() > 0) { wtx.mapValue["reference"] = sNarr; } CReserveKey keyChange(wallet); int64_t nFeeRequired = 0; bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, coinControl); if(!fCreated) { if((total + nFeeRequired) > nBalance) // FIXME: could cause collisions in the future { return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); } return TransactionCreationFailed; } if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) { return Aborted; } if(!wallet->CommitTransaction(wtx, keyChange)) { return TransactionCommitFailed; } hex = QString::fromStdString(wtx.GetHash().GetHex()); }
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 TransactionRecord::updateStatus(const CWalletTx &wtx) { // Determine transaction status // Find the block the tx is in CBlockIndex* pindex = NULL; std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock); if (mi != mapBlockIndex.end()) pindex = (*mi).second; // Sort order, unrecorded transactions sort to the top status.sortKey = strprintf("%010d-%01d-%010u-%03d", (pindex ? pindex->nHeight : std::numeric_limits<int>::max()), (wtx.IsCoinBase() ? 1 : 0), wtx.nTimeReceived, idx); status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0); status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = nBestHeight; if (!wtx.IsFinal()) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) { status.status = TransactionStatus::OpenUntilBlock; status.open_for = nBestHeight - wtx.nLockTime; } else { status.status = TransactionStatus::OpenUntilDate; status.open_for = wtx.nLockTime; } } // For generated transactions, determine maturity else if(type == TransactionRecord::Generated) { if (wtx.GetBlocksToMaturity() > 0) { status.status = TransactionStatus::Immature; if (wtx.IsInMainChain()) { status.matures_in = wtx.GetBlocksToMaturity(); // Check if the block was requested by anyone if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) status.status = TransactionStatus::MaturesWarning; } else { status.status = TransactionStatus::NotAccepted; } } else { status.status = TransactionStatus::Confirmed; } } else { if (status.depth < 0) { status.status = TransactionStatus::Conflicted; } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { status.status = TransactionStatus::Offline; } else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; } else if (status.depth < RecommendedNumConfirmations) { status.status = TransactionStatus::Confirming; } else { status.status = TransactionStatus::Confirmed; } } }
/* peggytx '{"BitcoinDark": "A revolution in cryptocurrency"}' '{"RWoDDki8gfqYMHDEzsyFdsCtdSkB79DbVc":1}' false */ Value peggytx(const Array& params, bool fHelp) { if (fHelp || params.size() < 2) throw runtime_error( "peggytx\n" "Creates a peggy transaction: \n" "'<json string>' '{\"<btcd addr>\" : <amount>}' [send?] \n" "!WARNING!: adding true as an option will attempt to automatically send coins from your wallet." "You will not be able to get them back until you redeem an equivalent number of BTCD." ); std::string retVal(""); const std::string peggyJson = params[0].get_str(); const Object& sendTo = params[1].get_obj(); bool signAndSend = false; if (params.size() > 2) signAndSend = params[2].get_bool(); const Pair& out = sendTo[0]; CBitcoinAddress returnAddr = CBitcoinAddress(out.name_); if (!returnAddr.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid BitcoinDark address: ")+out.name_); int64_t amountLocked = AmountFromValue(out.value_); std::string hex = HexStr(peggyJson.begin(), peggyJson.end(), false); //Construct a peggy locking hexstr from the json and the redeem address/lock amount. char *peggytx = peggy_tx((char*)peggyJson.c_str()); int i; CWallet wallet; CWalletTx wtx; CScript scriptPubKey = CScript(); unsigned char buf[4096]; char test[100]; strcpy(test, (char*)hex.c_str()); if(strlen(test) > 0) decode_hex(buf,(int)strlen(test),test); fprintf(stderr, "peggytx=%s\n", peggytx); scriptPubKey << OP_RETURN; scriptPubKey << ParseHex(peggytx); //for(i=0;i<strlen((const char*)buf);i++) // scriptPubKey << test[i]; //scriptPubKey << ParseHex(hex); //scriptPubKey.SetDestination(returnAddr.Get()); CReserveKey reservekey(pwalletMain); int64_t nFeeRequired; if(!pwalletMain->CreateTransaction(scriptPubKey, amountLocked, wtx, reservekey, nFeeRequired)) return std::string("Failed to Create the Transaction. Is your wallet unlocked?\n"); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << wtx; string strHex = HexStr(ssTx.begin(), ssTx.end()); cJSON *obj = cJSON_CreateObject(); jaddstr(obj, "txid", (char*)wtx.GetHash().ToString().c_str()); jaddstr(obj, "rawtx", (char*)strHex.c_str()); jaddstr(obj, "opreturnstr", (char*)HexStr(scriptPubKey.begin(), scriptPubKey.end(), false).c_str()); free(peggytx); if(signAndSend){ if(!pwalletMain->CommitTransaction(wtx, reservekey)) return std::string("The transaction was Rejected\n"); else{ return jprint(obj, 1); } } else{ return jprint(obj, 1); } /* CTransaction peggy; char *paymentScript= "{\"RWoDDki8gfqYMHDEzsyFdsCtdSkB79DbVc\":10000000}"; // temp. char *priceFeedHash = "5f43ac64"; if(wallet.CreatePeggyBase(peggy, paymentScript, priceFeedHash)) { peggy.nTime = 0; } Object o; TxToJSON(peggy, 0, o); return o;*/ }
WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients, const CCoinControl *coinControl) { 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_t nBalance = 0; std::vector<COutput> vCoins; wallet->AvailableCoins(vCoins, true, coinControl); BOOST_FOREACH(const COutput& out, vCoins) nBalance += out.tx->vout[out.i].nValue; if(total > nBalance) { return AmountExceedsBalance; } if((total + nTransactionFee) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); } std::string txid = ""; std::string messages = ""; std::string hashBoinc = ""; { LOCK2(cs_main, wallet->cs_wallet); // Sendmany std::vector<std::pair<CScript, int64_t> > vecSend; bool coinTracking = false; foreach(const SendCoinsRecipient &rcp, recipients) { CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); if (rcp.CoinTracking) coinTracking=true; messages += "<MESSAGE>" + AdvancedCrypt(FromQStringW(rcp.Message)) + "</MESSAGE>"; } CWalletTx wtx; CReserveKey keyChange(wallet); int64_t nFeeRequired = 0; if (coinTracking) { printf("Creating tracked tx : old hashboinc %s",wtx.hashBoinc.c_str()); wtx.hashBoinc = "<TRACK>" + wtx.GetHash().ToString() + "</TRACK>"; //Run time code execution feature - 12-7-2014 std::string q = "\""; std::string code = "MsgBox(" + q + "Hello!" + q + ",MsgBoxStyle.Critical," + q + "Message Title" + q + ")\r\n"; wtx.hashBoinc += "<CODE>" + code + "</CODE>"; } if (!msAttachmentGuid.empty()) { printf("Adding attachment to tx %s",wtx.hashBoinc.c_str()); wtx.hashBoinc += "<ATTACHMENT><TXID>" + wtx.GetHash().ToString() + "</TXID><ATTACHMENTGUID>" + msAttachmentGuid + "</ATTACHMENTGUID></ATTACHMENT>"; } wtx.hashBoinc += messages; bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, coinControl); if(!fCreated) { if((total + nFeeRequired) > nBalance) // FIXME: could cause collisions in the future { return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); } return TransactionCreationFailed; } if (coinTracking) { printf("Tracking hashBoinc %s",wtx.hashBoinc.c_str()); } std::string samt = FormatMoney(wtx.vout[0].nValue); double dblAmt = dblFromAmount(wtx.vout[0].nValue); if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) { return Aborted; } if(!wallet->CommitTransaction(wtx, keyChange)) { return TransactionCommitFailed; } hex = QString::fromStdString(wtx.GetHash().GetHex()); txid = wtx.GetHash().GetHex(); hashBoinc = wtx.hashBoinc; }
QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { QString strHTML; QString explorer(fTestNet ? "http://explorer.butterflycoin.info/" : "http://explorer.butterflycoin.info/"); LOCK2(cs_main, wallet->cs_wallet); strHTML.reserve(4000); strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; int64_t nTime = wtx.GetTxTime(); int64_t nCredit = wtx.GetCredit(); int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); int nRequests = wtx.GetRequestCount(); if (nRequests != -1) { if (nRequests == 0) strHTML += tr(", has not been successfully broadcast yet"); else if (nRequests > 0) strHTML += tr(", broadcast through %n node(s)", "", nRequests); }; strHTML += "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; // // From // if (wtx.IsCoinBase() || wtx.IsCoinStake()) { strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>"; } else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) { // Online transaction strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>"; } else { // Offline transaction if (nNet > 0) { // Credit BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (wtx.nVersion == ANON_TXN_VERSION && txout.IsAnonOutput()) { const CScript &s = txout.scriptPubKey; CKeyID ckidD = CPubKey(&s[2+1], 33).GetID(); std::string sAnonPrefix("ao "); if (wallet->HaveKey(ckidD) && (wallet->mapAddressBook[ckidD].empty() || !wallet->mapAddressBook[ckidD].compare(0, sAnonPrefix.length(), sAnonPrefix) == 0)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> <a href='"+explorer+"address.asp?address="; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(ckidD).ToString())+"' target='_blank'>"; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(ckidD).ToString()); if (!wallet->mapAddressBook[ckidD].empty()) strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[ckidD]) + ")"; else strHTML += " (" + tr("own address") + ")"; strHTML += "</a><br>"; }; continue; } if (wallet->IsMine(txout)) { CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { if (wallet->mapAddressBook.count(address)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> <a href='"+explorer+"address.asp?address="; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString())+"' target='_blank'>"; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); if (!wallet->mapAddressBook[address].empty()) strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + ")"; else strHTML += " (" + tr("own address") + ")"; strHTML += "</a><br>"; }; }; break; }; }; }; };
/* * Decompose CWallet transaction to model transaction records. */ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; int64_t nTime = wtx.GetTxTime(); CAmount nCredit = wtx.GetCredit(ISMINE_ALL); CAmount nDebit = wtx.GetDebit(ISMINE_ALL); CAmount nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(), hashPrev = 0; std::map<std::string, std::string> mapValue = wtx.mapValue; if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake()) { // // Credit // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { isminetype mine = wallet->IsMine(txout); if(mine) { TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; sub.address = CTurbogoldAddress(address).ToString(); } else { // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } if (wtx.IsCoinBase()) { // Generated (proof-of-work) sub.type = TransactionRecord::Generated; } if (wtx.IsCoinStake()) { // Generated (proof-of-stake) if (hashPrev == hash) continue; // last coinstake output CAmount nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (IsMine(*wallet,txout.scriptPubKey)) nValueOut += txout.nValue; if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); } sub.type = TransactionRecord::Generated; sub.credit = nNet > 0 ? nNet : nValueOut - nDebit; hashPrev = hash; } parts.append(sub); } } }
/* * Decompose CWallet transaction to model transaction records. */ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; int64_t nTime = wtx.GetTxTime(); int64_t nCredit = wtx.GetCredit(true); int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(), hashPrev = 0; std::map<std::string, std::string> mapValue = wtx.mapValue; if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake()) { // // Credit - Calculate Net from CryptoLottery Rob Halford - 4-3-2015-1 // for (auto const& txout : wtx.vout) { if(wallet->IsMine(txout)) { TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; sub.address = CBitcoinAddress(address).ToString(); } else { // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } if (wtx.IsCoinBase()) { // Generated (proof-of-work) sub.type = TransactionRecord::Generated; } if (wtx.IsCoinStake()) { // Generated (proof-of-stake) if (hashPrev == hash) continue; // last coinstake output if (wtx.vout.size()==2) { //Standard POR CoinStake sub.type = TransactionRecord::Generated; sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit; hashPrev = hash; } else { //CryptoLottery - CoinStake - 4-3-2015 sub.type = TransactionRecord::Generated; if (nDebit == 0) { sub.credit = GetMyValueOut(wallet,wtx); sub.RemoteFlag = 1; } else { sub.credit = nNet > 0 ? nNet : GetMyValueOut(wallet,wtx) - nDebit; } hashPrev = hash; } } parts.append(sub); } } } else { bool fAllFromMe = true; for (auto const& txin : wtx.vin) fAllFromMe = fAllFromMe && wallet->IsMine(txin); bool fAllToMe = true; for (auto const& txout : wtx.vout) fAllToMe = fAllToMe && wallet->IsMine(txout); if (fAllFromMe && fAllToMe) { // Payment to self int64_t nChange = wtx.GetChange(); parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", -(nDebit - nChange), nCredit - nChange)); } else if (fAllFromMe) { // // Debit // int64_t nTxFee = nDebit - wtx.GetValueOut(); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { const CTxOut& txout = wtx.vout[nOut]; TransactionRecord sub(hash, nTime); sub.idx = parts.size(); if(wallet->IsMine(txout)) { // Ignore parts sent to self, as this is usually the change // from a transaction sent back to our own address. continue; } CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address)) { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; sub.address = CBitcoinAddress(address).ToString(); } else { // Sent to IP, or other non-address transaction like OP_EVAL sub.type = TransactionRecord::SendToOther; sub.address = mapValue["to"]; } int64_t nValue = txout.nValue; /* Add fee to first output */ if (nTxFee > 0) { nValue += nTxFee; nTxFee = 0; } sub.debit = -nValue; parts.append(sub); } } else { // // Mixed debit transaction, can't break down payees // parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); } } return parts; }
/* * Decompose CWallet transaction to model transaction records. */ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; int64 nTime = wtx.GetTxTime(); int64 nCredit = wtx.GetCredit(true); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(); std::map<std::string, std::string> mapValue = wtx.mapValue; const bool combineOutputs = (wtx.cUnit == 'S'); if (wtx.IsCoinStake()) // ppcoin: coinstake transaction { parts.append(TransactionRecord(hash, nTime, TransactionRecord::StakeMint, "", -nDebit, wtx.GetValueOut())); } else if (nNet > 0 || wtx.IsCoinBase()) { // // Credit // QMap<CScript, TransactionRecord*> outputParts; BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if(wallet->IsMine(txout)) { TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { if (wtx.IsUnpark()) sub.type = TransactionRecord::Unpark; else // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; sub.address = CBitcoinAddress(address, wtx.cUnit).ToString(); } else { // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } if (wtx.IsCoinBase()) { // Generated sub.type = TransactionRecord::Generated; } if (combineOutputs) { QMap<CScript, TransactionRecord*>::const_iterator it = outputParts.find(txout.scriptPubKey); if (it != outputParts.end()) { TransactionRecord& previous = *it.value(); previous.credit += sub.credit; continue; } } parts.append(sub); if (combineOutputs) outputParts[txout.scriptPubKey] = &parts.back(); } } }
WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients) { 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; } if(total > getBalance()) { return AmountExceedsBalance; } if((total + nTransactionFee) > getBalance()) { 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(CAltcoinAddress(rcp.address.toStdString()).Get()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); } CWalletTx wtx; CReserveKey keyChange(wallet); int64 nFeeRequired = 0; bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); if(!fCreated) { if((total + nFeeRequired) > wallet->GetBalance()) { return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); } return TransactionCreationFailed; } if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) { return Aborted; } if(!wallet->CommitTransaction(wtx, keyChange)) { return TransactionCommitFailed; } hex = QString::fromStdString(wtx.GetHash().GetHex()); }
/* * Decompose CWallet transaction to model transaction records. */ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; int64_t nTime = wtx.GetTxTime(); int64_t nCredMOIN, nCredMoinX; wtx.GetCredit(nCredMOIN, nCredMoinX, true); int64_t nCredit = nCredMOIN + nCredMoinX; int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(), hashPrev = 0; std::map<std::string, std::string> mapValue = wtx.mapValue; char cbuf[256]; if (wtx.nVersion == ANON_TXN_VERSION) { if (nNet > 0 && nCredMoinX > 0) { // -- credit TransactionRecord sub(hash, nTime, TransactionRecord::RecvMoinX, "", "", nNet, 0); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { // display 1st transaction snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) { sub.narration = mi->second; break; }; }; parts.append(sub); return parts; } else if (nNet <= 0) { // -- debit TransactionRecord sub(hash, nTime, TransactionRecord::SendMoinX, "", "", nNet, 0); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { // display 1st transaction snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) { sub.narration = mi->second; break; } }; parts.append(sub); return parts; }; // continue on }; if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake()) { // // Credit // for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { const CTxOut& txout = wtx.vout[nOut]; if (wtx.nVersion == ANON_TXN_VERSION && txout.IsAnonOutput()) { const CScript &s = txout.scriptPubKey; CKeyID ckidD = CPubKey(&s[2+1], 33).GetID(); if (wallet->HaveKey(ckidD)) { TransactionRecord sub(hash, nTime); sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; sub.type = TransactionRecord::RecvMoinX; sub.address = CBitcoinAddress(ckidD).ToString(); //sub.address = wallet->mapAddressBook[ckidD] snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) sub.narration = mi->second; parts.append(sub); }; }; if (wallet->IsMine(txout)) { TransactionRecord sub(hash, nTime); sub.idx = parts.size(); // sequence number CTxDestination address; sub.credit = txout.nValue; if (ExtractDestination(txout.scriptPubKey, address) && IsDestMine(*wallet, address)) { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; sub.address = CBitcoinAddress(address).ToString(); } else { // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) sub.narration = mi->second; if (wtx.IsCoinBase()) { // Generated (proof-of-work) sub.type = TransactionRecord::Generated; }; if (wtx.IsCoinStake()) { // Generated (proof-of-stake) if (hashPrev == hash) continue; // last coinstake output sub.type = TransactionRecord::Generated; sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit; hashPrev = hash; }; parts.append(sub); } } } else { bool fAllFromMe = true; BOOST_FOREACH(const CTxIn& txin, wtx.vin) { if (wtx.nVersion == ANON_TXN_VERSION && txin.IsAnonInput()) { std::vector<uint8_t> vchImage; txin.ExtractKeyImage(vchImage); CWalletDB walletdb(wallet->strWalletFile, "r"); COwnedAnonOutput oao; if (!walletdb.ReadOwnedAnonOutput(vchImage, oao)) { fAllFromMe = false; break; // display as send/recv moinx }; continue; }; if (wallet->IsMine(txin)) continue; fAllFromMe = false; break; }; bool fAllToMe = true; BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (wtx.nVersion == ANON_TXN_VERSION && txout.IsAnonOutput()) { fAllToMe = false; break; // display as send/recv moinx } opcodetype firstOpCode; CScript::const_iterator pc = txout.scriptPubKey.begin(); if (txout.scriptPubKey.GetOp(pc, firstOpCode) && firstOpCode == OP_RETURN) continue; if (wallet->IsMine(txout)) continue; fAllToMe = false; break; }; if (fAllFromMe && fAllToMe) { // Payment to self int64_t nChange = wtx.GetChange(); std::string narration(""); mapValue_t::const_iterator mi; for (mi = wtx.mapValue.begin(); mi != wtx.mapValue.end(); ++mi) { if (mi->first.compare(0, 2, "n_") != 0) continue; narration = mi->second; break; }; parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", narration, -(nDebit - nChange), nCredit - nChange)); } else if (fAllFromMe) { // // Debit // int64_t nTxFee = nDebit - wtx.GetValueOut(); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { const CTxOut& txout = wtx.vout[nOut]; TransactionRecord sub(hash, nTime); sub.idx = parts.size(); if (wtx.nVersion == ANON_TXN_VERSION && txout.IsAnonOutput()) { const CScript &s = txout.scriptPubKey; CKeyID ckidD = CPubKey(&s[2+1], 33).GetID(); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; sub.type = TransactionRecord::SendMoinX; sub.address = CBitcoinAddress(ckidD).ToString(); } else { opcodetype firstOpCode; CScript::const_iterator pc = txout.scriptPubKey.begin(); if (txout.scriptPubKey.GetOp(pc, firstOpCode) && firstOpCode == OP_RETURN) continue; if (wallet->IsMine(txout)) { // Ignore parts sent to self, as this is usually the change // from a transaction sent back to our own address. continue; } CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address)) { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; sub.address = CBitcoinAddress(address).ToString(); } else { // Sent to IP, or other non-address transaction like OP_EVAL sub.type = TransactionRecord::SendToOther; sub.address = mapValue["to"]; } }; snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) sub.narration = mi->second; int64_t nValue = txout.nValue; /* Add fee to first output */ if (nTxFee > 0) { nValue += nTxFee; nTxFee = 0; } sub.debit = -nValue; parts.append(sub); } } else { // // Mixed debit transaction, can't break down payees // TransactionRecord sub(hash, nTime, TransactionRecord::Other, "", "", nNet, 0); /* for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { // display 1st transaction snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) sub.narration = mi->second; break; }; */ parts.append(sub); } } return parts; }
/* * Decompose CWallet transaction to model transaction records. */ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; int64_t nTime = wtx.GetTxTime(); int64_t nCredit = wtx.GetCredit(true); int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(), hashPrev = 0; std::map<std::string, std::string> mapValue = wtx.mapValue; char cbuf[256]; if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake()) { // // Credit // for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { const CTxOut& txout = wtx.vout[nOut]; if(wallet->IsMine(txout)) { TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { // Received by CannabisDarkcoin Address sub.type = TransactionRecord::RecvWithAddress; sub.address = CCannabisDarkcoinAddress(address).ToString(); } else { // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) sub.narration = mi->second; if (wtx.IsCoinBase()) { // Generated (proof-of-work) sub.type = TransactionRecord::Generated; } if (wtx.IsCoinStake()) { // Generated (proof-of-stake) if (hashPrev == hash) continue; // last coinstake output sub.type = TransactionRecord::Generated; sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit; hashPrev = hash; } parts.append(sub); } } } else { bool fAllFromMe = true; BOOST_FOREACH(const CTxIn& txin, wtx.vin) fAllFromMe = fAllFromMe && wallet->IsMine(txin); bool fAllToMe = true; BOOST_FOREACH(const CTxOut& txout, wtx.vout) fAllToMe = fAllToMe && wallet->IsMine(txout); if (fAllFromMe && fAllToMe) { // Payment to self int64_t nChange = wtx.GetChange(); std::string narration(""); mapValue_t::const_iterator mi; for (mi = wtx.mapValue.begin(); mi != wtx.mapValue.end(); ++mi) { if (mi->first.compare(0, 2, "n_") != 0) continue; narration = mi->second; break; }; parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", narration, -(nDebit - nChange), nCredit - nChange)); } else if (fAllFromMe) { // // Debit // int64_t nTxFee = nDebit - wtx.GetValueOut(); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { const CTxOut& txout = wtx.vout[nOut]; TransactionRecord sub(hash, nTime); sub.idx = parts.size(); if(wallet->IsMine(txout)) { // Ignore parts sent to self, as this is usually the change // from a transaction sent back to our own address. continue; } CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address)) { // Sent to CannabisDarkcoin Address sub.type = TransactionRecord::SendToAddress; sub.address = CCannabisDarkcoinAddress(address).ToString(); } else { // Sent to IP, or other non-address transaction like OP_EVAL sub.type = TransactionRecord::SendToOther; sub.address = mapValue["to"]; } snprintf(cbuf, sizeof(cbuf), "n_%u", nOut); mapValue_t::const_iterator mi = wtx.mapValue.find(cbuf); if (mi != wtx.mapValue.end() && !mi->second.empty()) sub.narration = mi->second; int64_t nValue = txout.nValue; /* Add fee to first output */ if (nTxFee > 0) { nValue += nTxFee; nTxFee = 0; } sub.debit = -nValue; parts.append(sub); } } else { // // Mixed debit transaction, can't break down payees // parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", "", nNet, 0)); } } return parts; }
QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int unit) { QString strHTML; LOCK2(cs_main, wallet->cs_wallet); strHTML.reserve(4000); strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; int64_t nTime = wtx.GetTxTime(); int64_t nCredit = wtx.GetCredit(); int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); int nRequests = wtx.GetRequestCount(); if (nRequests != -1) { if (nRequests == 0) strHTML += tr(", has not been successfully broadcast yet"); else if (nRequests > 0) strHTML += tr(", broadcast through %n node(s)", "", nRequests); } strHTML += "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; // // From // if (wtx.IsCoinBase()) { strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>"; } else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) { // Online transaction strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>"; } else { // Offline transaction if (nNet > 0) { // Credit BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (wallet->IsMine(txout)) { CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { if (wallet->mapAddressBook.count(address)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> "; strHTML += GUIUtil::HtmlEscape(CAricoinAddress(address).ToString()); if (!wallet->mapAddressBook[address].name.empty()) strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; else strHTML += " (" + tr("own address") + ")"; strHTML += "<br>"; } } break; } } } }
QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { QString strHTML; { LOCK(wallet->cs_wallet); strHTML.reserve(4000); strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; int64 nTime = wtx.GetTxTime(); int64 nCredit = wtx.GetCredit(); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); int nRequests = wtx.GetRequestCount(); if (nRequests != -1) { if (nRequests == 0) strHTML += tr(", has not been successfully broadcast yet"); else if (nRequests > 0) strHTML += tr(", broadcast through %n node(s)", "", nRequests); } strHTML += "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; // // From // if (wtx.IsCoinBase() || wtx.IsCoinStake()) { strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>"; } else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) { // Online transaction strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>"; } else { // Offline transaction if (nNet > 0) { // Credit BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (wallet->IsMine(txout)) { CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { if (wallet->mapAddressBook.count(address)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> "; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); if (!wallet->mapAddressBook[address].empty()) strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + ")"; else strHTML += " (" + tr("own address") + ")"; strHTML += "<br>"; } } break; } } } } // // To // if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) { // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "<b>" + tr("To") + ":</b> "; CTxDestination dest = CBitcoinAddress(strAddress).Get(); if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest]) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; } // // Amount // if (wtx.IsCoinBase() && nCredit == 0) { // // Coinbase // int64 nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) nUnmatured += wallet->GetCredit(txout); strHTML += "<b>" + tr("Credit") + ":</b> "; if (wtx.IsInMainChain()) strHTML += BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; else strHTML += "(" + tr("not accepted") + ")"; strHTML += "<br>"; }
WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients, const CCoinControl *coinControl) { 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; } // we do not use getBalance() here, because some coins could be locked or coin control could be active int64 nBalance = 0; std::vector<COutput> vCoins; wallet->AvailableCoins(vCoins, true, coinControl); BOOST_FOREACH(const COutput& out, vCoins) nBalance += out.tx->vout[out.i].nValue; 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)); } CWalletTx wtx; CReserveKey keyChange(wallet); int64 nFeeRequired = 0; bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, coinControl); if(!fCreated) { if((total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); } return TransactionCreationFailed; } if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) { return Aborted; } if(!wallet->CommitTransaction(wtx, keyChange)) { return TransactionCommitFailed; } hex = QString::fromStdString(wtx.GetHash().GetHex()); }
QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int unit) { QString strHTML; { LOCK(wallet->cs_wallet); strHTML.reserve(4000); strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; int64_t nTime = wtx.GetTxTime(); int64_t nCredit = wtx.GetCredit(); int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx) + "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; // // From // if (wtx.IsCoinBase()) { strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>"; } else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) { // Online transaction strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>"; } else { // Offline transaction if (nNet > 0) { // Credit BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (wallet->IsMine(txout)) { CTxDestination address = CKeyID(txout.pubKey); if (wallet->IsMine(txout.pubKey)) { if (wallet->mapAddressBook.count(address)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> "; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); if (!wallet->mapAddressBook[address].name.empty()) strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; else strHTML += " (" + tr("own address") + ")"; strHTML += "<br>"; } } break; } } } } // // To // if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) { // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "<b>" + tr("To") + ":</b> "; CTxDestination dest = CBitcoinAddress(strAddress).Get(); if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; } // // Amount // if (nNet > 0) { // // Credit // strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet) + "<br>"; } else { bool fAllFromMe = true; BOOST_FOREACH(const CTxIn& txin, wtx.vin) fAllFromMe = fAllFromMe && wallet->IsMine(txin); bool fAllToMe = true; BOOST_FOREACH(const CTxOut& txout, wtx.vout) fAllToMe = fAllToMe && wallet->IsMine(txout); if (fAllFromMe) { // // Debit // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (wallet->IsMine(txout)) continue; if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) { // Offline transaction CTxDestination address = CKeyID(txout.pubKey); strHTML += "<b>" + tr("To") + ":</b> "; if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); strHTML += "<br>"; } strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>"; } if (fAllToMe) { // Payment to self strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nDebit) + "<br>"; strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nCredit) + "<br>"; } uint64_t nTxFee = nDebit - wtx.GetValueOut(); if (nTxFee > 0) strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nTxFee) + "<br>"; } else { // // Mixed debit transaction // BOOST_FOREACH(const CTxIn& txin, wtx.vin) if (wallet->IsMine(txin)) strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetDebit(txin)) + "<br>"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (wallet->IsMine(txout)) strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>"; } }
QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { AssertLockHeld(cs_main); if (!IsFinalTx(wtx, nBestHeight + 1)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight); else return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); } else { int signatures = wtx.GetTransactionLockSignatures(); QString strUsingIX = ""; if(signatures >= 0){ if(signatures >= INSTANTX_SIGNATURES_REQUIRED){ int nDepth = wtx.GetDepthInMainChain(); if (nDepth < 0) return tr("conflicted"); else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) return tr("%1/offline (verified via instantx)").arg(nDepth); else if (nDepth < 10) return tr("%1/confirmed (verified via instantx)").arg(nDepth); else return tr("%1 confirmations (verified via instantx)").arg(nDepth); } else { if(!wtx.IsTransactionLockTimedOut()){ int nDepth = wtx.GetDepthInMainChain(); if (nDepth < 0) return tr("conflicted"); else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) return tr("%1/offline (InstantX verification in progress - %2 of %3 signatures)").arg(nDepth).arg(signatures).arg(INSTANTX_SIGNATURES_TOTAL); else if (nDepth < 10) return tr("%1/confirmed (InstantX verification in progress - %2 of %3 signatures )").arg(nDepth).arg(signatures).arg(INSTANTX_SIGNATURES_TOTAL); else return tr("%1 confirmations (InstantX verification in progress - %2 of %3 signatures)").arg(nDepth).arg(signatures).arg(INSTANTX_SIGNATURES_TOTAL); } else { int nDepth = wtx.GetDepthInMainChain(); if (nDepth < 0) return tr("conflicted"); else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) return tr("%1/offline (InstantX verification failed)").arg(nDepth); else if (nDepth < 10) return tr("%1/confirmed (InstantX verification failed)").arg(nDepth); else return tr("%1 confirmations").arg(nDepth); } } } else { int nDepth = wtx.GetDepthInMainChain(); if (nDepth < 0) return tr("conflicted"); else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) return tr("%1/offline").arg(nDepth); else if (nDepth < 10) return tr("%1/unconfirmed").arg(nDepth); else return tr("%1 confirmations").arg(nDepth); } } }