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);
}
Example #2
0
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;
    }
}