void transfer(std::shared_ptr<WalletInfo> walletInfo) { std::cout << InformationMsg("Note: You can type cancel at any time to " "cancel the transaction") << std::endl << std::endl; uint64_t balance = walletInfo->wallet.getActualBalance(); auto maybeAddress = getDestinationAddress(); if (!maybeAddress.isJust) { std::cout << WarningMsg("Cancelling transaction.") << std::endl; return; } std::string address = maybeAddress.x; auto maybeAmount = getTransferAmount(); if (!maybeAmount.isJust) { std::cout << WarningMsg("Cancelling transaction.") << std::endl; return; } uint64_t amount = maybeAmount.x; if (balance < amount) { std::cout << WarningMsg("You don't have enough funds to cover this " "transaction!") << std::endl << InformationMsg("Funds needed: " + formatAmount(amount)) << std::endl << SuccessMsg("Funds available: " + formatAmount(balance)) << std::endl; return; } auto maybeFee = getFee(); if (!maybeFee.isJust) { std::cout << WarningMsg("Cancelling transaction.") << std::endl; return; } uint64_t fee = maybeFee.x; if (balance < amount + fee) { std::cout << WarningMsg("You don't have enough funds to cover this " "transaction!") << std::endl << InformationMsg("Funds needed: " + formatAmount(amount + fee)) << std::endl << SuccessMsg("Funds available: " + formatAmount(balance)) << std::endl; return; } auto maybeMixin = getMixin(); if (!maybeMixin.isJust) { std::cout << WarningMsg("Cancelling transaction.") << std::endl; return; } uint16_t mixin = maybeMixin.x; auto maybeExtra = getPaymentID(); if (!maybeExtra.isJust) { std::cout << WarningMsg("Cancelling transaction.") << std::endl; return; } std::string extra = maybeExtra.x; doTransfer(mixin, address, amount, fee, extra, walletInfo); }
bool confirmTransaction(CryptoNote::TransactionParameters t, std::shared_ptr<WalletInfo> walletInfo) { std::cout << std::endl << InformationMsg("Confirm Transaction?") << std::endl; std::cout << "You are sending " << SuccessMsg(formatAmount(t.destinations[0].amount)) << ", with a fee of " << SuccessMsg(formatAmount(t.fee)) << ", " << std::endl; std::string paymentID = getPaymentID(t.extra); if (paymentID != "") { std::cout << "A mixin of " << SuccessMsg(std::to_string(t.mixIn)) << " and a Payment ID of " << SuccessMsg(paymentID); } else { std::cout << "And a mixin of " << SuccessMsg(std::to_string(t.mixIn)); } std::cout << std::endl << std::endl << "FROM: " << SuccessMsg(walletInfo->walletFileName) << std::endl << "TO: " << SuccessMsg(t.destinations[0].address) << std::endl << std::endl; if (confirm("Is this correct?")) { confirmPassword(walletInfo->walletPass); return true; } else { return false; } }
void listTransfers(bool incoming, bool outgoing, CryptoNote::WalletGreen &wallet, CryptoNote::INode &node) { const size_t numTransactions = wallet.getTransactionCount(); int64_t totalSpent = 0; int64_t totalReceived = 0; for (size_t i = 0; i < numTransactions; i++) { const CryptoNote::WalletTransaction t = wallet.getTransaction(i); if (t.totalAmount < 0 && outgoing) { printOutgoingTransfer(t, node); totalSpent += -t.totalAmount; } else if (t.totalAmount > 0 && incoming) { printIncomingTransfer(t, node); totalReceived += t.totalAmount; } } if (incoming) { std::cout << SuccessMsg("Total received: " + formatAmount(totalReceived)) << std::endl; } if (outgoing) { std::cout << WarningMsg("Total spent: " + formatAmount(totalSpent)) << std::endl; } }
void reserveProof(std::shared_ptr<WalletInfo> walletInfo, bool viewWallet) { if (viewWallet) { std::cout << WarningMsg("This is tracking wallet. ") << WarningMsg("The reserve proof can be generated ") << WarningMsg("only by a full wallet.") << std::endl; return; } uint64_t amount; uint64_t actualBalance = walletInfo->wallet.getActualBalance(); while (true) { std::cout << InformationMsg("Enter amount to prove ") << InformationMsg("or type ") << "\"all\" " << InformationMsg("if you want to prove all balance: "); std::string amtStr; std::getline(std::cin, amtStr); if (amtStr == "all") { amount = actualBalance; break; } if (parseAmount(amtStr, amount)) { if (amount > actualBalance) { std::cout << WarningMsg("Amount is bigger than ") << WarningMsg("actual balance ") << WarningMsg(formatAmount(actualBalance)) << std::endl; } else { break; } } if (std::cin.fail() || std::cin.eof()) { std::cin.clear(); break; } } std::string message; while (true) { std::cout << InformationMsg("Enter optional challenge message: "); std::getline(std::cin, message); boost::algorithm::trim(message); if (message == "") { break; } if (std::cin.fail() || std::cin.eof()) { std::cin.clear(); break; } } try { const std::string sig = walletInfo->wallet.getReserveProof(amount, walletInfo->walletAddress, message.empty() ? "" : message); std::string fileName; while (true) { std::cout << InformationMsg("Enter file name ") << InformationMsg("where to save your proof: "); std::getline(std::cin, fileName); boost::algorithm::trim(fileName); if (boost::filesystem::portable_name(fileName)) { break; } else { std::cout << WarningMsg("Enter valid file name") << std::endl; } if (std::cin.fail() || std::cin.eof()) { std::cin.clear(); break; } } fileName += ".txt"; boost::system::error_code ec; if (boost::filesystem::exists(fileName, ec)) { boost::filesystem::remove(fileName, ec); } std::ofstream proofFile(fileName, std::ios::out | std::ios::trunc | std::ios::binary); if (!proofFile.good()) { std::cout << WarningMsg("Failed to save reserve proof to file") << std::endl; return; } proofFile << sig; std::cout << SuccessMsg("Proof signature saved to file: ") << SuccessMsg(fileName) << std::endl; } catch (const std::exception &e) { std::cout << WarningMsg("Failed to get reserve proof: ") << WarningMsg(e.what()) << std::endl; } }
void sendMultipleTransactions(CryptoNote::WalletGreen &wallet, std::vector<CryptoNote::TransactionParameters> transfers) { size_t numTxs = transfers.size(); size_t currentTx = 1; std::cout << "Your transaction has been split up into " << numTxs << " separate transactions of " << formatAmount(transfers[0].destinations[0].amount) << ". It may take some time to send all the transactions, " << "please be patient." << std::endl << std::endl; for (auto tx : transfers) { while (true) { std::cout << "Attempting to send transaction " << InformationMsg(std::to_string(currentTx)) << " of " << InformationMsg(std::to_string(numTxs)) << std::endl; wallet.updateInternalCache(); uint64_t neededBalance = tx.destinations[0].amount + tx.fee; if (neededBalance < wallet.getActualBalance()) { size_t id = wallet.transfer(tx); CryptoNote::WalletTransaction sentTx = wallet.getTransaction(id); std::cout << SuccessMsg("Transaction has been sent!") << std::endl << SuccessMsg("Hash: " + Common::podToHex(sentTx.hash)) << std::endl << std::endl; break; } std::cout << "Not enough balance available to send transaction, " << "this is because some of your balance is used when " << "sending another transaction to help hide the size " << "of your transaction, and is locked for a short " << "time. It will return shortly." << std::endl << "Needed balance: " << formatAmount(neededBalance) << std::endl << "Available balance: " << formatAmount(wallet.getActualBalance()) << std::endl << "Locked balance: " << formatAmount(wallet.getPendingBalance()) << std::endl << "Will try again in 5 seconds..." << std::endl << std::endl; std::this_thread::sleep_for(std::chrono::seconds(5)); } currentTx++; } std::cout << SuccessMsg("All transactions sent!") << std::endl; }
void doTransfer(uint16_t mixin, std::string address, uint64_t amount, uint64_t fee, std::string extra, std::shared_ptr<WalletInfo> walletInfo) { uint64_t balance = walletInfo->wallet.getActualBalance(); if (balance < amount + fee) { std::cout << WarningMsg("You don't have enough funds to cover this " "transaction!") << std::endl << InformationMsg("Funds needed: " + formatAmount(amount + fee)) << std::endl << SuccessMsg("Funds available: " + formatAmount(balance)) << std::endl; return; } std::vector<CryptoNote::WalletOrder> transfers; CryptoNote::DonationSettings d; d.address = ""; CryptoNote::WalletOrder w; w.address = address; w.amount = amount; transfers.push_back(w); CryptoNote::TransactionParameters p; p.destinations = transfers; p.fee = fee; p.mixIn = mixin; p.extra = extra; p.changeDestination = walletInfo->walletAddress; if (!confirmTransaction(p, walletInfo)) { std::cout << WarningMsg("Cancelling transaction.") << std::endl; return; } bool retried = false; while (true) { try { if (walletInfo->wallet.txIsTooLarge(p)) { if (!fusionTX(walletInfo->wallet, p)) { return; } if (walletInfo->wallet.txIsTooLarge(p)) { splitTx(walletInfo->wallet, p); } else { size_t id = walletInfo->wallet.transfer(p); CryptoNote::WalletTransaction tx = walletInfo->wallet.getTransaction(id); std::cout << SuccessMsg("Transaction has been sent!") << std::endl << SuccessMsg("Hash:" + Common::podToHex(tx.hash)) << std::endl; } } else { size_t id = walletInfo->wallet.transfer(p); CryptoNote::WalletTransaction tx = walletInfo->wallet.getTransaction(id); std::cout << SuccessMsg("Transaction has been sent!") << std::endl << SuccessMsg("Hash: " + Common::podToHex(tx.hash)) << std::endl; } } catch (const std::system_error &e) { std::string errMsg = e.what(); /* For some reason we are unable to send our full balance when I have tested. It looks possible it is due to dust amounts, possibly these can't be sent? The relevant code can be found in src/Wallet/WalletGreen.cpp in the function selectTransfers() */ if (errMsg == "Not enough money: Wrong amount" && !retried) { std::cout << WarningMsg("Failed to send transaction - not " "enough funds!") << std::endl << "You sometimes need to send a small amount less " << "than your full balance to get the transfer to " << "succeed." << std::endl << "This is possibly due " << "to dust in your wallet that is unable to be " << "sent without a mixin of 0." << std::endl; /* We can try and resend with a mixin of zero, but only retry once */ if(confirm("Retry transaction with mixin of 0? " "This will compromise privacy.")) { p.mixIn = 0; retried = true; continue; } else { std::cout << WarningMsg("Cancelling transaction.") << std::endl; } } else if ((errMsg == "MixIn count is too big" || errMsg == "Internal node error") && !retried) { std::cout << WarningMsg("Failed to send transaction!") << std::endl << "Unable to find enough outputs to mix with." << std::endl << "Try lowering the amount you are " << "sending in one transaction." << std::endl << "Alternatively, you can try lowering the mixin " << "count to 0, but this will compromise privacy." << std::endl; if(confirm("Retry transaction with mixin of 0? " "This will compromise privacy.")) { p.mixIn = 0; retried = true; continue; } else { std::cout << WarningMsg("Cancelling transaction.") << std::endl; } } else if (errMsg == "Network error") { std::cout << WarningMsg("Couldn't connect to the network to " "send the transaction!") << std::endl << "Ensure Worktipsd or the remote node you are " << "using is open and functioning." << std::endl; } else if (retried) { std::cout << WarningMsg("Failed to send transaction with zero " "mixin! Try lowering the amount you " "are sending.") << std::endl; } else { std::cout << WarningMsg("Failed to send transaction!") << std::endl << "Error message: " << errMsg << std::endl; } } break; } }