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);
}
void printHeights(uint32_t localHeight, uint32_t remoteHeight,
	uint32_t walletHeight)
{
	/* 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;
}
void printSyncStatus(uint32_t localHeight, uint32_t remoteHeight,
	uint32_t walletHeight)
{
	std::string networkSyncPercentage
		= Common::get_sync_percentage(localHeight, remoteHeight) + "%";

	std::string walletSyncPercentage
		= Common::get_sync_percentage(walletHeight, remoteHeight) + "%";

	std::cout << "Network sync status: ";

	if (localHeight == remoteHeight)
	{
		std::cout << SuccessMsg(networkSyncPercentage) << std::endl;
	}
	else
	{
		std::cout << WarningMsg(networkSyncPercentage) << std::endl;
	}

	std::cout << "Wallet sync status: ";

	/* Small buffer because wallet height is not always completely accurate */
	if (walletHeight + 1000 > remoteHeight)
	{
		std::cout << SuccessMsg(walletSyncPercentage) << std::endl;
	}
	else
	{
		std::cout << WarningMsg(walletSyncPercentage) << std::endl;
	}
}
void printIncomingTransfer(CryptoNote::WalletTransaction t,
                           CryptoNote::INode &node)
{
    std::cout << SuccessMsg("Incoming transfer:")
              << std::endl
              << SuccessMsg("Block height: " + std::to_string(t.blockHeight))
              << std::endl;

    /* Block height will be garbage from memory if not confirmed yet */
    if (t.timestamp != 0)
    {
        std::cout << SuccessMsg("Block height: ")
                  << SuccessMsg(std::to_string(t.blockHeight))
                  << std::endl
                  << SuccessMsg("Timestamp: ")
                  << SuccessMsg(unixTimeToDate(t.timestamp))
                  << std::endl;
    }

    std::cout << SuccessMsg("Hash: " + Common::podToHex(t.hash))
              << std::endl
              << SuccessMsg("Amount: " + formatAmount(t.totalAmount))
              << std::endl;

    const std::string paymentID = getPaymentIDFromExtra(t.extra);

    if (paymentID != "")
    {
        std::cout << SuccessMsg("Payment ID: " + paymentID) << std::endl;
    }

    std::cout << std::endl;
}
void printPrivateKeys(CryptoNote::WalletGreen &wallet, bool viewWallet)
{
    auto privateViewKey = wallet.getViewKey().secretKey;

    if (viewWallet)
    {
        std::cout << SuccessMsg("Private view key:")
                  << std::endl
                  << SuccessMsg(Common::podToHex(privateViewKey))
                  << std::endl;
        return;
    }

	auto privateSpendKey = wallet.getAddressSpendKey(0).secretKey;

    Crypto::SecretKey derivedPrivateViewKey;

    CryptoNote::AccountBase::generateViewFromSpend(privateSpendKey,
                                                   derivedPrivateViewKey);

    const bool deterministicPrivateKeys
             = derivedPrivateViewKey == privateViewKey;

    std::cout << SuccessMsg("Private spend key:")
              << std::endl
              << SuccessMsg(Common::podToHex(privateSpendKey))
              << std::endl
              << std::endl
              << SuccessMsg("Private view key:")
              << std::endl
              << SuccessMsg(Common::podToHex(privateViewKey))
              << std::endl;

    if (deterministicPrivateKeys)
    {
        std::string mnemonicSeed;

        Crypto::ElectrumWords::bytes_to_words(privateSpendKey, 
                                              mnemonicSeed,
                                              "English");

        std::cout << std::endl
                  << SuccessMsg("Mnemonic seed:")
                  << std::endl
                  << SuccessMsg(mnemonicSeed)
                  << std::endl;
    }

    std::cout << std::endl
              << SuccessMsg("GUI Importable Private Key:")
              << std::endl
              << SuccessMsg(getGUIPrivateKey(wallet))
              << 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;
}
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;
	}
}
示例#8
0
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;
}
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;
}
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 printHashrate(uint64_t difficulty)
{
    /* Offline node / not responding */
    if (difficulty == 0)
    {
        return;
    }

    /* Hashrate is difficulty divided by block target time */
    uint32_t hashrate = static_cast<uint32_t>(
        round(difficulty / CryptoNote::parameters::DIFFICULTY_TARGET)
    );

    std::cout << "Network hashrate: "
              << SuccessMsg(Common::get_mining_speed(hashrate))
              << " (Based on the last local block)" << std::endl;
}
示例#12
0
bool fusionTX(CryptoNote::WalletGreen &wallet, 
              CryptoNote::TransactionParameters p)
{
    std::cout << WarningMsg("Your transaction is too large to be accepted by "
                            "the network!")
              << std::endl << "We're attempting to optimize your "
              << "wallet, which hopefully will make the transaction small "
              << "enough to fit in a block." << std::endl 
              << "Please wait, this will take some time..." << std::endl 
              << std::endl;

    /* We could check if optimization succeeded, but it's not really needed
       because we then check if the transaction is too large... it could have
       potentially become valid because another payment came in. */
    optimize(wallet, p.destinations[0].amount + p.fee);

    const auto startTime = std::chrono::system_clock::now();

    while (wallet.getActualBalance() < p.destinations[0].amount + p.fee)
    {
        /* Break after a minute just in case something has gone wrong */
        if ((std::chrono::system_clock::now() - startTime) > 
             std::chrono::minutes(5))
        {
            std::cout << WarningMsg("Fusion transactions have "
                                    "completed, however available "
                                    "balance is less than transfer "
                                    "amount specified.") << std::endl
                      << WarningMsg("Transfer aborted, please review "
                                    "and start a new transfer.")
                      << std::endl;

            return false;
        }

        std::cout << WarningMsg("Optimization completed, but balance "
                                "is not fully unlocked yet!")
                  << std::endl
                  << SuccessMsg("Will try again in 5 seconds...")
                  << std::endl;

        std::this_thread::sleep_for(std::chrono::seconds(5));
    }

    return true;
}
示例#13
0
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 changePassword(std::shared_ptr<WalletInfo> walletInfo)
{
    /* Check the user knows the current password */
    confirmPassword(walletInfo->walletPass, "Confirm your current password: "******"Enter your new password: "******"Your password has been changed!") << std::endl;
}
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;
    }
}
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 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;
    }
}
示例#18
0
bool optimize(CryptoNote::WalletGreen &wallet, uint64_t threshold)
{
    std::vector<Crypto::Hash> fusionTransactionHashes;

    while (true)
    {
        /* Create as many fusion transactions until we can't send anymore,
           either because balance is locked too much or we can no longer
           optimize anymore transactions */
        const size_t tmpFusionTxID = makeFusionTransaction(wallet, threshold);

        if (tmpFusionTxID == CryptoNote::WALLET_INVALID_TRANSACTION_ID)
        {
            break;
        }
        else
        {
            const CryptoNote::WalletTransaction w
                = wallet.getTransaction(tmpFusionTxID);

            fusionTransactionHashes.push_back(w.hash);

            if (fusionTransactionHashes.size() == 1)
            {
                std::cout << SuccessMsg("Created 1 fusion transaction!")
                          << std::endl;
            }
            else
            {
                std::cout << SuccessMsg("Created " 
                            + std::to_string(fusionTransactionHashes.size())
                                    + " fusion transactions!") << std::endl;
            }
        }
    }

    if (fusionTransactionHashes.empty())
    {
        return false;
    }

    /* Hurr durr grammar */
    if (fusionTransactionHashes.size() == 1)
    {
        std::cout << SuccessMsg("1 fusion transaction has been sent, waiting "
                                "for balance to return and unlock")
                  << std::endl << std::endl;
    }
    else
    {
        std::cout << SuccessMsg(std::to_string(fusionTransactionHashes.size()) +
                                " fusion transactions have been sent, waiting "
                                "for balance to return and unlock")
              << std::endl << std::endl;
    }

    wallet.updateInternalCache();

    /* Short sleep to ensure it's in the transaction pool when we poll it */
    std::this_thread::sleep_for(std::chrono::seconds(1));

    while (true)
    {
        const std::vector<CryptoNote::WalletTransactionWithTransfers> 
            unconfirmedTransactions = wallet.getUnconfirmedTransactions();

        std::vector<Crypto::Hash> unconfirmedTxHashes;

        for (const auto &t : unconfirmedTransactions)
        {
            unconfirmedTxHashes.push_back(t.transaction.hash);
        }

        bool fusionCompleted = true;

        /* Is our fusion transaction still unconfirmed? We can't gain the
           benefits of fusioning if the balance hasn't unlocked, so we can
           send this new optimized balance */
        for (const auto &tx : fusionTransactionHashes)
        {
            /* If the fusion transaction hash is present in the unconfirmed
               transactions pool, we need to wait for it to complete. */
            if (std::find(unconfirmedTxHashes.begin(),
                          unconfirmedTxHashes.end(), tx) 
                       != unconfirmedTxHashes.end())
            {
                fusionCompleted = false; 
            }
            else
            {
                /* We can't find this transaction in the unconfirmed
                   transaction pool anymore, so it has been confirmed. Remove
                   it so we both have to check less transactions each time,
                   and we can easily update the transactions left to confirm
                   output message */
                fusionTransactionHashes.erase(std::remove
                    (fusionTransactionHashes.begin(),
                     fusionTransactionHashes.end(), tx), 
                     fusionTransactionHashes.end());
            }
        }

        if (!fusionCompleted)
        {
            std::cout << WarningMsg("Balance is still locked, "
                  + std::to_string(fusionTransactionHashes.size()));

            /* More grammar... */
            if (fusionTransactionHashes.size() == 1)
            {
                std::cout << WarningMsg(" fusion transaction still to be "
                                        "confirmed.");
            }
            else
            {
                std::cout << WarningMsg(" fusion transactions still to be "
                                        "confirmed.");
            }
            
            std::cout << std::endl
                      << SuccessMsg("Will try again in 1 minute...")
                      << std::endl;

            std::this_thread::sleep_for(std::chrono::minutes(1));

            wallet.updateInternalCache();
        }
        else
        {
            std::cout << SuccessMsg("All fusion transactions confirmed!")
                      << std::endl;
            break;
        }
    }

    return true;
}
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;
    }
}
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 printPeerCount(size_t peerCount)
{
    std::cout << "Peers: " << SuccessMsg(std::to_string(peerCount))
              << 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;
    }
}