|
13 | 13 | #include "main.h"
|
14 | 14 | #include "net.h"
|
15 | 15 | #include "wallet.h"
|
| 16 | +#include "coincontrol.h" |
16 | 17 |
|
17 | 18 | using namespace std;
|
18 | 19 | using namespace boost;
|
@@ -604,6 +605,159 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
604 | 605 | }
|
605 | 606 |
|
606 | 607 |
|
| 608 | +UniValue consolidateutxos(const UniValue& params, bool fHelp) |
| 609 | +{ |
| 610 | + if (fHelp || params.size() < 1 || params.size() > 3) |
| 611 | + throw runtime_error( |
| 612 | + "consolidateutxos <address> [UTXO size [maximum number of inputs]]\n" |
| 613 | + "\n" |
| 614 | + "Performs a single transaction to consolidate UTXOs on\n" |
| 615 | + "a given address. The optional parameter of UTXO size will result\n" |
| 616 | + "in consolidating UTXOs to generate an output of that size or\n" |
| 617 | + "the output for the total value of the specified maximum,\n" |
| 618 | + "maximum number of smallest inputs, whichever is less.\n"); |
| 619 | + |
| 620 | + UniValue result(UniValue::VOBJ); |
| 621 | + |
| 622 | + std::string sAddress = params[0].get_str(); |
| 623 | + CBitcoinAddress OptimizeAddress(sAddress); |
| 624 | + |
| 625 | + int64_t nConsolidateLimit = 0; |
| 626 | + // Set default maximum consolidation to 200 inputs if it is not specified. |
| 627 | + unsigned int nInputNumberLimit = 200; |
| 628 | + |
| 629 | + if (params.size() > 1) nConsolidateLimit = AmountFromValue(params[1]); |
| 630 | + if (params.size() > 2) nInputNumberLimit = params[2].get_int(); |
| 631 | + |
| 632 | + // Clamp InputNumberLimit to 200. |
| 633 | + nInputNumberLimit = std::min(nInputNumberLimit, (unsigned int) 200); |
| 634 | + |
| 635 | + if (!OptimizeAddress.IsValid()) |
| 636 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Gridcoin address: ") + sAddress); |
| 637 | + |
| 638 | + // Set the consolidation transaction address to the same as the inputs to consolidate. |
| 639 | + CScript scriptDestPubKey; |
| 640 | + scriptDestPubKey.SetDestination(OptimizeAddress.Get()); |
| 641 | + |
| 642 | + std::vector<COutput> vecInputs; |
| 643 | + |
| 644 | + std::multimap<int64_t, COutput> mInputs; |
| 645 | + |
| 646 | + LOCK(pwalletMain->cs_wallet); |
| 647 | + |
| 648 | + pwalletMain->AvailableCoins(vecInputs, false, NULL, false); |
| 649 | + |
| 650 | + // Filter outputs by matching address and insert into sorted multimap. |
| 651 | + for (auto const& out : vecInputs) |
| 652 | + { |
| 653 | + CTxDestination outaddress; |
| 654 | + int64_t nOutValue = out.tx->vout[out.i].nValue; |
| 655 | + |
| 656 | + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, outaddress)) continue; |
| 657 | + |
| 658 | + if (CBitcoinAddress(outaddress) == OptimizeAddress) |
| 659 | + mInputs.insert(std::make_pair(nOutValue, out)); |
| 660 | + } |
| 661 | + |
| 662 | + CWalletTx wtxNew; |
| 663 | + |
| 664 | + // For min fee |
| 665 | + CTransaction txDummy; |
| 666 | + |
| 667 | + set<pair<const CWalletTx*,unsigned int>> setCoins; |
| 668 | + |
| 669 | + unsigned int iInputCount = 0; |
| 670 | + int64_t nValue = 0; |
| 671 | + |
| 672 | + // Construct the inputs to the consolidation transaction. Either all of the inputs from above, or 200, |
| 673 | + // or when the total reaches/exceeds nConsolidateLimit, whichever is more limiting. The map allows us |
| 674 | + // to elegantly select the UTXO's from the smallest upwards. |
| 675 | + for (auto const& out : mInputs) |
| 676 | + { |
| 677 | + // Increment first so the count is 1 based. |
| 678 | + ++iInputCount; |
| 679 | + |
| 680 | + if (fDebug) LogPrintf("INFO consolidateutxos: input value = %f, confirmations = %" PRId64, ((double) out.first) / (double) COIN, out.second.nDepth); |
| 681 | + |
| 682 | + setCoins.insert(make_pair(out.second.tx, out.second.i)); |
| 683 | + nValue += out.second.tx->vout[out.second.i].nValue; |
| 684 | + |
| 685 | + if (iInputCount == nInputNumberLimit || (nValue >= nConsolidateLimit && nConsolidateLimit != 0)) break; |
| 686 | + } |
| 687 | + |
| 688 | + // If number of inputs that meet criteria is less than two, then do nothing. |
| 689 | + if (iInputCount < 2) |
| 690 | + { |
| 691 | + result.pushKV("result", true); |
| 692 | + result.pushKV("UTXOs consolidated", (uint64_t) 0); |
| 693 | + |
| 694 | + return result; |
| 695 | + } |
| 696 | + |
| 697 | + CReserveKey reservekey(pwalletMain); |
| 698 | + |
| 699 | + |
| 700 | + // Fee calculation to avoid change. |
| 701 | + |
| 702 | + // Bytes - Assume two outputs (because there might be change for the time being). |
| 703 | + // ----------The inputs to the tx - The one output. |
| 704 | + int64_t nBytes = iInputCount * 148 + 34 + 10; |
| 705 | + |
| 706 | + // Min Fee |
| 707 | + int64_t nMinFee = txDummy.GetMinFee(1, GMF_SEND, nBytes); |
| 708 | + |
| 709 | + int64_t nFee = nTransactionFee * (1 + nBytes / 1000); |
| 710 | + |
| 711 | + int64_t nFeeRequired = max(nMinFee, nFee); |
| 712 | + |
| 713 | + |
| 714 | + if (pwalletMain->IsLocked()) |
| 715 | + { |
| 716 | + string strError = _("Error: Wallet locked, unable to create transaction."); |
| 717 | + LogPrintf("consolidateutxos: %s", strError); |
| 718 | + return strError; |
| 719 | + } |
| 720 | + |
| 721 | + if (fWalletUnlockStakingOnly) |
| 722 | + { |
| 723 | + string strError = _("Error: Wallet unlocked for staking only, unable to create transaction."); |
| 724 | + LogPrintf("consolidateutxos: %s", strError); |
| 725 | + return strError; |
| 726 | + } |
| 727 | + |
| 728 | + vector<pair<CScript, int64_t> > vecSend; |
| 729 | + |
| 730 | + // Reduce the out value for the transaction by nFeeRequired from the total of the inputs to provide a fee |
| 731 | + // to the staker. The fee has been calculated so that no change should be produced from the CreateTransaction |
| 732 | + // call. Just in case, the input address is specified as the return address via coincontrol. |
| 733 | + vecSend.push_back(std::make_pair(scriptDestPubKey, nValue - nFeeRequired)); |
| 734 | + |
| 735 | + CCoinControl* coinControl = new CCoinControl(); |
| 736 | + |
| 737 | + // Send the change back to the same address. |
| 738 | + coinControl->destChange = OptimizeAddress.Get(); |
| 739 | + |
| 740 | + if (!pwalletMain->CreateTransaction(vecSend, setCoins, wtxNew, reservekey, nFeeRequired, coinControl)) |
| 741 | + { |
| 742 | + string strError; |
| 743 | + if (nValue + nFeeRequired > pwalletMain->GetBalance()) |
| 744 | + strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired)); |
| 745 | + else |
| 746 | + strError = _("Error: Transaction creation failed "); |
| 747 | + LogPrintf("consolidateutxos: %s", strError); |
| 748 | + return strError; |
| 749 | + } |
| 750 | + |
| 751 | + if (!pwalletMain->CommitTransaction(wtxNew, reservekey)) |
| 752 | + return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); |
| 753 | + |
| 754 | + result.pushKV("result", true); |
| 755 | + result.pushKV("UTXOs consolidated", (uint64_t) iInputCount); |
| 756 | + result.pushKV("Output UTXO value", (double)(nValue - nFeeRequired) / COIN); |
| 757 | + |
| 758 | + return result; |
| 759 | +} |
| 760 | + |
607 | 761 |
|
608 | 762 | UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
609 | 763 | {
|
|
0 commit comments