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); }
std::string getNewWalletFileName() { std::string walletName; while (true) { std::cout << InformationMsg("What would you like to call your ") << InformationMsg("new wallet?: "); std::getline(std::cin, walletName); const std::string walletFileName = walletName + ".wallet"; if (boost::filesystem::exists(walletFileName)) { std::cout << std::endl << WarningMsg("A wallet with the filename " ) << InformationMsg(walletFileName) << WarningMsg(" already exists!") << std::endl << "Try another name." << std::endl << std::endl; } else if (walletName == "") { std::cout << std::endl << WarningMsg("Wallet name can't be blank! Try again.") << std::endl << std::endl; } else { return walletFileName; } } }
void viewWalletMsg() { std::cout << InformationMsg("Please remember that when using a view wallet " "you can only view incoming transactions!") << std::endl << InformationMsg("Therefore, if you have recieved transactions ") << InformationMsg("which you then spent, your balance will ") << InformationMsg("appear inflated.") << std::endl; }
std::string getExistingWalletFileName(Config &config) { bool initial = true; std::string walletName; while (true) { /* Only use wallet file once in case it is incorrect */ if (config.walletGiven && initial) { walletName = config.walletFile; } else { std::cout << InformationMsg("What is the name of the wallet ") << InformationMsg("you want to open?: "); std::getline(std::cin, walletName); } initial = false; const std::string walletFileName = walletName + ".wallet"; if (walletName == "") { std::cout << std::endl << WarningMsg("Wallet name can't be blank! Try again.") << std::endl << std::endl; } /* Allow people to enter wallet name with or without file extension */ else if (boost::filesystem::exists(walletName)) { return walletName; } else if (boost::filesystem::exists(walletFileName)) { return walletFileName; } else { std::cout << std::endl << WarningMsg("A wallet with the filename ") << InformationMsg(walletName) << WarningMsg(" or ") << InformationMsg(walletFileName) << WarningMsg(" doesn't exist!") << std::endl << "Ensure you entered your wallet name correctly." << std::endl << std::endl; } } }
void balance(CryptoNote::INode &node, CryptoNote::WalletGreen &wallet, bool viewWallet) { const uint64_t unconfirmedBalance = wallet.getPendingBalance(); const uint64_t confirmedBalance = wallet.getActualBalance(); const uint64_t totalBalance = unconfirmedBalance + confirmedBalance; const uint32_t localHeight = node.getLastLocalBlockHeight(); const uint32_t remoteHeight = node.getLastKnownBlockHeight(); const uint32_t walletHeight = wallet.getBlockCount(); std::cout << "Available balance: " << SuccessMsg(formatAmount(confirmedBalance)) << std::endl << "Locked (unconfirmed) balance: " << WarningMsg(formatAmount(unconfirmedBalance)) << std::endl << "Total balance: " << InformationMsg(formatAmount(totalBalance)) << std::endl; if (viewWallet) { std::cout << std::endl << InformationMsg("Please note that view only wallets " "can only track incoming transactions,") << std::endl << InformationMsg("and so your wallet balance may appear " "inflated.") << std::endl; } if (localHeight < remoteHeight) { std::cout << std::endl << InformationMsg("Your daemon is not fully synced with " "the network!") << std::endl << "Your balance may be incorrect until you are fully " << "synced!" << std::endl; } /* Small buffer because wallet height doesn't update instantly like node height does */ else if (walletHeight + 1000 < remoteHeight) { std::cout << std::endl << InformationMsg("The blockchain is still being scanned for " "your transactions.") << std::endl << "Balances might be incorrect whilst this is ongoing." << std::endl; } }
void printSyncSummary(uint32_t localHeight, uint32_t remoteHeight, uint32_t walletHeight) { if (localHeight == 0 && remoteHeight == 0) { std::cout << WarningMsg("Uh oh, it looks like you don't have ") << WarningMsg(WalletConfig::daemonName) << WarningMsg(" open!") << std::endl; } else if (walletHeight + 1000 < remoteHeight && localHeight == remoteHeight) { std::cout << InformationMsg("You are synced with the network, but the " "blockchain is still being scanned for " "your transactions.") << std::endl << "Balances might be incorrect whilst this is ongoing." << std::endl; } else if (localHeight == remoteHeight) { std::cout << SuccessMsg("Yay! You are synced!") << std::endl; } else { std::cout << WarningMsg("Be patient, you are still syncing with the " "network!") << std::endl; } }
std::shared_ptr<WalletInfo> mnemonicImportWallet(CryptoNote::WalletGreen &wallet) { std::string mnemonicPhrase; Crypto::SecretKey privateSpendKey; Crypto::SecretKey privateViewKey; do { std::cout << InformationMsg("Enter your mnemonic phrase (25 words): "); std::getline(std::cin, mnemonicPhrase); boost::algorithm::trim(mnemonicPhrase); } while (!Crypto::ElectrumWords::is_valid_mnemonic(mnemonicPhrase, privateSpendKey, std::cout)); CryptoNote::AccountBase::generateViewFromSpend(privateSpendKey, privateViewKey); return importFromKeys(wallet, privateSpendKey, privateViewKey); }
Maybe<uint64_t> getTransferAmount() { while (true) { std::string stringAmount; std::cout << std::endl << InformationMsg("How much WTIP do you want to send?: "); std::getline(std::cin, stringAmount); if (stringAmount == "cancel") { return Nothing<uint64_t>(); } uint64_t amount; if (parseAmount(stringAmount)) { parseAmount(stringAmount, amount); return Just<uint64_t>(amount); } } }
Maybe<uint16_t> getMixin() { while (true) { std::string stringMixin; std::cout << std::endl << InformationMsg("What mixin do you want to use?") << std::endl << "Mixin is how many times your transaction is mixed " << "with others for privacy." << std::endl << "Hit enter for the default mixin of 5: "; std::getline(std::cin, stringMixin); if (stringMixin == "") { return Just<uint16_t>(CryptoNote::parameters::DEFAULT_MIXIN); } else if (stringMixin == "cancel") { return Nothing<uint16_t>(); } else if (parseMixin(stringMixin)) { return Just<uint16_t>(std::stoi(stringMixin)); } } }
Maybe<uint64_t> getFee() { while (true) { std::string stringAmount; std::cout << std::endl << InformationMsg("What fee do you want to use?") << std::endl << "Hit enter for the default fee of 0.1 WTIP: "; std::getline(std::cin, stringAmount); if (stringAmount == "") { return Just<uint64_t>(CryptoNote::parameters::MINIMUM_FEE); } if (stringAmount == "cancel") { return Nothing<uint64_t>(); } uint64_t amount; if (parseFee(stringAmount)) { parseAmount(stringAmount, amount); return Just<uint64_t>(amount); } } }
void fullOptimize(CryptoNote::WalletGreen &wallet) { std::cout << "Attempting to optimize your wallet to allow you to " << "send large amounts at once. " << std::endl << WarningMsg("This may take a very long time!") << std::endl; if (!confirm("Do you want to proceed?")) { std::cout << WarningMsg("Cancelling optimization.") << std::endl; return; } for (int i = 1;;i++) { std::cout << InformationMsg("Running optimization round " + std::to_string(i) + "...") << std::endl; /* Optimize as many times as possible until optimization is no longer possible. */ if (!optimize(wallet, wallet.getActualBalance())) { break; } } std::cout << SuccessMsg("Full optimization completed!") << std::endl; }
Crypto::SecretKey getPrivateKey(std::string msg) { const size_t privateKeyLen = 64; size_t size; std::string privateKeyString; Crypto::Hash privateKeyHash; Crypto::SecretKey privateKey; Crypto::PublicKey publicKey; while (true) { std::cout << InformationMsg(msg); std::getline(std::cin, privateKeyString); boost::algorithm::trim(privateKeyString); if (privateKeyString.length() != privateKeyLen) { std::cout << std::endl << WarningMsg("Invalid private key, should be 64 ") << WarningMsg("characters! Try again.") << std::endl << std::endl; continue; } else if (!Common::fromHex(privateKeyString, &privateKeyHash, sizeof(privateKeyHash), size) || size != sizeof(privateKeyHash)) { std::cout << WarningMsg("Invalid private key, it is not a valid ") << WarningMsg("hex string! Try again.") << std::endl << std::endl; continue; } privateKey = *(struct Crypto::SecretKey *) &privateKeyHash; /* Just used for verification purposes before we pass it to walletgreen */ if (!Crypto::secret_key_to_public_key(privateKey, publicKey)) { std::cout << std::endl << WarningMsg("Invalid private key, is not on the ") << WarningMsg("ed25519 curve!") << std::endl << WarningMsg("Probably a typo - ensure you entered ") << WarningMsg("it correctly.") << std::endl << std::endl; continue; } return privateKey; } }
void printCommands(const std::vector<T> &commands, int offset) { int i = 1 + offset; std::cout << std::endl; for (const auto &command : commands) { std::cout << InformationMsg(" ") << InformationMsg(std::to_string(i)) << "\t" << SuccessMsg(command.commandName, 25) /* Pad to 25 chars */ << command.description << std::endl; i++; } std::cout << std::endl; }
void saveCSV(CryptoNote::WalletGreen &wallet, CryptoNote::INode &node) { const size_t numTransactions = wallet.getTransactionCount(); std::ofstream csv; csv.open(WalletConfig::csvFilename); if (!csv) { std::cout << WarningMsg("Couldn't open transactions.csv file for " "saving!") << std::endl << WarningMsg("Ensure it is not open in any other " "application.") << std::endl; return; } std::cout << InformationMsg("Saving CSV file...") << std::endl; /* Create CSV header */ csv << "Timestamp,Block Height,Hash,Amount,In/Out" << std::endl; /* Loop through transactions */ for (size_t i = 0; i < numTransactions; i++) { const CryptoNote::WalletTransaction t = wallet.getTransaction(i); /* Ignore fusion transactions */ if (t.totalAmount == 0) { continue; } const std::string amount = formatAmountBasic(std::abs(t.totalAmount)); const std::string direction = t.totalAmount > 0 ? "IN" : "OUT"; csv << unixTimeToDate(t.timestamp) << "," /* Timestamp */ << t.blockHeight << "," /* Block Height */ << Common::podToHex(t.hash) << "," /* Hash */ << amount << "," /* Amount */ << direction /* In/Out */ << std::endl; } csv.close(); std::cout << SuccessMsg("CSV successfully written to ") << SuccessMsg(WalletConfig::csvFilename) << SuccessMsg("!") << std::endl; }
Maybe<std::string> getPaymentID() { while (true) { std::string paymentID; std::cout << std::endl << InformationMsg("What payment ID do you want to use?") << std::endl << "These are usually used for sending to exchanges." << std::endl << WarningMsg("Warning: if you were given a payment ID,") << std::endl << WarningMsg("you MUST use it, or your funds may be lost!") << std::endl << "Hit enter for the default of no payment ID: "; std::getline(std::cin, paymentID); if (paymentID == "") { return Just<std::string>(paymentID); } if (paymentID == "cancel") { return Nothing<std::string>(); } std::vector<uint8_t> extra; /* Convert the payment ID into an "extra" */ if (!CryptoNote::createTxExtraWithPaymentId(paymentID, extra)) { std::cout << WarningMsg("Failed to parse! Payment ID's are 64 " "character hexadecimal strings.") << std::endl; } else { /* Then convert the "extra" back into a string so we can pass the argument that walletgreen expects. Note this string is not the same as the original paymentID string! */ std::string extraString; for (auto i : extra) { extraString += static_cast<char>(i); } return Just<std::string>(extraString); } } }
void promptSaveKeys(CryptoNote::WalletGreen &wallet) { std::cout << "Welcome to your new wallet, here is your payment address:" << std::endl << InformationMsg(wallet.getAddress(0)) << std::endl << std::endl << "Please copy your secret keys and mnemonic seed and store " << "them in a secure location: " << std::endl; printPrivateKeys(wallet, false); std::cout << std::endl; }
void reset(CryptoNote::INode &node, std::shared_ptr<WalletInfo> walletInfo) { uint64_t scanHeight = getScanHeight(); std::cout << std::endl << InformationMsg("This process may take some time to complete.") << std::endl << InformationMsg("You can't make any transactions during the ") << InformationMsg("process.") << std::endl << std::endl; if (!confirm("Are you sure?")) { return; } std::cout << InformationMsg("Resetting wallet...") << std::endl; walletInfo->wallet.reset(scanHeight); syncWallet(node, walletInfo); }
std::shared_ptr<WalletInfo> importFromKeys(CryptoNote::WalletGreen &wallet, Crypto::SecretKey privateSpendKey, Crypto::SecretKey privateViewKey) { const std::string walletFileName = getNewWalletFileName(); const std::string msg = "Give your new wallet a password: "******"Your wallet ") << InformationMsg(walletAddress) << InformationMsg(" has been successfully imported!") << std::endl << std::endl; return std::make_shared<WalletInfo>(walletFileName, walletPass, walletAddress, false, wallet); }
std::shared_ptr<WalletInfo> createViewWallet(CryptoNote::WalletGreen &wallet) { std::cout << WarningMsg("View wallets are only for viewing incoming ") << WarningMsg("transactions, and cannot make transfers.") << std::endl; bool create = confirm("Is this OK?"); std::cout << std::endl; if (!create) { return nullptr; } Crypto::SecretKey privateViewKey = getPrivateKey("Private View Key: "); std::string address; while (true) { std::cout << InformationMsg("Enter your public ") << InformationMsg(WalletConfig::ticker) << InformationMsg(" address: "); std::getline(std::cin, address); boost::algorithm::trim(address); if (parseAddress(address)) { break; } } const std::string walletFileName = getNewWalletFileName(); const std::string msg = "Give your new wallet a password: "******"Your view wallet ") << InformationMsg(address) << InformationMsg(" has been successfully imported!") << std::endl << std::endl; viewWalletMsg(); return std::make_shared<WalletInfo>(walletFileName, walletPass, address, true, wallet); }
void txSecretKey(CryptoNote::WalletGreen &wallet) { std::string hashStr; Crypto::Hash txid; while (true) { std::cout << InformationMsg("Enter transaction hash: "); std::getline(std::cin, hashStr); boost::algorithm::trim(hashStr); if (!parse_hash256(hashStr, txid)) { std::cout << WarningMsg("Failed to parse txid") << std::endl; return; } else { break; } if (std::cin.fail() || std::cin.eof()) { std::cin.clear(); break; } } Crypto::SecretKey txSecretKey = wallet.getTransactionSecretKey(txid); if (txSecretKey == CryptoNote::NULL_SECRET_KEY) { std::cout << WarningMsg("Transaction ") << WarningMsg(hashStr) << WarningMsg(" secret key is not available") << std::endl; return; } std::cout << SuccessMsg("Transaction secret key: ") << std::endl << SuccessMsg(Common::podToHex(txSecretKey)) << std::endl; }
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; } }
Maybe<std::string> getDestinationAddress() { while (true) { std::string transferAddr; std::cout << InformationMsg("What address do you want to " "transfer to?: "); std::getline(std::cin, transferAddr); boost::algorithm::trim(transferAddr); if (transferAddr == "cancel") { return Nothing<std::string>(); } if (parseAddress(transferAddr)) { return Just<std::string>(transferAddr); } } }
std::tuple<bool, std::shared_ptr<WalletInfo>> selectionScreen(Config &config, CryptoNote::WalletGreen &wallet, CryptoNote::INode &node) { while (true) { /* Get the users action */ std::string launchCommand = getAction(config); /* User wants to exit */ if (launchCommand == "exit") { return std::make_tuple(true, nullptr); } /* Handle the user input */ std::shared_ptr<WalletInfo> walletInfo = handleLaunchCommand( wallet, launchCommand, config ); /* Action failed, for example wallet file is corrupted. */ if (walletInfo == nullptr) { std::cout << InformationMsg("Returning to selection screen...") << std::endl; continue; } /* Node is down, user wants to exit */ if (!checkNodeStatus(node)) { return std::make_tuple(true, nullptr); } /* If we're creating a wallet, don't print the lengthy sync process */ if (launchCommand == "create") { std::stringstream str; str << std::endl << "Your wallet is syncing with the network in the background." << std::endl << "Until this is completed new transactions might not show " << "up." << std::endl << "Use the status command to check the progress." << std::endl; std::cout << InformationMsg(str.str()); } else { /* Need another signal handler here, in case the user does ctrl+c whilst syncing, to save the wallet. The walletInfo ptr will be null in the parent scope, since we haven't returned it yet. */ bool alreadyShuttingDown = false; Tools::SignalHandler::install([&] { if (shutdown(walletInfo, node, alreadyShuttingDown)) { exit(0); } }); syncWallet(node, walletInfo); } /* Return the wallet info */ return std::make_tuple(false, walletInfo); } }
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; } }
std::shared_ptr<WalletInfo> openWallet(CryptoNote::WalletGreen &wallet, Config &config) { const std::string walletFileName = getExistingWalletFileName(config); bool initial = true; while (true) { std::string walletPass; /* Only use the command line pass once, otherwise we will infinite loop if it is incorrect */ if (initial && config.passGiven) { walletPass = config.walletPass; } else { walletPass = getWalletPassword(false, "Enter password: "******"Your view only wallet ") << InformationMsg(walletAddress) << InformationMsg(" has been successfully opened!") << std::endl << std::endl; viewWalletMsg(); viewWallet = true; } else { std::cout << std::endl << InformationMsg("Your wallet ") << InformationMsg(walletAddress) << InformationMsg(" has been successfully opened!") << std::endl << std::endl; } return std::make_shared<WalletInfo>( walletFileName, walletPass, walletAddress, viewWallet, wallet ); } catch (const std::system_error& e) { bool handled = false; switch (e.code().value()) { case CryptoNote::error::WRONG_PASSWORD: { std::cout << std::endl << WarningMsg("Incorrect password! Try again.") << std::endl << std::endl; handled = true; break; } case CryptoNote::error::WRONG_VERSION: { std::stringstream msg; msg << "Could not open wallet file! It doesn't appear " << "to be a valid wallet!" << std::endl << "Ensure you are opening a wallet file, and the " << "file has not gotten corrupted." << std::endl << "Try reimporting via keys, and always close " << WalletConfig::walletName << " with the exit " << "command to prevent corruption." << std::endl; std::cout << WarningMsg(msg.str()) << std::endl; return nullptr; } } if (handled) { continue; } const std::string alreadyOpenMsg = "MemoryMappedFile::open: The process cannot access the file " "because it is being used by another process."; const std::string errorMsg = e.what(); /* The message actually has a \r\n on the end but i'd prefer to keep just the raw string in the source so check the it starts with instead */ if (boost::starts_with(errorMsg, alreadyOpenMsg)) { std::cout << WarningMsg("Could not open wallet! It is already " "open in another process.") << std::endl << WarningMsg("Check with a task manager that you " "don't have ") << WalletConfig::walletName << WarningMsg(" open twice.") << std::endl << WarningMsg("Also check you don't have another " "wallet program open, such as a GUI " "wallet or ") << WarningMsg(WalletConfig::walletdName) << WarningMsg(".") << std::endl << std::endl; return nullptr; } else { std::cout << "Unexpected error: " << errorMsg << std::endl; std::cout << "Please report this error message and what " << "you did to cause it." << std::endl << std::endl; return nullptr; } } } }
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 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 txProof(CryptoNote::WalletGreen &wallet) { std::string txHashStr; Crypto::Hash txid; while (true) { std::cout << InformationMsg("Enter transaction hash: "); std::getline(std::cin, txHashStr); boost::algorithm::trim(txHashStr); if (!parse_hash256(txHashStr, txid)) { std::cout << WarningMsg("Failed to parse txid") << std::endl; return; } else { break; } if (std::cin.fail() || std::cin.eof()) { std::cin.clear(); break; } } Crypto::SecretKey txSecretKey = wallet.getTransactionSecretKey(txid); if (txSecretKey == CryptoNote::NULL_SECRET_KEY) { std::cout << InformationMsg("Transaction ") << InformationMsg(txHashStr) << InformationMsg(" secret key is not available.") << std::endl << InformationMsg("If you have it elsewhere, ") << InformationMsg("enter it here to continue: ") << std::endl; Crypto::Hash tx_key_hash; while (true) { std::string keyStr; std::getline(std::cin, keyStr); boost::algorithm::trim(keyStr); size_t size; if (!Common::fromHex(keyStr, &tx_key_hash, sizeof(tx_key_hash), size) || size != sizeof(tx_key_hash)) { std::cout << WarningMsg("Failed to parse tx secret key ") << WarningMsg(keyStr) << std::endl; return; } else { txSecretKey = *(struct Crypto::SecretKey *) &tx_key_hash; break; } if (std::cin.fail() || std::cin.eof()) { std::cin.clear(); break; } } } CryptoNote::AccountPublicAddress destAddress; while (true) { std::cout << InformationMsg("Enter destination address: "); std::string addrStr; uint64_t prefix; std::getline(std::cin, addrStr); boost::algorithm::trim(addrStr); if (!CryptoNote::parseAccountAddressString(prefix, destAddress, addrStr)) { std::cout << WarningMsg("Failed to parse address") << std::endl; } else { break; } if (std::cin.fail() || std::cin.eof()) { std::cin.clear(); break; } } try { std::string sig; if (wallet.getTransactionProof(txid, destAddress, txSecretKey, sig)) { std::cout << SuccessMsg("Transaction proof: ") << std::endl << SuccessMsg(sig) << std::endl; } else { std::cout << WarningMsg("Failed to get transaction proof") << std::endl; } } catch (std::system_error& x) { std::cout << WarningMsg("Error while getting transaction proof: ") << WarningMsg(x.what()) << std::endl; } catch (std::exception& x) { std::cout << WarningMsg("Error while getting transaction proof: ") << WarningMsg(x.what()) << std::endl; } }
void save(CryptoNote::WalletGreen &wallet) { std::cout << InformationMsg("Saving.") << std::endl; wallet.save(); std::cout << InformationMsg("Saved.") << std::endl; }
void blockchainHeight(CryptoNote::INode &node, CryptoNote::WalletGreen &wallet) { const uint32_t localHeight = node.getLastLocalBlockHeight(); const uint32_t remoteHeight = node.getLastKnownBlockHeight(); const uint32_t walletHeight = wallet.getBlockCount() - 1; /* This is the height that the wallet has been scanned to. The blockchain can be fully updated, but we have to walk the chain to find our transactions, and this number indicates that progress. */ std::cout << "Wallet blockchain height: "; /* Small buffer because wallet height doesn't update instantly like node height does */ if (walletHeight + 1000 > remoteHeight) { std::cout << SuccessMsg(std::to_string(walletHeight)); } else { std::cout << WarningMsg(std::to_string(walletHeight)); } std::cout << std::endl << "Local blockchain height: "; if (localHeight == remoteHeight) { std::cout << SuccessMsg(std::to_string(localHeight)); } else { std::cout << WarningMsg(std::to_string(localHeight)); } std::cout << std::endl << "Network blockchain height: " << SuccessMsg(std::to_string(remoteHeight)) << std::endl; if (localHeight == 0 && remoteHeight == 0) { std::cout << WarningMsg("Uh oh, it looks like you don't have ") << WarningMsg(WalletConfig::daemonName) << WarningMsg(" open!") << std::endl; } else if (walletHeight + 1000 < remoteHeight && localHeight == remoteHeight) { std::cout << InformationMsg("You are synced with the network, but the " "blockchain is still being scanned for " "your transactions.") << std::endl << "Balances might be incorrect whilst this is ongoing." << std::endl; } else if (localHeight == remoteHeight) { std::cout << SuccessMsg("Yay! You are synced!") << std::endl; } else { std::cout << WarningMsg("Be patient, you are still syncing with the " "network!") << std::endl; } }