From eb1ab5ce637c912ba6eb4cc90b022b8a9c6d55e0 Mon Sep 17 00:00:00 2001 From: IPCChain <34546443+IPCChain@users.noreply.github.com> Date: Sat, 27 Jan 2018 19:26:42 +0800 Subject: [PATCH] v0.1.0 initial release v0.1.0 initial release --- ...16de83f738c50524241c10a00c4f82573.svn-base | 3382 ++++++ ...06726db15638652e2a362759c94031b68.svn-base | 152 + ...bf077f8f1112e5af05a6ade89f0364dde.svn-base | 4641 ++++++++ ...89045b8aa886ebda3799f05b463689a0a.svn-base | 786 ++ ...d23b124f26dfa5abfb3db12f08dfbeb48.svn-base | 5494 ++++++++++ ...bf11e79990dd9a5c3cf8b0a2bf113dc8f.svn-base | 1396 +++ ...beb411430ed6d3fd7ac90bd9654fd0a05.svn-base | 287 + ...b602d535c70e2d941ef2de74a8f77e468.svn-base | 170 + ...b444b841d3b6600e17b2de9ac5a1c173c.svn-base | 385 + ...6c61aa2dafcc8b401bdd48133efcaa946.svn-base | 1805 ++++ ...b5c07319c1e6133c951889cc146cc6681.svn-base | 290 + ...24b815f93dc91118be087095aa060b5bf.svn-base | 624 ++ ...1422c4bc34b84ddf7a36f16b70d49d9e3.svn-base | 431 + ...5ba2895c7ab1a13e1c5d790bca81314ae.svn-base | 424 + ...df2e47e8f85b67a8544a9ce80d997a3f0.svn-base | 6669 ++++++++++++ ...e534d7c4b7605817dd9d2f71628b2cb52.svn-base | 186 + ...36944e87811c798f1c479acc1d6a540c7.svn-base | 881 ++ ...9db3148e1fbf6619a47a9f42f125e8693.svn-base | 246 + ...2740f226e5f27ac8ba1966992568b4a8d.svn-base | 293 + ...14646fd7de2dbe6ded919cc23c707aa79.svn-base | 1168 ++ ...a7305ea835f6fc682968f41d195393bce.svn-base | 622 ++ ...f270de3f5bb3823f6a86505f1798386d9.svn-base | 77 + ...e5d0a3b179b7807cf2b05064919b13101.svn-base | 1749 +++ ...73460651c36d76633c9987748a4bdb4f5.svn-base | 152 + ...49695a91c66d8aeec17c9e094b0168ea7.svn-base | 4644 ++++++++ ...faaff267d11788e0f9f7e977eba8b70af.svn-base | 347 + ...71bc1359d1e16adc3cc029bcc4638f109.svn-base | 43 + ...10fa89745f03102f90e0c3898c1bcec3c.svn-base | 287 + ...1dcb4c736f3e796af79f2fe0560517b4d.svn-base | 51 + ...c2e32ce4a4540a648af65b2e2f6c55c55.svn-base | 9426 +++++++++++++++++ ...e8eda68f01055b5f88841aacdc82c2205.svn-base | 616 ++ ...930ea772dc377b2178b3738895b18ec8b.svn-base | 383 + ...d85747119f316896f9a2314574e477666.svn-base | 229 + ...30d56f3a8e7e9ef5bb647b087eb28ad50.svn-base | 569 + ...3d99364328d561e50f934d121c35787d4.svn-base | 9426 +++++++++++++++++ ...ac1326ecb43d5913fd236927368019496.svn-base | 1185 +++ ...a3fc269ae1c424536bdbb84942ea4adde.svn-base | 833 ++ ...f9b421661bc4ecfa1c0d9f31c8d708d12.svn-base | 1410 +++ .svn/wc.db | Bin 1071104 -> 1090560 bytes src/Makefile.qt.include | 2 + src/chainparams.cpp | 15 +- src/dpoc/ConsensusAccountPool.cpp | 38 +- src/dpoc/TimeService.cpp | 22 +- src/init.cpp | 7 + src/policy/policy.cpp | 8 +- src/primitives/transaction.h | 6 +- src/qt/cmessagebox.cpp | 1 + src/qt/ecoincreatedialog.cpp | 37 +- src/qt/ecoinsendaffrimdialog.cpp | 12 +- src/qt/forms/ecoincreatedialog.ui | 2 +- src/qt/forms/ipcdetails.ui | 4 +- src/qt/intro.cpp | 5 + src/qt/ipcdetails.cpp | 1 + src/qt/locale/bitcoin_zh_CN.ts | 23 +- src/qt/log/log.cpp | 2 - src/qt/log/stateinfo.cpp | 152 + src/qt/log/stateinfo.h | 43 + src/qt/passwordsettingwidget.cpp | 13 +- src/qt/sendcoinsdialog.cpp | 2 + src/qt/tallyaccount.cpp | 18 + src/qt/tallyaccount.h | 1 + src/qt/tallyapply.cpp | 8 + src/qt/tallyoutaccount.cpp | 6 +- src/qt/transactiontablemodel.cpp | 2 +- src/qt/walletframe.cpp | 11 +- src/qt/walletframe.h | 2 - src/qt/walletview.cpp | 8 +- src/rpc/client.cpp | 1 + src/rpc/rawtransaction.cpp | 27 +- src/validation.cpp | 71 +- src/validation.h | 1 + src/wallet/rpcwallet.cpp | 110 +- src/wallet/wallet.cpp | 503 +- src/wallet/wallet.h | 14 +- 74 files changed, 62803 insertions(+), 134 deletions(-) create mode 100644 .svn/pristine/04/045ede016de83f738c50524241c10a00c4f82573.svn-base create mode 100644 .svn/pristine/05/058420406726db15638652e2a362759c94031b68.svn-base create mode 100644 .svn/pristine/09/097e8c1bf077f8f1112e5af05a6ade89f0364dde.svn-base create mode 100644 .svn/pristine/13/130602489045b8aa886ebda3799f05b463689a0a.svn-base create mode 100644 .svn/pristine/18/18b6086d23b124f26dfa5abfb3db12f08dfbeb48.svn-base create mode 100644 .svn/pristine/1e/1efc524bf11e79990dd9a5c3cf8b0a2bf113dc8f.svn-base create mode 100644 .svn/pristine/26/26d9e4bbeb411430ed6d3fd7ac90bd9654fd0a05.svn-base create mode 100644 .svn/pristine/2b/2b485e6b602d535c70e2d941ef2de74a8f77e468.svn-base create mode 100644 .svn/pristine/2f/2f250d8b444b841d3b6600e17b2de9ac5a1c173c.svn-base create mode 100644 .svn/pristine/33/33424596c61aa2dafcc8b401bdd48133efcaa946.svn-base create mode 100644 .svn/pristine/34/34d7be7b5c07319c1e6133c951889cc146cc6681.svn-base create mode 100644 .svn/pristine/37/37fd25524b815f93dc91118be087095aa060b5bf.svn-base create mode 100644 .svn/pristine/3a/3a44d6d1422c4bc34b84ddf7a36f16b70d49d9e3.svn-base create mode 100644 .svn/pristine/3c/3c945115ba2895c7ab1a13e1c5d790bca81314ae.svn-base create mode 100644 .svn/pristine/41/412b82edf2e47e8f85b67a8544a9ce80d997a3f0.svn-base create mode 100644 .svn/pristine/48/48131b9e534d7c4b7605817dd9d2f71628b2cb52.svn-base create mode 100644 .svn/pristine/4a/4a83c0836944e87811c798f1c479acc1d6a540c7.svn-base create mode 100644 .svn/pristine/50/50e676c9db3148e1fbf6619a47a9f42f125e8693.svn-base create mode 100644 .svn/pristine/58/58286b12740f226e5f27ac8ba1966992568b4a8d.svn-base create mode 100644 .svn/pristine/60/60d89ba14646fd7de2dbe6ded919cc23c707aa79.svn-base create mode 100644 .svn/pristine/62/625fcbfa7305ea835f6fc682968f41d195393bce.svn-base create mode 100644 .svn/pristine/66/66c2a68f270de3f5bb3823f6a86505f1798386d9.svn-base create mode 100644 .svn/pristine/71/71615e3e5d0a3b179b7807cf2b05064919b13101.svn-base create mode 100644 .svn/pristine/7d/7d0084573460651c36d76633c9987748a4bdb4f5.svn-base create mode 100644 .svn/pristine/84/84388d149695a91c66d8aeec17c9e094b0168ea7.svn-base create mode 100644 .svn/pristine/90/905924afaaff267d11788e0f9f7e977eba8b70af.svn-base create mode 100644 .svn/pristine/9b/9bc848f71bc1359d1e16adc3cc029bcc4638f109.svn-base create mode 100644 .svn/pristine/9f/9f4fe9010fa89745f03102f90e0c3898c1bcec3c.svn-base create mode 100644 .svn/pristine/a7/a7f95691dcb4c736f3e796af79f2fe0560517b4d.svn-base create mode 100644 .svn/pristine/cf/cf3e87ec2e32ce4a4540a648af65b2e2f6c55c55.svn-base create mode 100644 .svn/pristine/d2/d26bb67e8eda68f01055b5f88841aacdc82c2205.svn-base create mode 100644 .svn/pristine/d9/d92e7e6930ea772dc377b2178b3738895b18ec8b.svn-base create mode 100644 .svn/pristine/d9/d98d5b8d85747119f316896f9a2314574e477666.svn-base create mode 100644 .svn/pristine/da/dadc4fd30d56f3a8e7e9ef5bb647b087eb28ad50.svn-base create mode 100644 .svn/pristine/dc/dcd55cf3d99364328d561e50f934d121c35787d4.svn-base create mode 100644 .svn/pristine/de/de8aee2ac1326ecb43d5913fd236927368019496.svn-base create mode 100644 .svn/pristine/df/dfd7921a3fc269ae1c424536bdbb84942ea4adde.svn-base create mode 100644 .svn/pristine/f0/f0f18f5f9b421661bc4ecfa1c0d9f31c8d708d12.svn-base create mode 100644 src/qt/log/stateinfo.cpp create mode 100644 src/qt/log/stateinfo.h diff --git a/.svn/pristine/04/045ede016de83f738c50524241c10a00c4f82573.svn-base b/.svn/pristine/04/045ede016de83f738c50524241c10a00c4f82573.svn-base new file mode 100644 index 0000000..33500c3 --- /dev/null +++ b/.svn/pristine/04/045ede016de83f738c50524241c10a00c4f82573.svn-base @@ -0,0 +1,3382 @@ + +#include +#include +#include "../util.h" +#include "../chain.h" +#include "../sync.h" +#include "../validation.h" +#include "base58.h" +#include "uint256.h" +#include "chainparams.h" +#include "protocol.h" +#include "wallet/wallet.h" +#include "net.h" +#include "consensus/validation.h" +#include "univalue.h" +#include "ConsensusAccountPool.h" +#include "DpocInfo.h" +#include "DpocMining.h" +#include "TimeService.h" +#include "SerializeDpoc.h" +#include +#include "wallet/wallet.h" + +extern CWallet* pwalletMain; +boost::shared_mutex checkmutex; + +SnapshotClass::SnapshotClass() { + pkHashIndex = 0; + blockHeight = 0; + timestamp = 0; + meetingstarttime = 0; + meetingstoptime = 0; + blockTime = 0; + + curCandidateIndexList.clear(); + curSeriousPunishIndexAdded.clear(); + curTimeoutIndexRecord.clear(); + curRefundIndexList.clear(); + cachedIndexsToRefund.clear(); + curTimeoutPunishList.clear(); + cachedTimeoutPunishToRun.clear(); + cachedMeetingAccounts.clear(); + curBanList.clear(); + cachedBanList.clear(); +} +SnapshotClass::SnapshotClass(const SnapshotClass& in){ + timestamp = in.timestamp; + blockHeight = in.blockHeight; + meetingstarttime = in.meetingstarttime; + meetingstoptime = in.meetingstoptime; + blockTime = in.blockTime; + pkHashIndex = in.pkHashIndex; + curCandidateIndexList = in.curCandidateIndexList; + curSeriousPunishIndexAdded = in.curSeriousPunishIndexAdded; + curTimeoutIndexRecord = in.curTimeoutIndexRecord; + curTimeoutPunishList = in.curTimeoutPunishList; + cachedTimeoutPunishToRun = in.cachedTimeoutPunishToRun; + curRefundIndexList = in.curRefundIndexList; + cachedIndexsToRefund = in.cachedIndexsToRefund; + cachedMeetingAccounts = in.cachedMeetingAccounts; + curBanList = in.curBanList; + cachedBanList = in.cachedBanList; +} + + +void CConsensusAccountPool::printsets(std::set list) +{ + if (g_bStdCout) + { + std::cout << "--------------------list--------------------" << std::endl; + } + LogPrintf("--------------------list--------------------\n"); + std::set::iterator it; + for (it = list.begin(); it != list.end(); it++) { + if (*it == 0) + { + continue; + } + if (g_bStdCout) + { + if (g_bStdCout) + { + std::cout << *it << " "; + } + } + LogPrintf("%d ", *it); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); +} + +void CConsensusAccountPool::printsnapshots(std::vector list) +{ + if(!fPrintToDebugLog) + return; + if (g_bStdCout) + { + std::cout << "--------------------check snapshots--------------------" << std::endl; + } + std::set::iterator it; + std::map::iterator mapit; + std::vector>::iterator meetingit; + + if (g_bStdCout) + { + std::cout << "Current Candidate Index List:" << std::endl; + } + LogPrintf("Current Candidate Index List:\n"); + for (int i=0; ifirst << "-" << mapit->second << " "; + } + LogPrintf("%d-%d ", mapit->first, mapit->second); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + + if (g_bStdCout) + { + std::cout << "New Timeout Punishment Added:" << std::endl; + } + LogPrintf("New Timeout Punishment Added:\n"); + for (int i = 0; i < list.size(); i++) + { + if (g_bStdCout) + { + std::cout << list.at(i).blockHeight << " | " << list.at(i).pkHashIndex << " | " << list.at(i).blockTime << " | "; + } + LogPrintf("%d |%03d |%d |", list.at(i).blockHeight, list.at(i).pkHashIndex, list.at(i).blockTime); + for (it = list.at(i).curTimeoutPunishList.begin(); it != list.at(i).curTimeoutPunishList.end(); it++) { + if (g_bStdCout) + { + std::cout << *it << " "; + } + LogPrintf("%d ", *it); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + + if (g_bStdCout) + { + std::cout << "Cached Candidate Index To Timeout Punishment List:" << std::endl; + } + LogPrintf("Cached Candidate Index To Timeout Punishment List:\n"); + for (int i = 0; i < list.size(); i++) + { + if (g_bStdCout) + { + std::cout << list.at(i).blockHeight << " | " << list.at(i).pkHashIndex << " | " << list.at(i).blockTime << " | "; + } + LogPrintf("%d |%03d |%d |", list.at(i).blockHeight, list.at(i).pkHashIndex, list.at(i).blockTime); + for (it = list.at(i).cachedTimeoutPunishToRun.begin(); it != list.at(i).cachedTimeoutPunishToRun.end(); it++) { + if (g_bStdCout) + { + std::cout << *it << " "; + } + LogPrintf("%d ", *it); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + + if (g_bStdCout) + { + std::cout << "New Ban:" << std::endl; + } + LogPrintf("New Ban :\n"); + for (int i = 0; i < list.size(); i++) + { + if (g_bStdCout) + { + std::cout << list.at(i).blockHeight << " | " << list.at(i).pkHashIndex << " | " << list.at(i).blockTime << " | "; + } + LogPrintf("%d |%03d |%d |", list.at(i).blockHeight, list.at(i).pkHashIndex, list.at(i).blockTime); + for (it = list.at(i).curBanList.begin(); it != list.at(i).curBanList.end(); it++) { + if (g_bStdCout) + { + std::cout << *it << " "; + } + LogPrintf("%d ", *it); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + + if (g_bStdCout) + { + std::cout << "Cached Ban List:" << std::endl; + } + LogPrintf("Cached Ban List:\n"); + for (int i = 0; i < list.size(); i++) + { + if (g_bStdCout) + { + std::cout << list.at(i).blockHeight << " | " << list.at(i).pkHashIndex << " | " << list.at(i).blockTime << " | "; + } + LogPrintf("%d |%03d |%d |", list.at(i).blockHeight, list.at(i).pkHashIndex, list.at(i).blockTime); + for (it = list.at(i).cachedBanList.begin(); it != list.at(i).cachedBanList.end(); it++) { + if (g_bStdCout) + { + std::cout << *it << " "; + } + LogPrintf("%d ", *it); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + } + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + + if (g_bStdCout) + { + std::cout << "Meeting Time:" << std::endl; + } + LogPrintf("Meeting Time:\n"); + for (int i = 0; i < list.size(); i++) + { + if (g_bStdCout) + { + std::cout << list.at(i).blockHeight << " | " << list.at(i).pkHashIndex << " | " << list.at(i).blockTime << " | "; + std::cout << list.at(i).meetingstarttime << " " << list.at(i).meetingstoptime; + } + LogPrintf("%d |%03d |%d |", list.at(i).blockHeight, list.at(i).pkHashIndex, list.at(i).blockTime); + LogPrintf("%u %u", + list.at(i).meetingstarttime, list.at(i).meetingstoptime); + + if (g_bStdCout) + { + std::cout << std::endl; + } + LogPrintf("\n"); + } + if (g_bStdCout) + { + std::cout << std::endl; + std::cout << "--------------------finish--------------------" << std::endl; + std::cout << std::endl; + } + LogPrintf("--------------------finish--------------------\n\n"); + +} + +CConsensusAccountPool* CConsensusAccountPool::_instance = nullptr; +std::once_flag CConsensusAccountPool::init_flag; + +CConsensusAccountPool::CConsensusAccountPool() +{ + snapshotlist.clear(); + m_mapSnapshotIndex.clear(); + candidatelist.clear(); + tmpcachedIndexsToRefund.clear(); + tmpcachedTimeoutToPunish.clear(); + verifysuccessed = false; + analysisfinished = false; + m_strSnapshotPath = std::string("Snapshot"); + m_strSnapshotIndexPath = std::string("SnapshotIndex"); + m_strSnapshotRevPath = std::string("SnapshotRev"); + m_strCandidatelistPath = std::string("Candidatelist"); + m_u32WriteHeight = 0; + + bReboot = false; + + + if(Params().NetworkIDString() == CBaseChainParams::MAIN) + { + trustPKHashList.push_back("1f324deede30e9f3ae61addbcaed3e2246ed9e77"); + trustPKHashList.push_back("ac4b6c5565ec567102cc666f8efef635546b3a3c"); + trustPKHashList.push_back("24edef7392b8264db505aa709161a9b733cf1153"); + trustPKHashList.push_back("2656438e6581fb298231b398c54b090fa1e7765f"); + trustPKHashList.push_back("55ca5cf8e8381e11e3fb155faf546b60cf966a81"); + + trustPKHashList.push_back("8f13e80390ce115e6523b80ebab2d07065c40202"); + trustPKHashList.push_back("03cc22fa2ff3ab748a90c2478d3267e9869e8969"); + trustPKHashList.push_back("9afc6a2110620348b4f427e6c20609e14234ad18"); + trustPKHashList.push_back("115cfeac3fd7c1a0288c771cada4831d77a7b11b"); + trustPKHashList.push_back("f76dfdccd2487d60f449a7676a67eacd5adefe92"); + + trustPKHashList.push_back("3bfb0bfc964a138b2f68133911aa5cdabfc5942c"); + trustPKHashList.push_back("19fa2c073142290358243a1c244f170bbbe2aea5"); + trustPKHashList.push_back("9afdfb65154bb930045a7be2c1fedfb957de69f3"); + trustPKHashList.push_back("a7b0231277f4e9e3b8d48efb417ddf5203a9c86a"); + trustPKHashList.push_back("2426551e1fd1ff6d440a8bfdb519803f53175696"); + + trustPKHashList.push_back("f057fed2179897f1dc21e3078882d719c543cbe5"); + trustPKHashList.push_back("7ee049deb78f0768cf13ebc6560a5eb1fd98f509"); + trustPKHashList.push_back("6d19330318e9f5a88175fadff7abd07064c149bc"); + trustPKHashList.push_back("37b3b9813f91ccdb293270c78dc1ddf71dd7d95b"); + trustPKHashList.push_back("c2b862e0809808dc97efda21c1e2bca62b1b4f26"); + } + else if (Params().NetworkIDString() == CBaseChainParams::TESTNET) + { + //trustPKHashList.push_back("887f8428ddb84e05d3d32b86fb5a15a4d4bf9a0e"); + //trustPKHashList.push_back("3560c2305ad91fa6ce64176e6f7b121205a396eb"); + //trustPKHashList.push_back("e164855857c576b5963b385cc2a1ee33fb949feb"); + //TestChain + trustPKHashList.push_back("e34450364c0b7569162998f7c0082f880df76946"); + trustPKHashList.push_back("e16d04b1cf614a3c897c30ed97fd21f11b017161"); + trustPKHashList.push_back("c29622b296bcf805f37846c6318f6f3425742063"); + trustPKHashList.push_back("942b392a5564796039990752062a91b6487e6c96"); + trustPKHashList.push_back("6e131a09afa65805f736c084ef79e9be7d413556"); + + trustPKHashList.push_back("40b27001c168cb8e5dfbb7999340d930eb8369d5"); + trustPKHashList.push_back("c725507037d6a7a3e0bb7dab582670abd68c0559"); + trustPKHashList.push_back("75e963dca9a292607c26c3d6a04d218ed986ec1b"); + trustPKHashList.push_back("01015f28af241b0493ed8aa50a91aed48abb0c3b"); + trustPKHashList.push_back("cf96d36126e6962769e1883d20465e4b4ceaab85"); + + trustPKHashList.push_back("48b8e2475b4d949254af74d257afb9b35fdb9bb4"); + trustPKHashList.push_back("1e5f5feb1590609cf1f4dd2ca134449a201140e6"); + trustPKHashList.push_back("02bef0749efd62e672a82188e1ddeb78b7ad417f"); + trustPKHashList.push_back("9e2f2490fbce7d073c98031af5a62c7ed6dfa281"); + trustPKHashList.push_back("d58124f372db3a195a85eb47f35c50ca31fb8b46"); + + trustPKHashList.push_back("5db186c3b09c1139d99dc7a8fe1d2cec436c036f"); + trustPKHashList.push_back("923cd7662c415bcc852fbecd0eb550a8192ea432"); + trustPKHashList.push_back("ea565638fb61dcbb0e5f4f1b7384044b17431674"); + trustPKHashList.push_back("7852d6b46fdfbb1a879057ead698190695944757"); + trustPKHashList.push_back("2358401fd6ec7774033253684c85bd3e2d188f66"); + } + + m_u64SnapshotIndexSize = getCSnapshotIndexSize(); + m_u64ConsensAccountSize = getConsensAccountSize(); +} + + CConsensusAccountPool::~CConsensusAccountPool() +{ +} + +void CConsensusAccountPool::CreateInstance() { + static CConsensusAccountPool instance; + CConsensusAccountPool::_instance = &instance; + } + +CConsensusAccountPool& CConsensusAccountPool::Instance() +{ + std::call_once(CConsensusAccountPool::init_flag, CConsensusAccountPool::CreateInstance); + return *CConsensusAccountPool::_instance; +} + +bool CConsensusAccountPool::getPublicKeyFromBlock(const CBlock *pblock, CPubKey& outPubkey,std::vector& outRecvSign) +{ + CTransactionRef coinbase = pblock->vtx[0]; + std::string strSignatrue = coinbase->vout[0].GetCheckBlockContent(); + if (strSignatrue.length() < 50) { + return false; + } + + std::vector vchReceive; + if (!DecodeBase58(strSignatrue, vchReceive)) { + if (g_bStdCout) + { + std::cout << "[getPublicKeyFromBlock] DecodeBase58 vchReceive is err =" << strSignatrue.length() << std::endl; + } + return false; + } + + uint32_t siglen = vchReceive[0]; + uint32_t sigPubkey = vchReceive[1 + siglen]; + + //Get the signature + std::vector recvSign; + recvSign.resize(siglen); + for (std::vector::size_type ix = 0; ix < siglen; ++ix) { + recvSign[ix] = vchReceive[ix + 1]; + } + + std::vector recvPublickey; + recvPublickey.resize(sigPubkey); + for (std::vector::size_type ix = 0; ix < sigPubkey; ++ix) { + recvPublickey[ix] = vchReceive[ix + 2 + siglen]; + } + + CPubKey PublicKeyRevc(recvPublickey); + outPubkey = PublicKeyRevc; + outRecvSign.swap(recvSign); + return true; +} + +bool CConsensusAccountPool::getPublicKeyFromSignstring(const std::string signstr, CPubKey& outPubkey, + std::vector& outRecvSign) +{ + if (signstr.length() < 50) { + return false; + } + + std::vector vchReceive; + if (!DecodeBase58(signstr, vchReceive)) { + if (g_bStdCout) + { + std::cout << "[getPublicKeyFromSignstring] DecodeBase58 vchReceive is err ===" << signstr.length() << std::endl; + } + return false; + } + + uint32_t siglen = vchReceive[0]; + uint32_t sigPubkey = vchReceive[1 + siglen]; + + std::vector recvSign; + recvSign.resize(siglen); + for (std::vector::size_type ix = 0; ix < siglen; ++ix) { + recvSign[ix] = vchReceive[ix + 1]; + } + + std::vector recvPublickey; + recvPublickey.resize(sigPubkey); + for (std::vector::size_type ix = 0; ix < sigPubkey; ++ix) { + recvPublickey[ix] = vchReceive[ix + 2 + siglen]; + } + + CPubKey PublicKeyRevc(recvPublickey); + outPubkey = PublicKeyRevc; + outRecvSign.swap(recvSign); + return true; +} + +bool CConsensusAccountPool::verifyDPOCTx(const CTransaction& tx, DPOC_errtype &errorType) +{ + SnapshotClass lastsnapshot; + if (!GetLastSnapshot(lastsnapshot)) + { + if (g_bStdCout) + { + std::cout << "[CConsensusAccountPool::verifyDPOCTx] Get the last snapshot to fail\n"; + } + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] Get the last snapshot to fail\n"); + errorType = GET_LAST_SNAPSHOT_FAILED; + return false; + } + + if (tx.GetCampaignType() == TYPE_CONSENSUS_REGISTER) + { + //Apply to join the transaction to get the public key HASH value + uint160 curHash = tx.GetCampaignPublickey(); + CAmount ipcvalue = tx.GetRegisterIPC(); + + //If the transaction amount is not sufficient, it is rejected + if (ipcvalue < Params().MIN_DEPOSI) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The deposit %d Insufficient, set value %d\n", ipcvalue, Params().MIN_DEPOSI); + errorType = JOIN_TRANS_DEPOSI_TOO_SMALL; + return false; + } + + //Look for subscript ID + uint16_t pkhashindex; + if (!ContainPK(curHash, pkhashindex)) + { + //The candidate list does not include the hash of the campaign, and returns true directly + return true; + } + + //If you are already in the current list of candidates, you are not allowed to join again + //If a refund is being processed, the refund transaction cannot be rejoined + if (lastsnapshot.curCandidateIndexList.count(pkhashindex)) + { + std::set::iterator iter = lastsnapshot.curCandidateIndexList.begin(); + for (;iter!= lastsnapshot.curCandidateIndexList.end();++iter) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] publickey HASH %d",*iter); + } + + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The public key HASH is already in the current candidate list %d++%d\n", pkhashindex,lastsnapshot.blockHeight); + errorType = JOIN_PUBKEY_ALREADY_EXIST_IN_LIST; + return false; + } + else if (lastsnapshot.curBanList.count(pkhashindex) || + lastsnapshot.cachedBanList.count(pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The current public key HASH is in the blacklist list\n"); + errorType = JOIN_PUBKEY_IS_BANNED; + return false; + } + else if (lastsnapshot.cachedTimeoutPunishToRun.count(pkhashindex) || + lastsnapshot.curTimeoutPunishList.count(pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The current public key HASH is being punished with a timeout\n"); + errorType = JOIN_PUBKEY_IS_TIMEOUT_PUNISHED; + return false; + } + else if (lastsnapshot.cachedIndexsToRefund.count(pkhashindex) || + lastsnapshot.curRefundIndexList.count(pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The current public key HASH is performing a refund operation\n"); + errorType = JOIN_PUBKEY_IS_DEPOSING; + return false; + } + else + { + //If a refund is to be performed in a snapshot of the cache block length, it is not allowed to join again + uint32_t cachedHeight = lastsnapshot.blockHeight - CACHED_BLOCK_COUNT; + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] Prepare to get the block height between %d-%d cache\n", cachedHeight, lastsnapshot.blockHeight); + std::vector cachedSnapshotList; + if (!GetSnapshotsByHeight(cachedSnapshotList, cachedHeight)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The list of cached snapshots failed!\n"); + errorType = GET_CACHED_SNAPSHOT_FAILED; + return false; + } + + std::vector::iterator pSnapshot; + + for (pSnapshot = cachedSnapshotList.begin(); pSnapshot != cachedSnapshotList.end(); pSnapshot++) + { + if ((*pSnapshot).curTimeoutPunishList.count(pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The current public key HASH is being punished with a timeout\n"); + errorType = EXIT_PUBKEY_IS_TIMEOUT_PUNISHED; + return false; + } + if ((*pSnapshot).curRefundIndexList.count(pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The current public key HASH is performing a refund operation\n"); + errorType = EXIT_PUBKEY_IS_DEPOSING; + return false; + } + } + } + } + else if (tx.GetCampaignType() == TYPE_CONSENSUS_QUITE) + { + if (g_bStdCout) + { + std::cout << "verify exit campaign" << std::endl; + } + + uint160 curHash = tx.GetCampaignPublickey(); + uint16_t pkhashindex; + if (!ContainPK(curHash, pkhashindex)) + { + //The record doesn't have this public key hash, obviously you can't get out of it + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The public key HASH is not in the index vector\n"); + errorType = EXIT_UNKNOWN_PUBKEY; + return false; + } + + //If not in the current list of candidates, do not let exit + if (!lastsnapshot.curCandidateIndexList.count(pkhashindex)) + { + printsets(lastsnapshot.curCandidateIndexList); + + LogPrintf("[CConsensusAccountPool::verifyDPOCTx] The public key HASH is not in the current candidate list\n"); + errorType = EXIT_PUBKEY_NOT_EXIST_IN_LIST; + return false; + } + } + + return true; +} + +bool CConsensusAccountPool::checkNewBlock(const std::shared_ptr pblock, uint160 &pubkeyHash, uint32_t blockHeight, DPOC_errtype &errorType) +{ + LogPrintf("[CConsensusAccountPool::checkNewBlock] INBLOCK nPeriodStartTime %d, nPeriodCount %d, nTimePeriod %d, blockTime %d height=%d\n", + pblock->nPeriodStartTime, pblock->nPeriodCount, pblock->nTimePeriod, pblock->nTime, blockHeight); + + SnapshotClass lastsnapshot; + if (!GetLastSnapshot(lastsnapshot)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] Gets a snapshot to record the tail block failed\n"); + return false; + } + + if (blockHeight > lastsnapshot.blockHeight +1) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The incoming block is too new to no verify\n"); + errorType = BLOCK_TOO_NEW_FOR_SNAPSHOT; + return false; + } + + //If the current public key is on the blacklist, reject the block + uint16_t pkindex; + if (!ContainPK(pubkeyHash, pkindex)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] Packaging public key unknown, reject\n"); + errorType = UNKNOWN_PACKAGE_PUBKEY; + return false; + } + + //Time to check + int64_t calcTime = (pblock->nPeriodStartTime + (pblock->nTimePeriod + 1) * BLOCK_GEN_TIME) / 1000; + LogPrintf("[CConsensusAccountPool::checkNewBlock] The current calculation should be packaged time=%d, The last block in the snapshot should be packaged Time=%d\n", calcTime, lastsnapshot.timestamp); + if (calcTime < (lastsnapshot.timestamp + BLOCK_GEN_TIME/1000 )) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The last block gap in the current calculation is less than the last one in the snapshot %d ms,Continuous block, severe punishment!\n", BLOCK_GEN_TIME); + return false; + } + + if (pblock->nTime != calcTime) { + LogPrintf("[CConsensusAccountPool::checkNewBlock] pblock->nTime != calcTime\n"); + + if ((pblock->nTime - calcTime < 0) &&(lastsnapshot.blockHeight >= Params().CHECK_START_BLOCKCOUNT)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] Packaging time is earlier than computation time,Or the time difference between the package time and the computation time is greater than %d seconds\n", MAX_BLOCK_TIME_DIFF); + errorType = PUNISH_BLOCK; + return false; + } + else if ((pblock->nTime - calcTime > MAX_BLOCK_TIME_DIFF) &&(lastsnapshot.blockHeight >= Params().CHECK_START_BLOCKCOUNT)) + { //It's possible to have a block delay + LogPrintf("[CConsensusAccountPool::checkNewBlock] The packaging time is earlier than the calculation time, or the time difference between the packaging time and the computation time is greater than %d seconds\n", MAX_BLOCK_TIME_DIFF); + return false; + } + } + + //Look for a snapshot of the current session's start time + int64_t curStarttime = pblock->nPeriodStartTime / 1000; + SnapshotClass meetingStartSnapshot; + if (!GetSnapshotByTime(meetingStartSnapshot, curStarttime)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] A snapshot of the start time of this session failed\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::checkNewBlock] current start time %d ,block height in snapshot %d\n", + curStarttime, meetingStartSnapshot.blockHeight); + + if (meetingStartSnapshot.blockHeight == 0 && meetingStartSnapshot.timestamp > curStarttime) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The current block time is earlier than the creation block, error!\n"); + errorType = PUNISH_BLOCK; + return false; + } + + if (!bReboot) + { + //the time doesn't match, it could be the missing block or the wrong block + if (meetingStartSnapshot.timestamp != curStarttime + && meetingStartSnapshot.blockHeight >= Params().CHECK_START_BLOCKCOUNT) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The current block start time is not consistent with the snapshot packaging time found at that time,You need to verify that the block is missing。curstarttime=%d,snapshot time=%d\n", + curStarttime, meetingStartSnapshot.timestamp); + + //Case handling: ignore the possibility of missing a full round + LogPrintf("[CConsensusAccountPool::checkNewBlock] Snapshot meeting starttime=%d, endtime=%d\n", + meetingStartSnapshot.meetingstarttime, meetingStartSnapshot.meetingstoptime); + + //Determines whether the end time of the last block found is the start time of the current block + //If it is, the block that is missing in the middle is cross-round, and the block is still valid + if (meetingStartSnapshot.meetingstoptime != pblock->nPeriodStartTime) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The end of the session of the snapshot does not equal the start time of the current block,There may be more than one round,error\n"); + return false; + } + LogPrintf("[CConsensusAccountPool::checkNewBlock] The end of the session of the snapshot is equal to the starting time of the current block meeting, with the phenomenon of cross-wheel leakage\n"); + } + } + + //find the cache block + + int foundcachedcount = CACHED_BLOCK_COUNT; + if (meetingStartSnapshot.blockHeight < Params().CHECK_START_BLOCKCOUNT) + { + foundcachedcount = meetingStartSnapshot.blockHeight < CACHED_BLOCK_COUNT ? meetingStartSnapshot.blockHeight : CACHED_BLOCK_COUNT; + } + uint32_t cachedTime = (meetingStartSnapshot.meetingstoptime - foundcachedcount * BLOCK_GEN_TIME) / 1000; + LogPrintf("[CConsensusAccountPool::checkNewBlock] Calculate the cache time is %d \n", cachedTime); + + //The consensus of this round + SnapshotClass cachedsnapshot; + if (!GetSnapshotByTime(cachedsnapshot, cachedTime)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The cache time corresponds to a snapshot failure, and the block information is problematic\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::checkNewBlock] find block height = %d , find block time= %d \n", + cachedsnapshot.blockHeight, cachedsnapshot.timestamp); + + std::set accounts = cachedsnapshot.curCandidateIndexList; + + printsets(accounts); + if (accounts.size() != pblock->nPeriodCount && cachedsnapshot.curCandidateIndexList.size() != pblock->nPeriodCount) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The number of meetings recorded in the block is inconsistent with the number of meetings in the snapshot,PeriodCount=%d,cachedsnapshot PeriodCount=%d,The number of people removed from the blacklist=%d \n", + pblock->nPeriodCount,cachedsnapshot.curCandidateIndexList.size(), accounts.size()); + /* + if (Params().NetworkIDString() == CBaseChainParams::TESTNET) + { + if (blockHeight >= 32050 && blockHeight <= 32070) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] ignore the blockheight %d check \n",blockHeight); + } + else{ + return false; + } + } + else + */ + return false; + } + + std::list> consensusList; + consensusList.clear(); + std::set::iterator candidateit; + for (candidateit= accounts.begin(); candidateit != accounts.end(); candidateit++) + { + std::shared_ptr tmpaccount = std::make_shared(candidatelist.at(*candidateit)); + consensusList.push_back(tmpaccount); + } + + uint64_t meetingstarttime = 0; + if (!bReboot) + { + meetingstarttime = meetingStartSnapshot.meetingstoptime; + } + else + { + meetingstarttime = pblock->nPeriodStartTime; + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] reboot meetingstarttime =%d", pblock->nPeriodStartTime); + bReboot = false; + } + //bool result = CDpocMining::Instance().GetMeetingList(meetingstarttime, consensusList); + //if (result) + if (CDpocMining::Instance().GetMeetingList(meetingstarttime, consensusList)) + { + //Print the public key HASH index value of this round sequentially + //LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] startime %d meeting pockager sort:\n", pblock->nPeriodStartTime); + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] meetingstarttime=%d blockstartime=%d meeting pockager sort:\n", meetingstarttime, pblock->nPeriodStartTime); + std::list>::iterator consensusListIt; + for (consensusListIt = consensusList.begin(); consensusListIt != consensusList.end(); consensusListIt++) + { + uint16_t tmpIndex = -1; + uint160 tmppkhash = (*consensusListIt)->getPubicKey160hash(); + ContainPK(tmppkhash, tmpIndex); + LogPrintf("%d ", tmpIndex); + } + LogPrintf("\n\n"); + + uint160 correctpubkeyhash; + uint16_t correctpkhashindex; + if (!getPKIndexBySortedIndex(correctpkhashindex, correctpubkeyhash, consensusList, pblock->nTimePeriod)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] The current block public key failed from the sorting list and block ordinal\n"); + return false; + } + + if (pubkeyHash != correctpubkeyhash) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] packager public key HASH don't match,in block PKHash=%s,snapshot PKHash=%s\n", + pubkeyHash.GetHex().c_str(), correctpubkeyhash.GetHex().c_str()); + return false; + } + } + else + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] Get a list of meeting collation failures\n"); + return false; + } + + //Check whether the HASH is consistent with the front-block HASH corresponding to the tail of the snapshot + if (!mapBlockIndex.count(pblock->GetBlockHeader().hashPrevBlock)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] There is no prefix HASH for this block in the current list %s \n", pblock->GetBlockHeader().hashPrevBlock.GetHex().c_str()); + + return false; + } + CBlockIndex* pblockindex = mapBlockIndex[pblock->GetBlockHeader().hashPrevBlock]; + if (pblockindex->nHeight != lastsnapshot.blockHeight) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] this block prevHASH %s ,prevblock height %d, snapshot tail height %d ,no match\n", + pblock->GetBlockHeader().hashPrevBlock.GetHex().c_str(), pblockindex->nHeight, lastsnapshot.blockHeight); + + return false; + } + + if (g_bStdCout) + { + std::cout << "checkNewBlock exit" << std::endl; + } + return true; +} + +bool CConsensusAccountPool::verifyDPOCBlock(const std::shared_ptr pblock, uint32_t blockHeight, DPOC_errtype &rejectreason) +{ + writeLock wtlock(checkmutex); + + CPubKey recvPublickey; + std::vector recvSign; + if (!getPublicKeyFromBlock(pblock.get(), recvPublickey, recvSign)) { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock()] getPublicKeyFromBlock is badlly is MisbeHaving\n"); + + rejectreason = PUNISH_BLOCK; + return false; + } + CKeyID pubicKey160hash = recvPublickey.GetID(); + + //time check。testnet The first 120 blocks are slightly over, and the main network is checked from the first block + if (blockHeight >= Params().CHECK_START_BLOCKCOUNT) + { + //Verify that the hash in the signature is in the consensus list of the block + if (!verifyPkInCandidateList(pblock, pubicKey160hash)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock()] The hash in the signature is not in the consensus list of the block \n"); + return false; + } + + uint256 hash; + CAmount nfee = pblock->vtx[0]->GetValueOut(); + CHash256 hashoperator; + + hashoperator.Write((unsigned char*)&(pblock->nPeriodStartTime), sizeof(pblock->nPeriodStartTime));//8Byte + hashoperator.Write((unsigned char*)&(pblock->nPeriodCount), sizeof(pblock->nPeriodCount));//4Byte + hashoperator.Write((unsigned char*)&(pblock->nTimePeriod), sizeof(pblock->nTimePeriod));//4Byte + hashoperator.Write((unsigned char*)&(pblock->nTime), sizeof(pblock->nTime));//4Byte + hashoperator.Write((unsigned char*)&nfee, sizeof(nfee));//8Byte + hashoperator.Write((unsigned char*)pblock->GetBlockHeader().hashPrevBlock.begin(), 32);//prevhash, 256bit + hashoperator.Finalize(hash.begin()); + + unsigned char dataBuff[10240]; + int datalen = 0; + memcpy(dataBuff, (unsigned char*)&(pblock->nPeriodStartTime), sizeof(pblock->nPeriodStartTime)); + datalen += sizeof(pblock->nPeriodStartTime); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nPeriodCount), sizeof(pblock->nPeriodCount)); + datalen += sizeof(pblock->nPeriodCount); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nTimePeriod), sizeof(pblock->nTimePeriod)); + datalen += sizeof(pblock->nTimePeriod); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nTime), sizeof(pblock->nTime)); + datalen += sizeof(pblock->nTime); + memcpy(dataBuff + datalen, (unsigned char*)&nfee, sizeof(nfee)); + datalen += sizeof(nfee); + memcpy(dataBuff + datalen, (unsigned char*)pblock->GetBlockHeader().hashPrevBlock.begin(), 32);//256 bits + datalen += 32; + std::string datahex; + for (int i = 0; i < datalen; i++) + { + char tmp[3]; + sprintf(tmp, "%02x", dataBuff[i]); + datahex = datahex + std::string(tmp); + } + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock()] clear=%s , clear HASH=%s , publickey HASH=%s\n", datahex.c_str(), hash.GetHex().c_str(), pubicKey160hash.GetHex().c_str()); + + if (recvPublickey.Verify(hash, recvSign)) { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock()] recvPublickey.Verify recvSign is OK\n"); + } + else + { + if (g_bStdCout) + { + std::cout << "CConsensusAccountPool::verifyDPOCBlock(): Verify block sign failed" << std::endl; + } + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] block head info check faile!The block is forged and unable to verify identity\n"); + + rejectreason = PUNISH_BLOCK; + return false; + } + + //reboot + if (REBOOTLABEL == pblock->nNonce) + { + if (verifyPkIsTrustNode(pubicKey160hash)) + { + bReboot = true; + } + else + { + return false; + } + } + + if (!checkNewBlock(pblock, pubicKey160hash, blockHeight, rejectreason)) { + if (g_bStdCout) + { + std::cout << "Block Check Failed" << std::endl; + } + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] checkNewBlock faile\n"); + return false; + } + } + + tmpcachedIndexsToRefund.clear(); + tmpcachedTimeoutToPunish.clear(); + verifysuccessed = false; + + SnapshotClass lastSnapshot; + if (GetLastSnapshot(lastSnapshot)) + { + tmpcachedIndexsToRefund = lastSnapshot.cachedIndexsToRefund; + tmpcachedTimeoutToPunish = lastSnapshot.cachedTimeoutPunishToRun; + + uint32_t cachedHeight = (lastSnapshot.blockHeight > CACHED_BLOCK_COUNT)?(lastSnapshot.blockHeight-CACHED_BLOCK_COUNT):0; + SnapshotClass cachedSnapshot; + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] get cache snapshot height is %d\n", cachedHeight); + if (!GetSnapshotByHeight(cachedSnapshot, cachedHeight)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] to get cache snapshot faile\n"); + return false; + } + std::set::iterator it; + for (it = cachedSnapshot.curRefundIndexList.begin(); it != cachedSnapshot.curRefundIndexList.end(); it++) + { + if (!tmpcachedIndexsToRefund.count(*it)) + tmpcachedIndexsToRefund.insert(*it); + } + + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock]The currently saved temporary list of waiting refunds is:\n"); + printsets(tmpcachedIndexsToRefund); + + for (it = cachedSnapshot.curTimeoutPunishList.begin(); it != cachedSnapshot.curTimeoutPunishList.end(); it++) + { + if (!tmpcachedTimeoutToPunish.count(*it)) + tmpcachedTimeoutToPunish.insert(*it); + } + + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock]The temporary list of the currently saved prepared execution timeout penalties is:\n"); + printsets(tmpcachedTimeoutToPunish); + + for (const auto& tx : pblock->vtx) + { + if (tx->IsCoinBase()) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] check Coinbase tx\n"); + + for (const CTxOut& txout : tx->vout) + { + if ((txout.txType != TXOUT_NORMAL) && (txout.txType != TXOUT_CAMPAIGN)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock()] coinbase TX has wrong Type reurn false and MisbeHaving\n"); + + rejectreason = PUNISH_BLOCK; + return false; + } + + if (txout.GetCampaignType() == TYPE_CONSENSUS_RETURN_DEPOSI) + { + uint16_t pkindex; + if (!ContainPK(txout.devoteLabel.hash, pkindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] The refund public key is not in the cache list\n"); + return false; + } + + if (!tmpcachedIndexsToRefund.count(pkindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] There is no refund record in the current cache list\n"); + return false; + } + } + else if (txout.GetCampaignType() == TYPE_CONSENSUS_ORDINARY_PUSNISHMENT) + { + uint16_t pkindex; + if (!ContainPK(txout.devoteLabel.hash, pkindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] The penalty public key is not in the cache list\n"); + return false; + } + if (!tmpcachedTimeoutToPunish.count(pkindex)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] There is no penalty record in the current cache list\n"); + return false; + } + + } + } + } + + if (tx->GetTxType() != 1) + continue; + + + DPOC_errtype errtype; + if (!verifyDPOCTx(*tx, errtype)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock] verifyDPOCTx return false error type: %d\n", errtype); + return false; + } + } + } + else + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock]A snapshot list may be empty if you fail at the end of the snapshot!\n"); + } + + verifysuccessed = true; + if (g_bStdCout) + { + std::cout << "VerifyDPOCBlock(): success exit" << std::endl << std::endl; + } + + return true; +} + +bool CConsensusAccountPool::ContainPK(uint160 pk, uint16_t& index) +{ + for (int i = 0; i < candidatelist.size(); i++) { + if (candidatelist.at(i).getPubicKey160hash() == pk) + { + index = i; + return true; + } + } + index = -1; + return false; +} + +bool CConsensusAccountPool::contain(std::vector> list, uint16_t indexIn) +{ + std::vector>::iterator it; + for (it=list.begin(); it!=list.end(); it++) + { + if ((*it).first == indexIn) + return true; + } + return false; +} + +CAmount CConsensusAccountPool::GetDepositBypkhash(uint160 pkhash) +{ + CAmount nValue = 0; + uint16_t index = -1; + if (!ContainPK(pkhash, index)) + { + LogPrintf("[CConsensusAccountPool::GetDepositBypkhash] The current account hash is not found in the list of candidates\n"); + return nValue; + } + + return candidatelist[index].getJoinIPC(); +} + +uint256 CConsensusAccountPool::GetTXhashBypkhash(uint160 pkhash) +{ + uint256 hash ; + hash.SetNull(); + uint16_t index = -1; + if (!ContainPK(pkhash, index)) + { + LogPrintf("[CConsensusAccountPool::GetDepositBypkhash] A hash of the current account is not found in the candidate list\n"); + return hash; + } + + return candidatelist[index].getTxhash(); +} + +bool CConsensusAccountPool::IsAviableUTXO(const uint256 hash) +{ + LogPrintf("[CConsensusAccountPool::IsAviableUTXO] get utxo state\n"); + SnapshotClass cursnapshot; + if (!GetLastSnapshot(cursnapshot)) + { + LogPrintf("[CConsensusAccountPool::checkNewBlock] Gets a snapshot to record the tail block failed\n"); + return false; + } + + std::set::iterator refundit; + for (refundit = cursnapshot.curCandidateIndexList.begin(); refundit != cursnapshot.curCandidateIndexList.end(); refundit++) + { + if (candidatelist[*refundit].getTxhash() == hash) + return false; + } + for (refundit = cursnapshot.cachedIndexsToRefund.begin(); refundit != cursnapshot.cachedIndexsToRefund.end(); refundit++) + { + if (candidatelist[*refundit].getTxhash() == hash) + return false; + } + for (refundit = cursnapshot.cachedTimeoutPunishToRun.begin(); refundit != cursnapshot.cachedTimeoutPunishToRun.end(); refundit++) + { + if (candidatelist[*refundit].getTxhash() == hash) + return false; + } + + std::vector cachedSnapshots; + uint32_t cachedHeight = cursnapshot.blockHeight - CACHED_BLOCK_COUNT; + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] to get cachedHeight between %d-%d \n", cachedHeight, cursnapshot.blockHeight); + if (!GetSnapshotsByHeight(cachedSnapshots, cachedHeight)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] to get the list of cached snapshots failed\n"); + return false; + } + std::vector::iterator searchIt; + bool founded = false; + for (searchIt = cachedSnapshots.begin(); searchIt != cachedSnapshots.end(); searchIt++) + { + + for (refundit = (*searchIt).curRefundIndexList.begin(); refundit != (*searchIt).curRefundIndexList.end(); refundit++) + { + if (candidatelist[*refundit].getTxhash() == hash) + return false; + } + for (refundit = (*searchIt).curTimeoutPunishList.begin(); refundit != (*searchIt).curTimeoutPunishList.end(); refundit++) + { + if (candidatelist[*refundit].getTxhash() == hash) + return false; + } + } + + return true; +} + +bool CConsensusAccountPool::getCreditFromSnapshotByIndex(SnapshotClass snapshot, const uint16_t indexIn, int64_t &credit) +{ + LogPrintf("[CConsensusAccountPool::getCreditFromSnapshotByIndex] called\n"); + for (std::vector>::iterator accountit = snapshot.cachedMeetingAccounts.begin(); + accountit != snapshot.cachedMeetingAccounts.end(); accountit ++) + { + if ( (*accountit).first == indexIn) + { + credit = (*accountit).second; + LogPrintf("[CConsensusAccountPool::getCreditFromSnapshotByIndex] from snapshot get index=%d,credit=%d\n", indexIn, credit); + return true; + } + } + + return false; +} + +bool CConsensusAccountPool::pushDPOCBlock(const std::shared_ptr pblock, uint32_t blockHeight) +{ + if (analysisfinished) + { + setTotalAmount(*pblock, true); + } + + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] IN block height= %d , timestamp= %d , realtime = %d , nPeriodStartTime= %d , accountNumber= %d , accountindex= %d\n", + blockHeight, (pblock->nPeriodStartTime + (pblock->nTimePeriod + 1) * BLOCK_GEN_TIME) / 1000, + pblock->nTime, pblock->nPeriodStartTime, pblock->nPeriodCount, pblock->nTimePeriod); + + SnapshotClass newsnapshot; + newsnapshot.blockTime = pblock->nTime; + newsnapshot.timestamp = (pblock->nPeriodStartTime + (pblock->nTimePeriod + 1) * BLOCK_GEN_TIME) / 1000; + newsnapshot.meetingstarttime = pblock->nPeriodStartTime; + newsnapshot.meetingstoptime = pblock->nPeriodStartTime + pblock->nPeriodCount * BLOCK_GEN_TIME; + newsnapshot.blockHeight = blockHeight; + + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] tmp snapshot height= %d , timestamp= %d , realtime = %d , meetingstarttime= %d , meetingstoptime= %d\n", + newsnapshot.blockHeight, newsnapshot.timestamp, newsnapshot.blockTime, + newsnapshot.meetingstarttime, newsnapshot.meetingstoptime); + + SnapshotClass lastSnapshot; + if (!GetLastSnapshot(lastSnapshot))//generate block + { + uint160 MeetingHash; + CAmount ipcvalue; + MeetingHash.SetHex(trustPKHashList[0]); + ipcvalue = Params().MIN_DEPOSI + CACHED_BLOCK_COUNT; + candidatelist.push_back(CConsensusAccount(MeetingHash, ipcvalue)); + + newsnapshot.pkHashIndex = 0; + newsnapshot.curCandidateIndexList.insert(0); + newsnapshot.cachedMeetingAccounts.push_back(std::make_pair(0, 0)); + + newsnapshot.curTimeoutIndexRecord.clear(); + newsnapshot.cachedBanList.clear(); + newsnapshot.timestamp = newsnapshot.meetingstoptime / 1000; + } + else + { + newsnapshot.curCandidateIndexList = lastSnapshot.curCandidateIndexList; + newsnapshot.cachedIndexsToRefund = lastSnapshot.cachedIndexsToRefund; + newsnapshot.cachedTimeoutPunishToRun = lastSnapshot.cachedTimeoutPunishToRun; + newsnapshot.curTimeoutIndexRecord = lastSnapshot.curTimeoutIndexRecord; + newsnapshot.cachedBanList = lastSnapshot.cachedBanList; + + std::set::iterator banit; + for (banit = newsnapshot.cachedBanList.begin(); banit != newsnapshot.cachedBanList.end(); banit++) + { + if (newsnapshot.cachedIndexsToRefund.count(*banit)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Remove the blacklist included in the refund list index=%d\n",*banit); + newsnapshot.cachedIndexsToRefund.erase(*banit); + } + } + + if (verifysuccessed) + { + tmpcachedIndexsToRefund.clear(); + tmpcachedTimeoutToPunish.clear(); + verifysuccessed = false; + + SnapshotClass lastSnapshot; + if (GetLastSnapshot(lastSnapshot)) + { + tmpcachedIndexsToRefund = lastSnapshot.cachedIndexsToRefund; + tmpcachedTimeoutToPunish = lastSnapshot.cachedTimeoutPunishToRun; + + uint32_t cachedHeight = (lastSnapshot.blockHeight > CACHED_BLOCK_COUNT) ? (lastSnapshot.blockHeight - CACHED_BLOCK_COUNT) : 0; + SnapshotClass cachedSnapshot; + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] ready to get snapshot height = %d\n", cachedHeight); + if (!GetSnapshotByHeight(cachedSnapshot, cachedHeight)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] get cache snapshot faile\n"); + return false; + } + + std::set::iterator it; + for (it = cachedSnapshot.curRefundIndexList.begin(); it != cachedSnapshot.curRefundIndexList.end(); it++) + { + if (!tmpcachedIndexsToRefund.count(*it)) + tmpcachedIndexsToRefund.insert(*it); + } + + LogPrintf("[CConsensusAccountPool::pushDPOCBlock]The currently saved temporary list of waiting refunds is:\n"); + printsets(tmpcachedIndexsToRefund); + + for (it = cachedSnapshot.curTimeoutPunishList.begin(); it != cachedSnapshot.curTimeoutPunishList.end(); it++) + { + if (!tmpcachedTimeoutToPunish.count(*it)) + tmpcachedTimeoutToPunish.insert(*it); + } + + LogPrintf("[CConsensusAccountPool::pushDPOCBlock]The temporary list of the currently saved prepared execution timeout penalties is:\n"); + printsets(tmpcachedTimeoutToPunish); + + } + + std::set::iterator itforrefund; + for (itforrefund = tmpcachedIndexsToRefund.begin(); itforrefund != tmpcachedIndexsToRefund.end(); itforrefund++) + { + newsnapshot.cachedIndexsToRefund.insert(*itforrefund); + } + for (itforrefund = tmpcachedTimeoutToPunish.begin(); itforrefund != tmpcachedTimeoutToPunish.end(); itforrefund++) + { + newsnapshot.cachedTimeoutPunishToRun.insert(*itforrefund); + } + } + + uint64_t cacheTime = (pblock->nPeriodStartTime - CACHED_BLOCK_COUNT * BLOCK_GEN_TIME) / 1000; + SnapshotClass cachedsnapshot; + if (!GetSnapshotByTime(cachedsnapshot, cacheTime)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] GetSnapshotByTime faile!\n"); + return false; + } + + CPubKey recvPublickey; + std::vector recvSign; + if (!getPublicKeyFromBlock(pblock.get(), recvPublickey, recvSign)) { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] get publickey and sigedsting in block faile\n"); + return false; + } + + CKeyID curHash = recvPublickey.GetID(); + uint16_t curblockpkhashindex; + if (!ContainPK(curHash, curblockpkhashindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] publickey is not in cashed list,error\n"); + return false; + } + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] now publickey=%s index=%d\n", curHash.GetHex().c_str(), curblockpkhashindex); + newsnapshot.pkHashIndex = curblockpkhashindex; + + CConsensusAccount tmpaccount = candidatelist[curblockpkhashindex]; + tmpaccount.setCredit(tmpaccount.getCredit() + PACKAGE_CREDIT_REWARD); + candidatelist[curblockpkhashindex] = tmpaccount; + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] add index = %d,credit=%d\n", curblockpkhashindex, candidatelist[curblockpkhashindex].getCredit()); + + //Calculate the current block to the previous block timeout violation + if (snapshotlist.size() > Params().CHECK_START_BLOCKCOUNT) + { + if (newsnapshot.curTimeoutIndexRecord.count(curblockpkhashindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Delete the timeout record of the block\n"); + newsnapshot.curTimeoutIndexRecord.erase(curblockpkhashindex); + } + + std::map timeoutaccountindexs; + if (!GetTimeoutIndexs(pblock, lastSnapshot, timeoutaccountindexs)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Failed to obtain timeout record\n"); + return false; + } + + //The current timeout record list is refreshed + std::map::iterator timeoutIt; + for (timeoutIt = timeoutaccountindexs.begin(); timeoutIt != timeoutaccountindexs.end(); timeoutIt++) + { + //If the current public key is not in the penalty cache or the penalty record, it is recorded as a new timeout record + uint16_t curtimeoutindex = timeoutIt->first; + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Calculated the timeout key HASH index=%d\n", curtimeoutindex); + printsets(newsnapshot.cachedTimeoutPunishToRun); + printsets(newsnapshot.curTimeoutPunishList); + if (newsnapshot.cachedTimeoutPunishToRun.count(curtimeoutindex) || + newsnapshot.curTimeoutPunishList.count(curtimeoutindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The record of the penalty record or the new overtime penalty record includes the index, and no overtime record is added\n"); + continue; + } + if (lastSnapshot.curBanList.count(curtimeoutindex) || + lastSnapshot.cachedBanList.count(curtimeoutindex)) + { + newsnapshot.curTimeoutIndexRecord.erase(curtimeoutindex); + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] This index is included in the serious penalty record, which removes the timeout record of the node\n"); + continue; + } + + std::vector cachedSnapshots; + uint32_t cachedHeight = lastSnapshot.blockHeight - CACHED_BLOCK_COUNT; + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] ready to get snapshots when height in %d-%d\n", cachedHeight, lastSnapshot.blockHeight); + if (!GetSnapshotsByHeight(cachedSnapshots, cachedHeight)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] get snapshot faile\n"); + return false; + } + std::vector::iterator searchIt; + bool founded = false; + for (searchIt = cachedSnapshots.begin(); searchIt != cachedSnapshots.end(); searchIt++) + { + printsets((*searchIt).curTimeoutPunishList); + if ((*searchIt).curTimeoutPunishList.count(curtimeoutindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The cached new timeout penalty record includes the index, which does not add a timeout record\n"); + founded = true; + break; + } + } + if (founded) + continue; + + int count = timeoutIt->second; + if (newsnapshot.curTimeoutIndexRecord.count(curtimeoutindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] This index is present in the current timeout record, and the count is cumulative\n"); + count = newsnapshot.curTimeoutIndexRecord[curtimeoutindex] + count; + newsnapshot.curTimeoutIndexRecord[curtimeoutindex] = count; + } + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] add timeout recode in index = %d\n", curtimeoutindex); + newsnapshot.curTimeoutIndexRecord[curtimeoutindex] = count; + } + + //According to the time-out record list, once there are N consecutive records that do not block, the execution timeout penalty + std::map::iterator iterTimeout = newsnapshot.curTimeoutIndexRecord.begin(); + + while(iterTimeout != newsnapshot.curTimeoutIndexRecord.end()) + { + bool founded = false; + for (std::vector::iterator it = trustPKHashList.begin(); it != trustPKHashList.end(); it++) + { + uint160 believedPubkey; + believedPubkey.SetHex(*it); + if (candidatelist.at(iterTimeout->first).getPubicKey160hash() == believedPubkey) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The current public key is trusted not to be penalized!\n"); + founded = true; + break; + } + } + if (founded) + { + ++iterTimeout; + continue; + } + + bool bErase = false; + if (iterTimeout->second >= MAX_TIMEOUT_COUNT) + { + uint16_t pkhashindex = iterTimeout->first; + + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] publickey index=%d timeout,Record the timeout penalty list\n", pkhashindex); + + newsnapshot.curTimeoutPunishList.insert(pkhashindex); + + //iterTimeout = + ++iterTimeout; + newsnapshot.curTimeoutIndexRecord.erase(pkhashindex); + bErase = true; + + if (newsnapshot.curCandidateIndexList.count(pkhashindex)) + { + newsnapshot.curCandidateIndexList.erase(pkhashindex); + } + } + + if (!bErase) + { + ++iterTimeout; + } + } + + } + + //Calculate the current chunk order index and plug in the snapshot list + std::list> consensusList; + consensusList.clear(); + std::set::iterator candidateit; + for (candidateit = cachedsnapshot.curCandidateIndexList.begin(); candidateit != cachedsnapshot.curCandidateIndexList.end(); candidateit++) + { + std::shared_ptr tmpaccount = std::make_shared(candidatelist.at(*candidateit)); + consensusList.push_back(tmpaccount); + } + bool result = CDpocMining::Instance().GetMeetingList(pblock->nPeriodStartTime, consensusList); + if (result) + { + std::list>::iterator consensusListIt; + newsnapshot.cachedMeetingAccounts.clear(); + for (consensusListIt = consensusList.begin(); consensusListIt != consensusList.end(); consensusListIt++) + { + uint16_t tmpIndex = -1; + uint160 tmppkhash = (*consensusListIt)->getPubicKey160hash(); + ContainPK(tmppkhash, tmpIndex); + newsnapshot.cachedMeetingAccounts.push_back(std::make_pair(tmpIndex, (*consensusListIt)->getCredit())); + } + } + + } + + //Analysis of trading + for (const auto& tx : pblock->vtx) + { + if (tx->IsCoinBase()) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Deal with Coinbase TX %s\n", tx->GetHash().GetHex().c_str()); + //Read CoinBase to remove the already executed refund transaction from the cache list + for (const CTxOut& txout : tx->vout) + { + uint16_t pkindex; + if (txout.GetCampaignType() == TYPE_CONSENSUS_RETURN_DEPOSI) + { + if (ContainPK(txout.devoteLabel.hash, pkindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Deal with refund\n"); + if (newsnapshot.cachedIndexsToRefund.count(pkindex)) + { + newsnapshot.cachedIndexsToRefund.erase(pkindex); + + //Remove the refund account record from the timeout list + newsnapshot.curTimeoutIndexRecord.erase(pkindex); + + //If it is a billing account (and is not currently analyzing a local block), delete it + std::string localpkhashhexstring; + + if (CDpocInfo::Instance().getLocalAccoutVar(localpkhashhexstring)) + { + uint160 accountmyself; + accountmyself.SetHex(localpkhashhexstring); + if (accountmyself == txout.devoteLabel.hash && + analysisfinished) { + + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Delete the local accounting account %s\n", localpkhashhexstring.c_str()); + + std::string strStatus; + std::string strPunishHash; + if (CDpocInfo::Instance().GetConsensusStatus(strStatus, strPunishHash)) + { + int nStatus = atoi(strStatus.c_str()); + nStatus += 3; + + std::stringstream ss; + ss << nStatus; + std::string strStatusNew = ss.str(); + + std::string strHash(txout.devoteLabel.hash.ToString()); + SetConsensusStatus(strStatusNew, strHash); + } + + CDpocInfo::Instance().RemoveInfo(); + CDpocInfo::Instance().setJoinCampaign(false); + } + } + continue; + } + } + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The refund public key is not in the cache list\n"); + } + //Update the penalty list according to the penalty status + else if (txout.GetCampaignType() == TYPE_CONSENSUS_ORDINARY_PUSNISHMENT) + { + if (ContainPK(txout.devoteLabel.hash, pkindex)) + { + if ((pkindex >= 0) && (pkindex < candidatelist.size())) + { + CConsensusAccount &curAccount = candidatelist[pkindex]; + int64_t curCredit = curAccount.getCredit(); + curCredit -= TIMEOUT_CREDIT_PUNISH; + //Once added to the penalty list, the record is cleared, each time the deduction needs to be backed up, + //so that pop can be rolled back + curAccount.backupCredit(); + curAccount.setCredit(curCredit); + candidatelist[pkindex] = curAccount; + + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Index=%d Credit value to %d\n", pkindex, curCredit); + } + + //The timeout penalty that has been executed is cleared from the timeout record + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Prepare to delete the timeout record\n"); + std::vector vecforprint; + vecforprint.push_back(newsnapshot); + printsnapshots(vecforprint); + if (newsnapshot.cachedTimeoutPunishToRun.count(pkindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The timeout penalty is executed,Remove the index = %d from the penalty list\n", pkindex); + newsnapshot.cachedTimeoutPunishToRun.erase(pkindex); + } + + //Set the average penalty value + std::string strStatus("1"); + std::string strHash(txout.devoteLabel.hash.ToString()); + SetConsensusStatus(strStatus, strHash); + + //Give the public key a refund + newsnapshot.curRefundIndexList.insert(pkindex); + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Execute the timeout refund to the index=%d\n", pkindex); + + continue; + } + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The timeout penalty public key is not in the cache list,Abnormal situation\n"); + } + } + + continue; + } + + if (tx->GetTxType() != 1) + continue; + + if (tx->GetCampaignType() == TYPE_CONSENSUS_REGISTER) + { + if (g_bStdCout) + { + std::cout << "join campaign" << std::endl; + } + + uint160 curHash = tx->GetCampaignPublickey(); + uint16_t pkhashindex; + CAmount ipcvalue = tx->GetRegisterIPC(); + if (!ContainPK(curHash, pkhashindex)) + { + pkhashindex = candidatelist.size(); + candidatelist.push_back(CConsensusAccount(curHash, ipcvalue,tx->GetHash())); + } + else + { + //Although the public key has been added, the deposit needs to be updated + candidatelist[pkhashindex].setJoinIPC(ipcvalue); + candidatelist[pkhashindex].setTxhash(tx->GetHash()); + LogPrintf("[update candidatelist] The currently added public key=%d The deposit=%d, The current public key credit value is %d,txhash:%s\n", + pkhashindex, candidatelist[pkhashindex].getJoinIPC(), candidatelist[pkhashindex].getCredit(), candidatelist[pkhashindex].getTxhash().ToString()); + } + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The currently added public key=%d The deposit=%d, The current public key credit value=%d,txhash :%s\n", + pkhashindex, candidatelist[pkhashindex].getJoinIPC(), candidatelist[pkhashindex].getCredit(), + candidatelist[pkhashindex].getTxhash().ToString()); + + //Based on the dynamic adjustment of the deposit value and the current public key credit value, + //the limit value of the security gate of the current public key is obtained + CAmount curDeposiThreshold = GetCurDepositAdjust(curHash, blockHeight); + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The current dynamic adjustment of the deposit threshold is %d\n", curDeposiThreshold); + + if (ipcvalue >= curDeposiThreshold) + { + if (!newsnapshot.curCandidateIndexList.count(pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The deposit meets the requirements of the quota,To join campaign\n"); + if (g_bStdCout) + { + std::cout << "The deposit meets the requirements of the quota,Application for successful adoption,The public key has been added to the list of candidates" << std::endl; + } + newsnapshot.curCandidateIndexList.insert(pkhashindex); + } + } + else + { + if (!newsnapshot.cachedIndexsToRefund.count(pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] The deposit does not meet the quota request , carries out the refund\n"); + if (g_bStdCout) + { + std::cout << "The deposit does not meet the quota request and carries out the refund" << std::endl; + } + newsnapshot.cachedIndexsToRefund.insert(pkhashindex); + } + } + + } + else if (tx->GetCampaignType() == TYPE_CONSENSUS_QUITE) + { + uint160 curHash = tx->GetCampaignPublickey(); + uint16_t pkhashindex; + if (!ContainPK(curHash, pkhashindex)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] ContainPK faile"); + } + + if (newsnapshot.curCandidateIndexList.count(pkhashindex)) + { + newsnapshot.curCandidateIndexList.erase(pkhashindex); + newsnapshot.curRefundIndexList.insert(pkhashindex); + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] Perform a normal exit refund to the index=%d\n", pkhashindex); + } + } + } + + if (!PushSnapshot(newsnapshot)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock]PushSnapshot Adding a snapshot to the list faile!\n"); + return false; + } + + uint32_t cachedHeight = (blockHeight > CACHED_BLOCK_COUNT) ? blockHeight - CACHED_BLOCK_COUNT : 0; + std::vector listforprint; + if (!GetSnapshotsByHeight(listforprint, cachedHeight, blockHeight)) + { + LogPrintf("[CConsensusAccountPool::pushDPOCBlock] get snapshot height %d-%d to print reslut faile\n", blockHeight - CACHED_BLOCK_COUNT, blockHeight); + } + + printsnapshots(listforprint); + + return true; +} + +bool CConsensusAccountPool::rollbackCandidatelist(uint32_t nHeight) +{ + writeLock wtlock(rwmutex); + + std::map::iterator iterList = snapshotlist.end(); + while (iterList != snapshotlist.begin()) + { + iterList--; + if (nHeight < iterList->first) + { + uint16_t tmpIndex; + std::map::iterator iterLast = iterList; + if (iterLast != snapshotlist.begin()) + { + iterLast--; + } + + //From the previous snapshot to the current pending delete snapshot, + //if the blacklist operation is performed, the credit value is rolled back + for (std::set::iterator banit = iterLast->second.curBanList.begin(); + banit != iterLast->second.curBanList.end(); banit++) + { + tmpIndex = *banit; + candidatelist[tmpIndex].revertCredit(); + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] pop publickey Index=%d,creditValue=%d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + + //Sets the credit value of the current node directly to the credit value in the previous snapshot + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] Rollback the public key index = %d directly from the snapshot, To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + + //For the current pending delete snapshot, the credit value of the added public key of the block person needs to be rolled back + tmpIndex = iterList->second.pkHashIndex; + + //Sets the credit value of the current node directly to the credit value in the previous snapshot + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] Rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + + //The credit value that is deducted in the timeout record needs to be rolled back + std::vector listforprint; + listforprint.push_back(iterLast->second); + listforprint.push_back(iterList->second); + printsnapshots(listforprint); + for (std::map::iterator timeoutit = iterList->second.curTimeoutIndexRecord.begin(); + timeoutit != iterList->second.curTimeoutIndexRecord.end(); timeoutit++) + { + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] The current timeout record for deleting a snapshot includes indexe = %d\n", timeoutit->first); + + if (iterLast->second.curTimeoutIndexRecord.count(timeoutit->first)) + { + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] An index=%d is included in the timeout record for the previous snapshot\n", timeoutit->first); + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] Last snapshot of the timeout record index=%d number=%d,The number of times to delete a snapshot = %d\n", + timeoutit->first, iterLast->second.curTimeoutIndexRecord[timeoutit->first], timeoutit->second); + + if (iterLast->second.curTimeoutIndexRecord[timeoutit->first] < timeoutit->second) + { + tmpIndex = timeoutit->first; + + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + } + + else + { + tmpIndex = timeoutit->first; + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + } + + //If the current snapshot has a timeout penalty, + //and the previous snapshot does not have this timeout penalty, revert is required + for (std::set::iterator punishit = iterList->second.curTimeoutPunishList.begin(); + punishit != iterList->second.curTimeoutPunishList.end(); + punishit++) + { + tmpIndex = (*punishit); + + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] An index=%d is included in the timeout penalty list for the snapshot\n", tmpIndex); + if (!iterLast->second.curTimeoutPunishList.count(tmpIndex) && + iterLast->second.curTimeoutIndexRecord.count(tmpIndex)) + { + //Sets the credit value of the current node directly to the credit value in the previous snapshot + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] 2rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + } + + LogPrintf("[CConsensusAccountPool::rollbackCandidatelist] pop Snapshot height=%d\n", iterLast->first); + } + else + { + break; + } + } +} + +bool CConsensusAccountPool::popDPOCBlock( uint32_t blockHeight) +{ + writeLock wtlock(rwmutex); + + std::map::iterator iterList = snapshotlist.end(); + while(iterList != snapshotlist.begin()) + { + iterList--; + if (blockHeight < iterList->first) + { + uint16_t tmpIndex; + std::map::iterator iterLast = iterList; + if (iterLast != snapshotlist.begin()) + { + iterLast--; + } + + for (std::set::iterator banit = iterLast->second.curBanList.begin(); + banit != iterLast->second.curBanList.end(); banit++) + { + tmpIndex = *banit; + candidatelist[tmpIndex].revertCredit(); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] rollback the public key index=%d,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + + tmpIndex = iterList->second.pkHashIndex; + + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + + std::vector listforprint; + listforprint.push_back(iterLast->second); + listforprint.push_back(iterList->second); + printsnapshots(listforprint); + for (std::map::iterator timeoutit = iterList->second.curTimeoutIndexRecord.begin(); + timeoutit != iterList->second.curTimeoutIndexRecord.end(); timeoutit++) + { + LogPrintf("[CConsensusAccountPool::popDPOCBlock] The current timeout record for deleting a snapshot includes index = %d\n", timeoutit->first); + + if (iterLast->second.curTimeoutIndexRecord.count(timeoutit->first)) + { + LogPrintf("[CConsensusAccountPool::popDPOCBlock] An index=%d is included in the timeout record for the previous snapshot\n", timeoutit->first); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] Last snapshot of the timeout record index=%d number=%d,The number of times to delete a snapshot = %d\n\n", + timeoutit->first, iterLast->second.curTimeoutIndexRecord[timeoutit->first], timeoutit->second); + + if (iterLast->second.curTimeoutIndexRecord[timeoutit->first] < timeoutit->second) + { + tmpIndex = timeoutit->first; + + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + } + else + { + tmpIndex = timeoutit->first; + + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + } + + for (std::set::iterator punishit = iterList->second.curTimeoutPunishList.begin(); + punishit != iterList->second.curTimeoutPunishList.end(); + punishit++) + { + tmpIndex = (*punishit); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] An index=%d is included in the timeout penalty list for the snapshot\n", tmpIndex); + if (!iterLast->second.curTimeoutPunishList.count(tmpIndex) && + iterLast->second.curTimeoutIndexRecord.count(tmpIndex)) + { + int64_t tmpCredit; + if (getCreditFromSnapshotByIndex(iterLast->second, tmpIndex, tmpCredit)) + { + candidatelist[tmpIndex].setCredit(tmpCredit); + LogPrintf("[CConsensusAccountPool::popDPOCBlock] rollback the public key index=%d directly from the snapshot,To creditValue %d\n", tmpIndex, candidatelist[tmpIndex].getCredit()); + } + } + } + + LogPrintf("[CConsensusAccountPool::popDPOCBlock] pop snapshot height=%d\n", iterLast->first); + + uint32_t u32Height = iterList->first; + + boost::filesystem::path pathTmpIndex = GetDataDir() / m_strSnapshotIndexPath; + uint64_t u64Num = 0; + if (boost::filesystem::exists(pathTmpIndex)) + { + uint64_t u64FileSizeIndex = boost::filesystem::file_size(pathTmpIndex); + u64Num = u64FileSizeIndex / m_u64SnapshotIndexSize; + } + + if (u32Height <= u64Num) + { + writeCandidatelistToFile(); + + //Truncate the SnapshotIndex file + uint64_t fileSizeIndex = 0; + if (boost::filesystem::exists(pathTmpIndex)) + { + fileSizeIndex = boost::filesystem::file_size(pathTmpIndex); + fileSizeIndex = fileSizeIndex - m_u64SnapshotIndexSize; + + FILE *fileIndex = fopen(pathTmpIndex.string().c_str(), "ab+"); + if (fileIndex) + { + TruncateFile(fileIndex, fileSizeIndex); + } + fclose(fileIndex); + } + + //Truncate the Snapshot file + boost::filesystem::path pathTmp = GetDataDir() / m_strSnapshotPath; + uint64_t fileSize = 0; + if (boost::filesystem::exists(pathTmp)) + { + fileSize = boost::filesystem::file_size(pathTmp); + fileSize = m_mapSnapshotIndex[iterList->first]; + + FILE *file = fopen(pathTmp.string().c_str(), "ab+"); + if (file) + { + TruncateFile(file, fileSize); + } + fclose(file); + } + + flushSnapshotToDisk(); + } + + m_mapSnapshotIndex.erase(iterList->first); + + iterList = snapshotlist.erase(iterList); + } + else + { + break; + } + } + + LogPrintf("[CConsensusAccountPool::popDPOCBlock] end. \n"); + + return true; +} + + +bool CConsensusAccountPool::AddDPOCCoinbaseToBlock(CBlock* pblockNew, CBlockIndex* pindexPrev, uint32_t blockHeight, CMutableTransaction &coinbaseTx) +{ + SnapshotClass cursnapshot; + if (!GetLastSnapshot(cursnapshot)) + { + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] You can't get the latest snapshot, perhaps because the snapshot list is empty and return true\n"); + return true; + } + + std::set::iterator refundit; + for (refundit = cursnapshot.cachedIndexsToRefund.begin(); refundit!= cursnapshot.cachedIndexsToRefund.end(); refundit++) + { + if(contain(cursnapshot.cachedMeetingAccounts, *refundit)) + { + //LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] Delay the refund of the public key deposit %d\n", *refundit); + continue; + } + if (cursnapshot.curBanList.count(*refundit) || cursnapshot.cachedBanList.count(*refundit)) + { + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] The refund account is on the blacklist!\n"); + } + + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] The public key=%d of the current refund, The deposit=%d\n", *refundit, candidatelist[*refundit].getJoinIPC()); + CAmount zero = 0; + CTxOut curRefund(zero, candidatelist[*refundit].getPubicKey160hash()); + coinbaseTx.vout.emplace_back(std::move(curRefund)); + + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] Refundable deposit=%d\n", candidatelist[*refundit].getJoinIPC()); + } + + //Ordinary punishment + for (refundit = cursnapshot.cachedTimeoutPunishToRun.begin(); refundit != cursnapshot.cachedTimeoutPunishToRun.end(); refundit++) + { + if (contain(cursnapshot.cachedMeetingAccounts, *refundit)) + { + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] Delay the refund of the public key index %d\n", *refundit); + continue; + } + std::cout << "Add Refund to Coinbase tx" << std::endl; + + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] Timeout Punish publickey=%d,the credit=%d", *refundit, candidatelist[*refundit].getJoinIPC()); + CTxOut curTimeoutPunish(TYPE_CONSENSUS_ORDINARY_PUSNISHMENT, candidatelist[*refundit].getPubicKey160hash()); + coinbaseTx.vout.emplace_back(std::move(curTimeoutPunish)); + } + + for (refundit = cursnapshot.curBanList.begin(); refundit != cursnapshot.curBanList.end(); refundit++) + { + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] Severe punishment publickey=%d,the credit=%d", *refundit, candidatelist[*refundit].getJoinIPC()); + CTxOut curBanTx(TYPE_CONSENSUS_SEVERE_PUNISHMENT, candidatelist[*refundit].getPubicKey160hash()); + coinbaseTx.vout.emplace_back(std::move(curBanTx)); + } + + std::string pubkeystring; + if (!CDpocInfo::Instance().getLocalAccoutVar(pubkeystring)) + { + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] ERROR! no local account\n"); + return false; + } + + uint160 curpk; + curpk.SetHex(pubkeystring); + if (!addSignToCoinBase(pblockNew, pindexPrev, blockHeight, coinbaseTx, &curpk)) { + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] ERROR! add sign failed\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::AddDPOCCoinbaseToBlock] end by true\n "); + return true; +} + +bool CConsensusAccountPool::addSignToCoinBase(CBlock* pblock, CBlockIndex* pindexPrev, uint32_t blockHeight, CMutableTransaction &coinbaseTx, uint160* pPubKeyHash) +{ + if (nullptr == pPubKeyHash || NULL == pPubKeyHash) { + return false; + } + + if (pPubKeyHash->IsNull()) { + return false; + } + + if (pPubKeyHash) { + CKeyID publickeyID(*pPubKeyHash); + CPubKey vchPubKeyOut; + if (pwalletMain->GetPubKey(publickeyID, vchPubKeyOut)) { + LogPrintf("[CConsensusAccountPool::addSignToCoinBase] GetPubKey is ok\n"); + } + else { + LogPrintf("[CConsensusAccountPool::addSignToCoinBase] don't find public key "); + return false; + } + + if (pwalletMain->IsLocked()) + pwalletMain->Unlock(pwalletMain->curstrWalletPassphrase); + + CKey vchPrivKeyOut; + if (pwalletMain->GetKey(publickeyID, vchPrivKeyOut)) { + LogPrintf("[CConsensusAccountPool::addSignToCoinBase] pwalletMain->GetKey() success! \n"); + } + else { + LogPrintf("[CConsensusAccountPool::addSignToCoinBase] isLocked = %d \n ",pwalletMain->IsLocked()); + LogPrintf("[CConsensusAccountPool::addSignToCoinBase] don't find private key"); + return false; + } + pwalletMain->Lock(); //To encrypt + + uint256 hash; + CAmount nfee = 0; + for (int i=0; inPeriodStartTime), sizeof(pblock->nPeriodStartTime));//8Byte + hashoperator.Write((unsigned char*)&(pblock->nPeriodCount), sizeof(pblock->nPeriodCount));//4Byte + hashoperator.Write((unsigned char*)&(pblock->nTimePeriod), sizeof(pblock->nTimePeriod));//4Byte + hashoperator.Write((unsigned char*)&(pblock->nTime), sizeof(pblock->nTime));//4Byte + hashoperator.Write((unsigned char*)&nfee, sizeof(nfee));//8Byte + hashoperator.Write((unsigned char*)pindexPrev->GetBlockHash().begin(), 32);//256bit + hashoperator.Finalize(hash.begin()); + + std::vector vchSig; + vchPrivKeyOut.Sign(hash, vchSig); + + unsigned char dataBuff[10240]; + int datalen = 0; + memcpy(dataBuff, (unsigned char*)&(pblock->nPeriodStartTime), sizeof(pblock->nPeriodStartTime)); + datalen += sizeof(pblock->nPeriodStartTime); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nPeriodCount), sizeof(pblock->nPeriodCount)); + datalen += sizeof(pblock->nPeriodCount); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nTimePeriod), sizeof(pblock->nTimePeriod)); + datalen += sizeof(pblock->nTimePeriod); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nTime), sizeof(pblock->nTime)); + datalen += sizeof(pblock->nTime); + memcpy(dataBuff + datalen, (unsigned char*)&nfee, sizeof(nfee)); + datalen += sizeof(nfee); + memcpy(dataBuff + datalen, (unsigned char*)pindexPrev->GetBlockHash().begin(), 32);//256 bits + datalen += 32; + std::string datahex; + for (int i = 0; i < datalen; i++) + { + char tmp[3]; + sprintf(tmp, "%02x", dataBuff[i]); + datahex = datahex + std::string(tmp); + } + LogPrintf("[CConsensusAccountPool::addSignToCoinBase] Signature clear=%s , clear HASH=%s , public key HASH=%s\n", datahex.c_str(), hash.GetHex().c_str(), pPubKeyHash->GetHex().c_str()); + + // Generate signature cache + std::vector vchSigSend; + vchSigSend.resize(2 + vchPubKeyOut.size() + vchSig.size()); + + //Cached signature + unsigned char vchSigLen = (unsigned char)vchSig.size(); + vchSigSend[0] = vchSigLen; + for (std::vector::size_type ix = 0; ix < vchSig.size(); ++ix) { + vchSigSend[ix + 1] = vchSig[ix]; + } + + vchSigSend[vchSigLen + 1] = (unsigned char)vchPubKeyOut.size(); + for (std::vector::size_type ix = 0; ix < vchPubKeyOut.size(); ++ix) { + vchSigSend[ix + 2 + vchSigLen] = vchPubKeyOut[ix]; + } + + std::string strSin2Publickey = EncodeBase58(vchSigSend); + + LogPrintf("[CConsensusAccountPool::addSignToCoinBase] sign=%s\n", strSin2Publickey); + coinbaseTx.vout[0].coinbaseScript = strSin2Publickey; + coinbaseTx.vout[0].txType = 0; + } + return true; +} + +bool CConsensusAccountPool::GetSnapshotByTime(SnapshotClass &snapshot, uint64_t readtime) +{ + readLock rdlock(rwmutex); + + if (snapshotlist.size()==0) + { + LogPrintf("[CConsensusAccountPool::GetSnapshotByTime] The snapshot list is empty,cannot check\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::GetSnapshotByTime] Passed-in cache time= %d \n", readtime); + + //Find the time corresponding block as a cache block, give it back value, and cache the list of candidates + if (snapshotlist.size() > 0) + { + std::map::iterator mapit = snapshotlist.end(); + mapit--; + LogPrintf("[CConsensusAccountPool::GetSnapshotByTime] The last snapshot time= %d \n", mapit->second.timestamp); + + while (mapit->second.timestamp > readtime) + { + if (mapit == snapshotlist.begin()) + { + LogPrintf("[CConsensusAccountPool::GetSnapshotByTime] No matching snapshot was found\n"); + return false; + } + mapit--; + } + LogPrintf("[CConsensusAccountPool::GetSnapshotByTime] Find the height of the snapshot block= %d , time= %d \n", + mapit->second.blockHeight, mapit->second.timestamp); + + snapshot = mapit->second; + return true; + } + + LogPrintf("[CConsensusAccountPool::GetSnapshotByTime] The snapshot list is empty\n"); + + return false; +} + + +bool CConsensusAccountPool::GetSnapshotByHeight(SnapshotClass &snapshot, uint32_t height) +{ + readLock rdlock(rwmutex); + + if (snapshotlist.empty()) + { + LogPrintf("[CConsensusAccountPool::GetSnapshotByHeight] The snapshot list is empty!\n"); + return false; + } + + std::map::iterator mapit = snapshotlist.end(); + mapit--; + while (mapit->second.blockHeight > height && mapit != snapshotlist.begin()) + mapit--; + + if (mapit->second.blockHeight > height) + { + if (g_bStdCout) + { + std::cout << "[CConsensusAccountPool::GetSnapshotsByHeight] There is no block in the snapshot list that is not higher than this height!\n"; + } + LogPrintf("[CConsensusAccountPool::GetSnapshotsByHeight] There is no block in the snapshot list that is not higher than this height!\n"); + + return false; + } + + snapshot = mapit->second; + return true; +} + +bool CConsensusAccountPool::GetSnapshotsByHeight(std::vector &snapshots, uint32_t lowest, uint32_t highest) +{ + readLock rdlock(rwmutex); + + LogPrintf("[CConsensusAccountPool::GetSnapshotsByHeight] The height of the block to be read is in %d-%d\n", lowest, highest); + + if (snapshotlist.empty()) + { + LogPrintf("[CConsensusAccountPool::GetSnapshotsByHeight] The snapshot list is empty!\n"); + return false; + } + + std::map::iterator mapit = snapshotlist.end(); + mapit--; + + if (highest != 0) + { + while (mapit->second.blockHeight > highest && mapit != snapshotlist.begin()) + mapit--; + + if (mapit == snapshotlist.begin()) + { + LogPrintf("[CConsensusAccountPool::GetSnapshotsByHeight] There is no less than the highest height in the snapshot list!\n"); + return false; + } + } + + snapshots.clear(); + while (mapit->second.blockHeight >= lowest) + { + LogPrintf("[CConsensusAccountPool::GetSnapshotsByHeight] Find the snapshot height %d\n", mapit->second.blockHeight); + snapshots.push_back(mapit->second); + if (mapit == snapshotlist.begin()) + break; + mapit--; + } + + return true; +} + +bool CConsensusAccountPool::GetLastSnapshot(SnapshotClass &snapshot) +{ + readLock rdlock(rwmutex); + + if (snapshotlist.empty()) + { + LogPrintf("[CConsensusAccountPool::GetLastSnapshot] The snapshot list is empty!\n"); + return false; + } + + std::map::iterator mapit = snapshotlist.end(); + mapit--; + + snapshot = mapit->second; + LogPrintf("[CConsensusAccountPool::GetLastSnapshot] Get the snapshot tail height=%d, Plan package time=%d,Actually package time=%d\n", + snapshot.blockHeight, snapshot.timestamp, snapshot.blockTime); + + return true; +} + +bool CConsensusAccountPool::PushSnapshot(SnapshotClass snapshot) +{ + writeLock wtlock(rwmutex); + + if (snapshotlist.count(snapshot.blockHeight)) + { + LogPrintf("[CConsensusAccountPool::PushSnapshot] add height = %d of sanpshot to list\n", snapshot.blockHeight); + return false; + } + + LogPrintf("[CConsensusAccountPool::PushSnapshot] add height = %d of sanpshot to list\n", snapshot.blockHeight); + snapshotlist[snapshot.blockHeight] = snapshot; + + if (snapshotlist.size() > SNAPSHOTLENGTH) + { + snapshotlist.erase(snapshotlist.begin()); + m_mapSnapshotIndex.erase(m_mapSnapshotIndex.begin()); + } + + //Synchronous write file + if ((0 == (snapshot.blockHeight % 2000)) && (snapshot.blockHeight != 0)) + { + writeCandidatelistToFile(); + + boost::filesystem::path pathTmp = GetDataDir() / m_strSnapshotPath; + uint64_t fileSize = 0; + if (boost::filesystem::exists(pathTmp)) + { + fileSize = boost::filesystem::file_size(pathTmp); + } + + int nIndex = 0; + if (snapshot.blockHeight == 2000) + { + nIndex = 0; + } + else + { + boost::filesystem::path pathTmpIndex = GetDataDir() / m_strSnapshotIndexPath; + if (boost::filesystem::exists(pathTmpIndex)) + { + uint64_t u64FileSizeIndex = boost::filesystem::file_size(pathTmpIndex); + uint64_t u64Num = u64FileSizeIndex / m_u64SnapshotIndexSize; + uint64_t u64Mod = u64Num % 2000; + + nIndex = snapshot.blockHeight - 1999; + nIndex = nIndex + u64Mod; + } + } + + std::map::iterator iter = snapshotlist.find(nIndex); + + boost::filesystem::path pathTmp1 = GetDataDir() / m_strSnapshotPath; + FILE *file1 = fopen(pathTmp1.string().c_str(), "ab+"); + if (file1 == NULL) + { + LogPrintf("[CConsensusAccountPool::PushSnapshot] file1 is NULL\n"); + return false; + } + + boost::filesystem::path pathTmpIndex1 = GetDataDir() / m_strSnapshotIndexPath; + FILE *fileIndex1 = fopen(pathTmpIndex1.string().c_str(), "ab+"); + if (fileIndex1 == NULL) + { + LogPrintf("[CConsensusAccountPool::PushSnapshot] fileIndex1 is NULL\n"); + return false; + } + + int nIndexTest = 0; + for (; iter != snapshotlist.end(); ++iter) + { + ++nIndexTest; + + //Gets the start offset of the snapshot file + CSnapshotIndex sanpshotIndex; + sanpshotIndex.nHeight = iter->first; + sanpshotIndex.nOffset = fileSize; + + CAutoFile fileout1(file1, SER_DISK, CLIENT_VERSION); + if (fileout1.IsNull()) + { + LogPrintf("CSerializeDpoc::WriteToDisk return false by fileout.IsNull() \n"); + fclose(file1); + return false; + } + + try { + fileout1 << (iter->second); + } + catch (const std::exception& e) { + LogPrintf("CSerializeDpoc::WriteToDisk return false by %d \n", e.what()); + return false; + } + file1 = fileout1.release(); + + CAutoFile fileoutIndex1(fileIndex1, SER_DISK, CLIENT_VERSION); + if (fileoutIndex1.IsNull()) + { + LogPrintf("CSerializeDpoc::WriteToDisk return false by fileout.IsNull() \n"); + fclose(fileIndex1); + return false; + } + + try { + fileoutIndex1 << sanpshotIndex; + } + catch (const std::exception& e) { + LogPrintf("CSerializeDpoc::WriteToDisk return false by %d \n", e.what()); + return false; + } + + fileIndex1 = fileoutIndex1.release(); + m_mapSnapshotIndex[sanpshotIndex.nHeight] = sanpshotIndex.nOffset; + fileSize = fileSize + getSnapshotSize(iter->second); + } + fclose(file1); + fclose(fileIndex1); + file1 = NULL; + fileIndex1 = NULL; + + flushSnapshotToDisk(); + } + + return true; +} + +bool CConsensusAccountPool::getPKIndexBySortedIndex(uint16_t &pkIndex, uint160 &pkhash, std::list> consensusList, int sortedIndex) +{ + LogPrintf("[CConsensusAccountPool::getPKIndexBySortedIndex] To find the list of candidates size=%d , To find index=%d\n", consensusList.size(), sortedIndex); + std::list >::iterator listit = consensusList.begin(); + for (int i=0; igetPubicKey160hash(); + return ContainPK(pkhash, pkIndex); +} + +bool CConsensusAccountPool::GetTimeoutIndexs(const std::shared_ptr pblock, SnapshotClass lastsnapshot, std::map& timeoutindexs) +{ + timeoutindexs.clear(); + + //Calculate the number of blocks from the current block to the end of the snapshot + int timediff = (pblock->nPeriodStartTime + pblock->nTimePeriod * BLOCK_GEN_TIME + BLOCK_GEN_TIME) / 1000 - lastsnapshot.timestamp; + + if (timediff > BLOCK_GEN_TIME/1000) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] there is Timeout Missing blocks\n"); + + //Calculate the sorting of the current wheel meeting + uint64_t currentmeetingcachedtime = (pblock->nPeriodStartTime - CACHED_BLOCK_COUNT * BLOCK_GEN_TIME) /1000; + SnapshotClass currentmeetingcachedsnapshot; + if (!GetSnapshotByTime(currentmeetingcachedsnapshot, currentmeetingcachedtime)) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] The list of candidates for the cache failed\n"); + return false; + } + + std::list> consensusList; + consensusList.clear(); + std::set::iterator candidateit; + for (candidateit = currentmeetingcachedsnapshot.curCandidateIndexList.begin(); + candidateit != currentmeetingcachedsnapshot.curCandidateIndexList.end(); + candidateit++) + { + std::shared_ptr tmpaccount = std::make_shared(candidatelist.at(*candidateit)); + consensusList.push_back(tmpaccount); + } + bool result = CDpocMining::Instance().GetMeetingList(pblock->nPeriodStartTime, consensusList); + if (!result) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] Calculate the current round meeting sort failure\n"); + return false; + } + + //Determine whether the block at the end of the snapshot is the same wheel meeting as the current block, or the tail block of the previous session + if (lastsnapshot.meetingstarttime == pblock->nPeriodStartTime || lastsnapshot.timestamp == pblock->nPeriodStartTime / 1000) + { + int index = 0; + while ((pblock->nPeriodStartTime + index*BLOCK_GEN_TIME + BLOCK_GEN_TIME) / 1000 <= lastsnapshot.timestamp) + { + index++; + } + + //Find the index index of the missing block and start processing the timeout + for (index; index < pblock->nTimePeriod; index++) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] this round public key HASH sort %d node has no block,Record the timeout\n", index); + uint16_t tmpPubkeyIndex; + uint160 tmpPubkeyHash; + if (!getPKIndexBySortedIndex(tmpPubkeyIndex, tmpPubkeyHash, consensusList, index)) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] not find public key HASH index!\n", index); + continue; + } + + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] public key HASH index %d record one timeout\n", tmpPubkeyIndex); + int count = 1; + if (timeoutindexs.count(tmpPubkeyIndex)) + { + count = timeoutindexs[tmpPubkeyIndex] ++; + } + timeoutindexs[tmpPubkeyIndex] = count; + } + } + else + { + int index = 0; + + for (index = 0; index < pblock->nTimePeriod; index++) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] this round sort %d node has no block,Record the timeout\n", index); + uint16_t tmpPubkeyIndex; + uint160 tmpPubkeyHash; + if (!getPKIndexBySortedIndex(tmpPubkeyIndex, tmpPubkeyHash, consensusList, index)) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] not find public key HASH index!\n", index); + continue; + } + + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] public key HASH Index %d record one timeout\n", tmpPubkeyIndex); + int count = 1; + if (timeoutindexs.count(tmpPubkeyIndex)) + { + count = timeoutindexs[tmpPubkeyIndex] ++; + } + timeoutindexs[tmpPubkeyIndex] = count; + } + + currentmeetingcachedtime = (lastsnapshot.meetingstarttime - CACHED_BLOCK_COUNT * BLOCK_GEN_TIME) / 1000; + if (!GetSnapshotByTime(currentmeetingcachedsnapshot, currentmeetingcachedtime)) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] get the previous round cashe CandidateList false\n"); + return false; + } + consensusList.clear(); + std::set::iterator candidateit; + for (candidateit = currentmeetingcachedsnapshot.curCandidateIndexList.begin(); + candidateit != currentmeetingcachedsnapshot.curCandidateIndexList.end(); + candidateit++) + { + std::shared_ptr tmpaccount = std::make_shared(candidatelist.at(*candidateit)); + consensusList.push_back(tmpaccount); + } + bool result = CDpocMining::Instance().GetMeetingList(lastsnapshot.meetingstarttime, consensusList); + if (!result) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] The calculation of the previous session failed\n"); + return false; + } + + //Calculate the last round of missing blocks to the end + while ((lastsnapshot.meetingstarttime + index*BLOCK_GEN_TIME + BLOCK_GEN_TIME) / 1000 <= lastsnapshot.timestamp) + { + index++; + } + + //Find the index index of the missing block and start processing the timeout + for (index; lastsnapshot.meetingstarttime + index*BLOCK_GEN_TIME + BLOCK_GEN_TIME <= lastsnapshot.meetingstoptime; index++) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] The previous round public key HASH sort %d node has no block,Record the timeout\n", index); + uint16_t tmpPubkeyIndex; + uint160 tmpPubkeyHash; + if (!getPKIndexBySortedIndex(tmpPubkeyIndex, tmpPubkeyHash, consensusList, index)) + { + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] No index of the public key HASH was found!%d \n", index); + continue; + } + + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] publickey HASH %s Index %d record one timeout\n", tmpPubkeyHash.GetHex().c_str(), tmpPubkeyIndex); + int count = 1; + if (timeoutindexs.count(tmpPubkeyIndex)) + { + count = timeoutindexs[tmpPubkeyIndex] ++; + } + timeoutindexs[tmpPubkeyIndex] = count; + } + + } + } + return true; +} + +CAmount CConsensusAccountPool::GetCurDepositAdjust(uint160 pkhash, uint32_t blockheight) +{ + CAmount deposit = Params().MIN_DEPOSI; + + for (std::vector::iterator trustit = trustPKHashList.begin(); + trustit != trustPKHashList.end(); trustit++) + { + if (pkhash.GetHex() == *trustit) + { + LogPrintf("[CConsensusAccountPool::GetCurDepositThreshold] trust publickey,cash pledge=%d (%f IPC)\n", deposit, (double)deposit / COIN); + return deposit; + } + } + + uint32_t currentblockheight = blockheight; + uint32_t consultblockheight = (currentblockheight - 1) / 1000; + consultblockheight *= 1000; + if (consultblockheight > chainActive.Tip()->nHeight) + consultblockheight = chainActive.Tip()->nHeight; + CBlockIndex* pblockindex = chainActive[consultblockheight]; + if (NULL == pblockindex) + { + LogPrintf("[CConsensusAccountPool::GetCurDepositThreshold] NULL = pblockindex!!!! \n"); + return Params().MIN_DEPOSI * 34; + } + uint32_t count = pblockindex->nPeriodCount; + LogPrintf("[CConsensusAccountPool::GetCurDepositThreshold] get MAX[%d block, current best lock]meeting num):%d\n", consultblockheight, count); + + if (count < 20) + { + deposit = Params().MIN_DEPOSI; + } + else if (count < 30) + { + deposit = Params().MIN_DEPOSI * 2; + } + else if (count < 40) + { + deposit = Params().MIN_DEPOSI * 3; + } + else if (count < 50) + { + deposit = Params().MIN_DEPOSI * 5; + } + else if (count < 60) + { + deposit = Params().MIN_DEPOSI * 8; + } + else if (count < 70) + { + deposit = Params().MIN_DEPOSI * 13; + } + else if (count < 100) + { + deposit = Params().MIN_DEPOSI * 21; + } + else + { + deposit = Params().MIN_DEPOSI * 34; + } + + LogPrintf("[CConsensusAccountPool::GetCurDepositThreshold] Current adjustment costs based on the number of people=%d (%f IPC)\n", deposit, (double)deposit / COIN); + + uint16_t tmpIndex; + if (ContainPK(pkhash, tmpIndex)) + { + + if (blockheight <= Params().ADJUSTDP_BLOCKS) + { + if (0 < candidatelist[tmpIndex].getCredit()) + { + deposit = candidatelist[tmpIndex].getJoinIPC(); + LogPrintf("[CConsensusAccountPool::GetCurDepositThreshold] The deposit fee shall be maintained for the first time during the first year =%d (%f IPC)\n", deposit, (double)deposit / COIN); + return deposit; + } + return deposit; + + } + + int64_t Credit = candidatelist[tmpIndex].getCredit(); + + Credit -= Credit % 1000; + deposit -= (Params().MIN_DEPOSI*Credit) / 10000; + + if (deposit < Params().MIN_DEPOSI) + { + deposit = Params().MIN_DEPOSI; + } + + LogPrintf("[CConsensusAccountPool::GetCurDepositThreshold] Adjust the cost according to the credit value deduction=%d (%f IPC)\n", deposit, (double)deposit / COIN); + + } + + return deposit; +} + + +bool CConsensusAccountPool::listSnapshotsToTime(std::list> &listConsus, int readtime) +{ + listConsus.clear(); + SnapshotClass cachedsnapshot; + if (!GetSnapshotByTime(cachedsnapshot, readtime)) + { + LogPrintf("[CConsensusAccountPool::listSnapshotsToTime] Lookup cache snapshot failed\n"); + return false; + } + + std::set accounts = cachedsnapshot.curCandidateIndexList; + std::set::const_iterator iter; + + for (iter = accounts.begin(); iter != accounts.end(); iter++) + { + + CConsensusAccount acount = candidatelist.at(*iter); + listConsus.emplace_back(std::make_shared(acount)); + + } + + return true; +} + +bool CConsensusAccountPool::listSnapshots(std::list> &listConsus) +{ + int64_t nNowTime = timeService.GetCurrentTimeMillis()/1000; + return listSnapshotsToTime(listConsus, nNowTime); +} + +bool CConsensusAccountPool::analysisConsensusSnapshots() +{ + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] begin\n"); + + int nAllSnapshotSize = 0; + { + writeLock wtlock(rwmutex); + //Load consensus public key HASH + readCandidatelistFromFile(); + + boost::filesystem::path pathIndex = GetDataDir() / m_strSnapshotIndexPath; + boost::filesystem::path pathSnapshot = GetDataDir() / m_strSnapshotPath; + if (boost::filesystem::exists(pathIndex) && boost::filesystem::exists(pathSnapshot)) + { + uint64_t fileSizeSnapshot = boost::filesystem::file_size(pathSnapshot); + uint64_t u64fileSize = boost::filesystem::file_size(pathIndex); + + if ((fileSizeSnapshot > 0)&&(u64fileSize >= m_u64SnapshotIndexSize)) + { + for (uint64_t nBegin = 0; nBegin <= u64fileSize - m_u64SnapshotIndexSize; nBegin += m_u64SnapshotIndexSize) + { + CSerializeDpoc serializeIndex; + CSnapshotIndex sanpshotIndex; + serializeIndex.ReadFromDisk(sanpshotIndex, nBegin, m_strSnapshotIndexPath); + + //Load the snapshot + if (sanpshotIndex.nOffset < fileSizeSnapshot) + { + CSerializeDpoc serializeSnapshot; + SnapshotClass snapshot; + serializeSnapshot.ReadFromDisk(snapshot, sanpshotIndex.nOffset, m_strSnapshotPath); + + snapshotlist[sanpshotIndex.nHeight] = snapshot; + m_mapSnapshotIndex[sanpshotIndex.nHeight] = sanpshotIndex.nOffset; + + if(snapshotlist.size() > SNAPSHOTLENGTH) + { + snapshotlist.erase(snapshotlist.begin()); + m_mapSnapshotIndex.erase(m_mapSnapshotIndex.begin()); + } + + ++nAllSnapshotSize; + + if ((sanpshotIndex.nOffset+getSnapshotSize(snapshot)) >= fileSizeSnapshot) + { + break; + } + } + else + { + break; + } + }//for + + } + } + + if (1 < snapshotlist.size()) + { + std::map::iterator iterMap = snapshotlist.end(); + + --iterMap; + int nHeightTest = iterMap->first; + uint32_t nHeight = iterMap->first; + nAllSnapshotSize = iterMap->first; + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] snapshotlist height TEST:%d\n", nHeightTest); + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] snapshotlist height :%d\n", nHeight); + } + + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] snapshotlist size:%d\n", snapshotlist.size()); + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] Read the file completion and read the height from the file %d\n", nAllSnapshotSize); + } + + int nChainHeight = 0; + { + LOCK(cs_main); + nChainHeight = chainActive.Height(); + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] chainActive height:%d\n", nChainHeight); + } + + //chainActive height < Snapshot height + if (nChainHeight < nAllSnapshotSize) + { + //Truncate the candidate list and write to the file + rollbackCandidatelist(nChainHeight); + writeCandidatelistToFile(); + boost::filesystem::path pathCandidatelist = GetDataDir() / m_strCandidatelistPath; + FILE *fileCandidatelist = fopen(pathCandidatelist.string().c_str(), "ab+"); + if (fileCandidatelist != NULL) + { + FileCommit(fileCandidatelist); + } + fclose(fileCandidatelist); + + //The snapshot file height + uint64_t u64SnapshotFileSize = m_mapSnapshotIndex[nChainHeight +1]; + //snapshotIndex file height + int nTruncateNum = nAllSnapshotSize - nChainHeight; + + std::map::iterator iterIndex = m_mapSnapshotIndex.find(nChainHeight+1); + if (iterIndex != m_mapSnapshotIndex.end()) + { + m_mapSnapshotIndex.erase(iterIndex, m_mapSnapshotIndex.end()); + } + + //Remove the redundant snapshot list + std::map::iterator iterSnapShot = snapshotlist.find(nChainHeight + 1); + if (iterSnapShot != snapshotlist.end()) + { + snapshotlist.erase(iterSnapShot, snapshotlist.end()); + } + + //Truncate the SnapshotIndex file + boost::filesystem::path pathTmpIndex = GetDataDir() / m_strSnapshotIndexPath; + uint64_t fileSizeIndex = 0; + if (boost::filesystem::exists(pathTmpIndex)) + { + fileSizeIndex = boost::filesystem::file_size(pathTmpIndex); + fileSizeIndex = fileSizeIndex - m_u64SnapshotIndexSize*nTruncateNum; + + FILE *fileIndex = fopen(pathTmpIndex.string().c_str(), "ab+"); + if (fileIndex) + { + TruncateFile(fileIndex, fileSizeIndex); + } + fclose(fileIndex); + } + + //Truncate the Snapshot file + boost::filesystem::path pathTmp = GetDataDir() / m_strSnapshotPath; + uint64_t fileSize = 0; + if (boost::filesystem::exists(pathTmp)) + { + FILE *file = fopen(pathTmp.string().c_str(), "ab+"); + if (file) + { + TruncateFile(file, u64SnapshotFileSize); + } + fclose(file); + } + + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] chainActive heightnHeight <= nAllSnapshotSize)&& nAllSnapshotSize !=0) + { + { + LOCK(cs_main); + pblockindex = chainActive.Next(pblockindex); + } + continue; + } + + CBlock block; + { + LOCK(cs_main); + if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())){ + LogPrintf("[analysisConsensusSnapshots] Can't read block from disk"); + continue; + } + } + std::shared_ptr shared_pblock = std::make_shared(block); + DPOC_errtype errorType; + if (!verifyDPOCBlock(shared_pblock, pblockindex->nHeight, errorType)) + return false; + + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] ready to push block height = %d\n", pblockindex->nHeight); + if (!pushDPOCBlock(shared_pblock, pblockindex->nHeight)) + return false; + + { + LOCK(cs_main); + pblockindex = chainActive.Next(pblockindex); + } + } + } + + analysisfinished = true; + LogPrintf("[CConsensusAccountPool::analysisConsensusSnapshots] end by The local block is completed and returned correctly\n"); + return true; +} + +bool CConsensusAccountPool::SetConsensusStatus(const std::string &strStatus, const std::string &strHash) +{ + std::string strPublicKey; + CDpocInfo::Instance().getLocalAccoutVar(strPublicKey); + + bool bRet = false; + if ((strPublicKey == strHash)&&(!strPublicKey.empty())) + { + bRet = CDpocInfo::Instance().SetConsensusStatus(strStatus, strPublicKey); + } + + LogPrintf("[CConsensusAccountPool::SetConsensusStatus] public key %s,Set the consensus return value %d\n", strPublicKey, bRet); +} + +uint64_t CConsensusAccountPool::getCSnapshotIndexSize() +{ + CSnapshotIndex snapshotIndex; + CDataStream ssSnapshotIndex(SER_DISK, CLIENT_VERSION); + ssSnapshotIndex << snapshotIndex; + + return ssSnapshotIndex.size(); +} + +uint64_t CConsensusAccountPool::getSnapshotSize(SnapshotClass &snapshot) +{ + CDataStream ssSnapshot(SER_DISK, CLIENT_VERSION); + ssSnapshot << snapshot; + + return ssSnapshot.size(); +} + +void CConsensusAccountPool::writeCandidatelistToFile() +{ + boost::filesystem::path pathTmp = GetDataDir() / m_strCandidatelistPath; + if (boost::filesystem::exists(pathTmp)) + { + boost::filesystem::remove(pathTmp); + } + CSerializeDpoc serializeAccount; + std::vector::iterator iter = candidatelist.begin(); + for (; iter != candidatelist.end();++iter) + { + serializeAccount.WriteToDisk(*iter, m_strCandidatelistPath); + } +} + +void CConsensusAccountPool::writeCandidatelistToFileByHeight(uint32_t nHeight) +{ + boost::filesystem::path pathTmp = GetDataDir() / m_strCandidatelistPath; + if (boost::filesystem::exists(pathTmp)) + { + boost::filesystem::remove(pathTmp); + } + CSerializeDpoc serializeAccount; + std::vector::iterator iter = candidatelist.begin(); + for (; iter != candidatelist.end(); ++iter) + { + if (iter->getHeight()<= nHeight) + { + serializeAccount.WriteToDisk(*iter, m_strCandidatelistPath); + } + } +} + +bool CConsensusAccountPool::writeCandidatelistToFile(const CConsensusAccount &account) +{ + CSerializeDpoc serializeAccount; + return serializeAccount.WriteToDiskWithFlush(account, m_strCandidatelistPath); +} + +bool CConsensusAccountPool::readCandidatelistFromFile() +{ + boost::filesystem::path pathTmp = GetDataDir() / m_strCandidatelistPath; + if (boost::filesystem::exists(pathTmp)) + { + uint64_t u64fileSize = boost::filesystem::file_size(pathTmp); + + uint64_t u64Seek = m_u64ConsensAccountSize; + if (u64fileSize >= u64Seek) + { + for (int nIndex = 0; nIndex <= u64fileSize - u64Seek; nIndex += u64Seek) + { + CConsensusAccount account; + CSerializeDpoc serializeAccount; + serializeAccount.ReadFromDisk(account, nIndex, m_strCandidatelistPath); + candidatelist.push_back(account); + } + } + } + return false; +} + +uint64_t CConsensusAccountPool::getConsensAccountSize() +{ + CConsensusAccount account; + CDataStream ssConsensusAccount(SER_DISK, CLIENT_VERSION); + ssConsensusAccount << account; + return ssConsensusAccount.size(); +} + +void CConsensusAccountPool::flushSnapshotToDisk() +{ + boost::filesystem::path pathTmp = GetDataDir() / m_strSnapshotPath; + FILE *file = fopen(pathTmp.string().c_str(), "ab+"); + if (file != NULL) + { + FileCommit(file); + } + fclose(file); + + boost::filesystem::path pathTmpIndex = GetDataDir() / m_strSnapshotIndexPath; + FILE *fileIndex = fopen(pathTmpIndex.string().c_str(), "ab+"); + if (fileIndex != NULL) + { + FileCommit(fileIndex); + } + fclose(fileIndex); + + boost::filesystem::path pathCandidatelist = GetDataDir() / m_strCandidatelistPath; + FILE *fileCandidatelist = fopen(pathCandidatelist.string().c_str(), "ab+"); + if (fileCandidatelist != NULL) + { + FileCommit(fileCandidatelist); + } + fclose(fileCandidatelist); +} + +bool CConsensusAccountPool::verifyBlockSign(const CBlock *pblock) +{ + CPubKey recvPublickey; + std::vector recvSign; + if (!getPublicKeyFromBlock(pblock, recvPublickey, recvSign)) { + LogPrintf("[CConsensusAccountPool::verifyBlockSign] getPublicKeyFromBlock is badlly\n"); + return false; + } + CKeyID pubicKey160hash = recvPublickey.GetID(); + + uint256 hash; + CAmount nfee = pblock->vtx[0]->GetValueOut(); + CHash256 hashoperator; + + hashoperator.Write((unsigned char*)&(pblock->nPeriodStartTime), sizeof(pblock->nPeriodStartTime));//8Byte + hashoperator.Write((unsigned char*)&(pblock->nPeriodCount), sizeof(pblock->nPeriodCount));//4Byte + hashoperator.Write((unsigned char*)&(pblock->nTimePeriod), sizeof(pblock->nTimePeriod));//4Byte + hashoperator.Write((unsigned char*)&(pblock->nTime), sizeof(pblock->nTime));//4Byte + hashoperator.Write((unsigned char*)&nfee, sizeof(nfee));//8Byte + hashoperator.Write((unsigned char*)pblock->GetBlockHeader().hashPrevBlock.begin(), 32);//256bit + hashoperator.Finalize(hash.begin()); + + unsigned char dataBuff[10240]; + int datalen = 0; + memcpy(dataBuff, (unsigned char*)&(pblock->nPeriodStartTime), sizeof(pblock->nPeriodStartTime)); + datalen += sizeof(pblock->nPeriodStartTime); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nPeriodCount), sizeof(pblock->nPeriodCount)); + datalen += sizeof(pblock->nPeriodCount); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nTimePeriod), sizeof(pblock->nTimePeriod)); + datalen += sizeof(pblock->nTimePeriod); + memcpy(dataBuff + datalen, (unsigned char*)&(pblock->nTime), sizeof(pblock->nTime)); + datalen += sizeof(pblock->nTime); + memcpy(dataBuff + datalen, (unsigned char*)&nfee, sizeof(nfee)); + datalen += sizeof(nfee); + memcpy(dataBuff + datalen, (unsigned char*)pblock->GetBlockHeader().hashPrevBlock.begin(), 32);//256 bits + datalen += 32; + std::string datahex; + for (int i = 0; i < datalen; i++) + { + char tmp[3]; + sprintf(tmp, "%02x", dataBuff[i]); + datahex = datahex + std::string(tmp); + } + LogPrintf("[CConsensusAccountPool::verifyBlockSign] Attestation clear=%s , clear HASH=%s , publickey HASH=%s\n", datahex.c_str(), hash.GetHex().c_str(), pubicKey160hash.GetHex().c_str()); + + if (recvPublickey.Verify(hash, recvSign)) { + LogPrintf("[CConsensusAccountPool::verifyBlockSign] recvPublickey.Verify recvSign is OK\n"); + } + else { + LogPrintf("[CConsensusAccountPool::verifyBlockSign] The bulk information check failed!The block is forged, unable to verify identity, and cannot be penalized\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::verifyBlockSign] end true\n"); + return true; +} + +void CConsensusAccountPool::getTrustList(std::vector &trustList) +{ + std::copy(trustPKHashList.begin(), trustPKHashList.end(), std::back_inserter(trustList)); +} + +bool CConsensusAccountPool::getSignPkByBlockIndex(const CBlockIndex *pindexTest, CKeyID &pubicKey160hash) +{ + std::shared_ptr pblockNew = std::make_shared(); + if (!ReadBlockFromDisk(*pblockNew, pindexTest, Params().GetConsensus())) + { + LogPrintf("[GetTrustNodeHeightOnChain] end by false ReadBlockFromDisk return false\n"); + return false; + } + + //Gets the public key and the signature string in the block + CPubKey recvPublickey; + std::vector recvSign; + if (!getPublicKeyFromBlock(pblockNew.get(), recvPublickey, recvSign)) + { + LogPrintf("[GetTrustNodeHeightOnChain] end by false getPublicKeyFromBlock return false\n"); + return false; + } + + pubicKey160hash = recvPublickey.GetID(); + return true; +} + +bool CConsensusAccountPool::getConsensusListByBlock(const CBlock& block, std::set &setAccounts, std::list> &consensusList) +{ + int64_t curStarttime = block.nPeriodStartTime / 1000; + SnapshotClass meetingStartSnapshot; + if (!GetSnapshotByTime(meetingStartSnapshot, curStarttime)) + { + LogPrintf("[CConsensusAccountPool::getConsensusListByBlock] A snapshot of the start time of this session failed\n"); + return false; + } + + uint64_t meetingstarttime = meetingStartSnapshot.meetingstoptime; + int foundcachedcount = CACHED_BLOCK_COUNT; + if (meetingStartSnapshot.blockHeight < Params().CHECK_START_BLOCKCOUNT) + { + foundcachedcount = meetingStartSnapshot.blockHeight < CACHED_BLOCK_COUNT ? meetingStartSnapshot.blockHeight : CACHED_BLOCK_COUNT; + } + uint32_t cachedTime = (meetingStartSnapshot.meetingstoptime - foundcachedcount * BLOCK_GEN_TIME) / 1000; + LogPrintf("[CConsensusAccountPool::getConsensusListByBlock] The calculated cache time is %d \n", cachedTime); + + SnapshotClass cachedsnapshot; + if (!GetSnapshotByTime(cachedsnapshot, cachedTime)) + { + LogPrintf("[CConsensusAccountPool::getConsensusListByBlock] The cache time corresponds to a snapshot failure, and the block information is problematic\n"); + return false; + } + + setAccounts = cachedsnapshot.curCandidateIndexList; + consensusList.clear(); + std::set::iterator candidateit; + for (candidateit = setAccounts.begin(); candidateit != setAccounts.end(); candidateit++) + { + std::shared_ptr tmpaccount = std::make_shared(candidatelist.at(*candidateit)); + consensusList.push_back(tmpaccount); + } + + return true; +} + +bool CConsensusAccountPool::checkPackagerInCurrentList(const CBlock& block) +{ + std::set setAccounts; + std::list> consensusList; + if (!getConsensusListByBlock(block, setAccounts, consensusList)) + { + LogPrintf("[CConsensusAccountPool::checkPackagerInCurrentList] end by false getConsensusListByBlock return false\n"); + return false; + } + + CPubKey recvPublickey; + std::vector recvSign; + if (!getPublicKeyFromBlock(&block, recvPublickey, recvSign)) + { + LogPrintf("[CConsensusAccountPool::checkPackagerInCurrentList] end by false getPublicKeyFromBlock return false\n"); + return false; + } + CKeyID pubicKey160hash = recvPublickey.GetID(); + + std::list>::iterator iter = consensusList.begin(); + for (; iter != consensusList.end(); ++iter) + { + if (pubicKey160hash == (*iter)->getPubicKey160hash()) + { + LogPrintf("[CConsensusAccountPool::checkPackagerInCurrentList] end by true\n"); + return true; + } + } + + LogPrintf("[CConsensusAccountPool::checkPackagerInCurrentList] end by false\n"); + return false; +} + +bool CConsensusAccountPool::setTotalAmount(const CBlock &block,bool bAdd) +{ + CPubKey recvPublickey; + std::vector recvSign; + if (!getPublicKeyFromBlock(&block, recvPublickey, recvSign)) { + + LogPrintf("[CConsensusAccountPool::setTotalAmount] getPublicKeyFromBlock return false\n"); + return false; + } + CKeyID pubicKey160hash = recvPublickey.GetID(); + + std::string strPublickey; + CDpocInfo::Instance().getLocalAccoutVar(strPublickey); + if (pubicKey160hash.GetHex() == strPublickey) + { + int64_t n64Amount = CDpocInfo::Instance().GetTotalAmount(); + if (bAdd) + { + n64Amount += block.vtx[0]->vout[0].nValue; + } + else + { + n64Amount -= block.vtx[0]->vout[0].nValue; + } + + CDpocInfo::Instance().SetTotalAmount(n64Amount); + LogPrintf("[CConsensusAccountPool::setTotalAmount] return true at TotalAmount = %d\n", n64Amount); + } + return true; +} + +bool CConsensusAccountPool::verifyPkInCandidateListByTime(int64_t curStarttime, CKeyID &pubicKey160hash) +{ + //Look for a snapshot of the current session's start time + SnapshotClass meetingStartSnapshot; + if (!GetSnapshotByTime(meetingStartSnapshot, curStarttime)) + { + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] A snapshot of the start time of this session failed\n"); + return false; + } + + uint64_t meetingstarttime = meetingStartSnapshot.meetingstoptime; + int foundcachedcount = CACHED_BLOCK_COUNT; + if (meetingStartSnapshot.blockHeight < Params().CHECK_START_BLOCKCOUNT) + { + foundcachedcount = meetingStartSnapshot.blockHeight < CACHED_BLOCK_COUNT ? meetingStartSnapshot.blockHeight : CACHED_BLOCK_COUNT; + } + uint32_t cachedTime = (meetingStartSnapshot.meetingstoptime - foundcachedcount * BLOCK_GEN_TIME) / 1000; + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] The calculated cache time is %d \n", cachedTime); + + SnapshotClass cachedsnapshot; + if (!GetSnapshotByTime(cachedsnapshot, cachedTime)) + { + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] Take the cache time corresponding to the snapshot failure, block information has a problem\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] Find the height of the block= %d , Find the block time= %d \n", + cachedsnapshot.blockHeight, cachedsnapshot.timestamp); + + std::set accounts = cachedsnapshot.curCandidateIndexList; + printsets(accounts); + + //If the current public key is on the blacklist, reject the block + uint16_t pkindex; + if (!ContainPK(pubicKey160hash, pkindex)) + { + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] Packaging public key unknown, reject\n"); + return false; + } + + std::set::iterator iter = cachedsnapshot.curCandidateIndexList.begin(); + for (; iter != cachedsnapshot.curCandidateIndexList.end(); ++iter) + { + if (pkindex == *iter) + { + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] return true\n"); + return true; + } + } + + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] return false\n"); + return false; +} + +bool CConsensusAccountPool::verifyPkInCandidateListByIndex(CBlockIndex *pIndex, CKeyID &pubicKey160hash) +{ + if (!verifyPkInCandidateListByTime(pIndex->nPeriodStartTime / 1000, pubicKey160hash)) + { + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateListByIndex] return false\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateListByIndex] return true\n"); + return true; +} + +bool CConsensusAccountPool::verifyPkInCandidateList(const std::shared_ptr pblock, CKeyID &pubicKey160hash) +{ + if (!verifyPkInCandidateListByTime(pblock->nPeriodStartTime / 1000, pubicKey160hash)) + { + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] return false\n"); + return false; + } + + LogPrintf("[CConsensusAccountPool::verifyPkInCandidateList] return true\n"); + return true; +} + +bool CConsensusAccountPool::verifyPkIsTrustNode(std::string strPublicKey) +{ + std::vector vecTrustList; + getTrustList(vecTrustList); + std::vector::iterator iterTrust = vecTrustList.begin(); + for (; iterTrust != vecTrustList.end(); ++iterTrust) + { + if (*iterTrust == strPublicKey) + { + return true; + } + } + + return false; +} + +bool CConsensusAccountPool::verifyPkIsTrustNode(CKeyID &pubicKey160hash) +{ + return verifyPkIsTrustNode(pubicKey160hash.GetHex()); +} diff --git a/.svn/pristine/05/058420406726db15638652e2a362759c94031b68.svn-base b/.svn/pristine/05/058420406726db15638652e2a362759c94031b68.svn-base new file mode 100644 index 0000000..cbebd38 --- /dev/null +++ b/.svn/pristine/05/058420406726db15638652e2a362759c94031b68.svn-base @@ -0,0 +1,152 @@ +// Copyright (c) 2011-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_WALLETFRAME_H +#define BITCOIN_QT_WALLETFRAME_H + +#include +#include + +#include + +#include +#include +#include + +class BitcoinGUI; +class ClientModel; +class PlatformStyle; +class SendCoinsRecipient; +class WalletModel; +class WalletView; + +class Settingwidget; +class SendCoinsAffrimWidget; +class AddressTableModel; +class InfoWidget; +class sendhistory; +class RecvHistory; +class sendipchistory; +class recvipchistory; +class SendTokenHistory; +class RecvTokenHistory; +class SendResultWidget; + +class walletpagebuttons; +class ipcSelectAddress; +QT_BEGIN_NAMESPACE +class QStackedWidget; +QT_END_NAMESPACE + +/** + * A container for embedding all wallet-related + * controls into BitcoinGUI. The purpose of this class is to allow future + * refinements of the wallet controls with minimal need for further + * modifications to BitcoinGUI, thus greatly simplifying merges while + * reducing the risk of breaking top-level stuff. + */ + +class WalletFrame : public QFrame +{ + Q_OBJECT + +public: + + explicit WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui = 0); + ~WalletFrame(); + + + enum TabTypes { + TAB_sendipc = 1, + TAB_recvipc = 2, + TAB_sendtoken = 5, + TAB_recvtoken = 6, + TAB_send = 7, + TAB_recv = 8 + }; + + QStackedWidget* getwaletstack(); + void setClientModel(ClientModel *clientModel); + + bool addWallet(const QString& name, WalletModel *walletModel); + bool setCurrentWallet(const QString& name); + bool removeWallet(const QString &name); + void removeAllWallets(); + void headshowchaininfo(int concount,int count, bool header); + +Q_SIGNALS: + /** Notify that the user has requested more information about the out-of-sync warning */ + void requestedSyncWarningInfo(); + void requestoverwidget(); + +private: + QTimer *pollTimer =NULL; + QStackedWidget *walletStack; + QStackedWidget *walletStackBranchPage; + QWidget * pwalletStackwidget; + + walletpagebuttons* walletpagebuttonswidget; + + Settingwidget* settingwidgetPage; + + InfoWidget* ChainInfoPage; + + SendCoinsAffrimWidget* SendCoinsAffrimPage; + + sendhistory* sendpage =NULL; + + RecvHistory* recvpage = NULL; + + sendipchistory* sendipcpage= NULL; + + recvipchistory* recvipcpage= NULL; + + SendTokenHistory* sendtokenpage= NULL ; + RecvTokenHistory* recvtokenpage= NULL; + + BitcoinGUI *gui; + ClientModel *clientModel; + WalletModel *walletmd; + ClientModel *clientmd; + QModelIndex sel = QModelIndex(); + int selid; + QMap mapWalletViews; + + bool bOutOfSync; + + const PlatformStyle *platformStyle; + + WalletView *currentWalletView(); + +public Q_SLOTS: + + void StatusInfoUpdate(); + /** Switch to overview (home) page */ + void gotoOverviewPage(); + /** Switch to receive coins page */ + void gotoReceiveCoinsPage(); + /** Switch to send coins page */ + void gotoSendCoinsPage(QString addr = ""); + /** Switch to ipc page */ + void gotoIpcPage(); + /** Switch to set page */ + void gotoSetPage(); + void gotoTallyPage(); + void gotoeCoinPage(); + void showwwDetails(QModelIndex index,quint8 t); + void GoToChainInfoPage(); + + /** Encrypt the wallet */ + void encryptWallet(bool status); + /** Backup the wallet */ + void backupWallet(); + + /** Ask for passphrase to unlock wallet temporarily */ + void unlockWallet(); + + /** Pass on signal over requested out-of-sync-warning information */ + void outOfSyncWarningClicked(); +}; + +#endif // BITCOIN_QT_WALLETFRAME_H diff --git a/.svn/pristine/09/097e8c1bf077f8f1112e5af05a6ade89f0364dde.svn-base b/.svn/pristine/09/097e8c1bf077f8f1112e5af05a6ade89f0364dde.svn-base new file mode 100644 index 0000000..cf7ed08 --- /dev/null +++ b/.svn/pristine/09/097e8c1bf077f8f1112e5af05a6ade89f0364dde.svn-base @@ -0,0 +1,4641 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "amount.h" +#include "base58.h" +#include "chain.h" +#include "consensus/validation.h" +#include "core_io.h" +#include "init.h" +#include "validation.h" +#include "net.h" +#include "policy/policy.h" +#include "policy/rbf.h" +#include "rpc/server.h" +#include "script/sign.h" +#include "timedata.h" +#include "util.h" +#include "utilmoneystr.h" +#include "wallet.h" +#include "walletdb.h" + +#include + +#include + +#include + +#include "script/ismine.h" +#include "dpoc/DpocInfo.h" +#include "dpoc/ConsensusAccountPool.h" + + +using namespace std; + +int64_t nWalletUnlockTime; +static CCriticalSection cs_nWalletUnlockTime; +extern bool b_TestTxLarge; +std::string HelpRequiringPassphrase() +{ + return pwalletMain && pwalletMain->IsCrypted() + ? "\nRequires wallet passphrase to be set with walletpassphrase call." + : ""; +} + +bool EnsureWalletIsAvailable(bool avoidException) +{ + if (!pwalletMain) + { + if (!avoidException) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + else + return false; + } + return true; +} + +void EnsureWalletIsUnlocked() +{ + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); +} + +void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) +{ + int confirms = wtx.GetDepthInMainChain(); + entry.push_back(Pair("confirmations", confirms)); + if (wtx.IsCoinBase()) + entry.push_back(Pair("generated", true)); + if (confirms > 0) + { + entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); + entry.push_back(Pair("blockindex", wtx.nIndex)); + entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); + } else { + entry.push_back(Pair("trusted", wtx.IsTrusted())); + } + uint256 hash = wtx.GetHash(); + entry.push_back(Pair("txid", hash.GetHex())); + UniValue conflicts(UniValue::VARR); + BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) + conflicts.push_back(conflict.GetHex()); + entry.push_back(Pair("walletconflicts", conflicts)); + entry.push_back(Pair("time", wtx.GetTxTime())); + entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); + + // Add opt-in RBF status + std::string rbfStatus = "no"; + if (confirms <= 0) { + LOCK(mempool.cs); + RBFTransactionState rbfState = IsRBFOptIn(wtx, mempool); + if (rbfState == RBF_TRANSACTIONSTATE_UNKNOWN) + rbfStatus = "unknown"; + else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125) + rbfStatus = "yes"; + } + entry.push_back(Pair("bip125-replaceable", rbfStatus)); + + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const UniValue& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name"); + return strAccount; +} + +UniValue getnewaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1) + throw runtime_error( + "getnewaddress ( \"account\" )\n" + "\nReturns a new Bitcoin address for receiving payments.\n" + "If 'account' is specified (DEPRECATED), it is added to the address book \n" + "so payments received with the address will be credited to 'account'.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) DEPRECATED. The account name for the address to be linked to. If not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n" + "\nResult:\n" + "\"address\" (string) The new bitcoin address\n" + "\nExamples:\n" + + HelpExampleCli("getnewaddress", "") + + HelpExampleRpc("getnewaddress", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (request.params.size() > 0) + strAccount = AccountFromValue(request.params[0]); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + // Generate a new key that is added to wallet + CPubKey newKey; + if (!pwalletMain->GetKeyFromPool(newKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + CKeyID keyID = newKey.GetID(); + + pwalletMain->SetAddressBook(keyID, "", "receive"); + + return CBitcoinAddress(keyID).ToString(); +} + + +CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) +{ + CPubKey pubKey; + if (!pwalletMain->GetAccountPubkey(pubKey, strAccount, bForceNew)) { + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + } + + return CBitcoinAddress(pubKey.GetID()); +} + +UniValue getaccountaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "getaccountaddress \"account\"\n" + "\nDEPRECATED. Returns the current IPChain address for receiving payments to this account.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The account name for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n" + "\nResult:\n" + "\"address\" (string) The account IPChain address\n" + "\nExamples:\n" + + HelpExampleCli("getaccountaddress", "") + + HelpExampleCli("getaccountaddress", "\"\"") + + HelpExampleCli("getaccountaddress", "\"myaccount\"") + + HelpExampleRpc("getaccountaddress", "\"myaccount\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(request.params[0]); + + UniValue ret(UniValue::VSTR); + + ret = GetAccountAddress(strAccount).ToString(); + return ret; +} + + +UniValue getrawchangeaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1) + throw runtime_error( + "getrawchangeaddress\n" + "\nReturns a new Bitcoin address, for receiving change.\n" + "This is for use with raw transactions, NOT normal use.\n" + "\nResult:\n" + "\"address\" (string) The address\n" + "\nExamples:\n" + + HelpExampleCli("getrawchangeaddress", "") + + HelpExampleRpc("getrawchangeaddress", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + CReserveKey reservekey(pwalletMain); + CPubKey vchPubKey; + if (!reservekey.GetReservedKey(vchPubKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + reservekey.KeepKey(); + + CKeyID keyID = vchPubKey.GetID(); + + return CBitcoinAddress(keyID).ToString(); +} + + +UniValue setaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "setaccount \"address\" \"account\"\n" + "\nDEPRECATED. Sets the account associated with the given address.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to be associated with an account.\n" + "2. \"account\" (string, required) The account to assign the address to.\n" + "\nExamples:\n" + + HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + string strAccount; + if (request.params.size() > 1) + strAccount = AccountFromValue(request.params[1]); + + // Only add the account if the address is yours. + if (IsMine(*pwalletMain, address.Get())) + { + // Detect when changing the account of an address that is the 'unused current key' of another account: + if (pwalletMain->mapAddressBook.count(address.Get())) + { + string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name; + if (address == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + pwalletMain->SetAddressBook(address.Get(), strAccount, "receive"); + } + else + throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); + + return NullUniValue; +} + + +UniValue getaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "getaccount \"address\"\n" + "\nDEPRECATED. Returns the account associated with the given address.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address for account lookup.\n" + "\nResult:\n" + "\"accountname\" (string) the account address\n" + "\nExamples:\n" + + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + string strAccount; + map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) + strAccount = (*mi).second.name; + return strAccount; +} + + +UniValue getaddressesbyaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \"account\"\n" + "\nDEPRECATED. Returns the list of addresses for the given account.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The account name.\n" + "\nResult:\n" + "[ (json array of string)\n" + " \"address\" (string) a bitcoin address associated with the given account\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") + + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount = AccountFromValue(request.params[0]); + + // Find all addresses that have the given account + UniValue ret(UniValue::VARR); + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strName = item.second.name; + if (strName == strAccount) + ret.push_back(address.ToString()); + } + return ret; +} + +UniValue setTesttx(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (b_TestTxLarge) + b_TestTxLarge = false; + else + b_TestTxLarge = true; + + return b_TestTxLarge; +} +static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; + vecSend.push_back(recipient); + if (!pwalletMain->CreateNormalTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + std::cout << "error transaction id=" << wtxNew.GetHash().GetHex() << "\n"; + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + +UniValue sendtoaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) + throw runtime_error( + "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" + "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "4. \"comment_to\" (string, optional) A comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the \n" + " transaction, just kept in your wallet.\n" + "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" + " The recipient will receive less bitcoins than you enter in the amount field.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = AmountFromValue(request.params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + + // Wallet comments + CWalletTx wtx; + if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) + wtx.mapValue["comment"] = request.params[2].get_str(); + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["to"] = request.params[3].get_str(); + + bool fSubtractFeeFromAmount = false; + if (request.params.size() > 4) + fSubtractFeeFromAmount = request.params[4].get_bool(); + + EnsureWalletIsUnlocked(); + + SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//add by xxy +//General trading +static void NormalSendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateNormalTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + { + std::cout << "nFeeRequired" << nFeeRequired << std::endl; + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + } + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue NormalSendToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) + throw runtime_error( + "NormalSendToAddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" + "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "4. \"comment_to\" (string, optional) A comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the \n" + " transaction, just kept in your wallet.\n" + "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" + " The recipient will receive less bitcoins than you enter in the amount field.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = AmountFromValue(request.params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + + // Wallet comments + CWalletTx wtx; + if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) + wtx.mapValue["comment"] = request.params[2].get_str(); + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["to"] = request.params[3].get_str(); + + bool fSubtractFeeFromAmount = false; + if (request.params.size() > 4) + fSubtractFeeFromAmount = request.params[4].get_bool(); + + EnsureWalletIsUnlocked(); + + NormalSendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//IPC Reg Tx +static void IPCRegSendMoney(std::string strReglabel,const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateIPCRegTransaction(strReglabel, vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCRegisterToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "IPCRegisterToAddress \"address\" \"icplabel\" (amount \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"icplabel\" (string) ipclabel...\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = 0; + CWalletTx wtx; + std::string strReglabel = ""; + if (request.params.size() > 1 && !request.params[1].isNull() && !request.params[1].get_str().empty()) + strReglabel = request.params[1].get_str(); + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + IPCRegSendMoney(strReglabel, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//IPCSend Transaction +static void IPCSdSendMoney(std::string& txid,int Index,const CTxDestination &address,CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + + if (!pwalletMain->CreateIPCSendTransaction(txid,Index,vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCSendToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 3) + throw runtime_error( + "IPCSendToAddress \"txid\" \"index\" \"address\" ( subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"txid\" (string) The transaction id of putin.\n" + "2. \"index\" (string)The id of preVout.\n" + "3. \"address\" (string, required) The bitcoin address to send to.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCSendToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" ") + + HelpExampleCli("IPCSendToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" " "false") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::string TXid = request.params[0].get_str(); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + std::string iIndex = request.params[1].get_str(); + int Index = std::atoi(iIndex.c_str()); + if (Index < 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid prevout index"); + CBitcoinAddress address(request.params[2].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = 0; + // Wallet comments + CWalletTx wtx; + bool fSubtractFeeFromAmount = false; + + + EnsureWalletIsUnlocked(); + + IPCSdSendMoney(TXid, Index, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} +//IPC Authorize Transaction +static void IPCAuthorSendMoney(std::string& txid, int Index, const CTxDestination &address,std::string& strLabel, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateIPCAuthorizationTransaction(txid, Index, vecSend, strLabel, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCAuthorToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 4 || request.params.size() > 4) + throw runtime_error( + "IPCAuthorToAddress \"txid\" \"index\" \"address\" \"ipclabel\"( subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1.\"txid\" (string) The transaction id of putin.\n" + "2. \"index\" (string)The id of preVout.\n" + "3. \"address\" (string, required) The bitcoin address to send to.\n" + "4. \"ipclabel\" (string).The ipclabel of IPCAuthorToAddress.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCAuthorToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" " "\"...(ipclabel)\"") + + HelpExampleCli("IPCAuthorToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" " "\"...(ipclabel)\"" "false") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::string TXid = request.params[0].get_str(); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + std::string iIndex = request.params[1].get_str(); + int Index = std::atoi(iIndex.c_str()); + if (Index < 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid prevout index"); + CBitcoinAddress address(request.params[2].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + std::string strIPClabel = request.params[3].get_str(); + // Amount + CAmount nAmount = 0; + // Wallet comments + CWalletTx wtx; + bool fSubtractFeeFromAmount = false; + + + EnsureWalletIsUnlocked(); + + IPCAuthorSendMoney(TXid, Index, address.Get(), strIPClabel,nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +static void JoinCampaignCommand(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + //CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired = 0; + std::string strError; + int nChangePosRet = -1; + if (!pwalletMain->JoinCampaign(address, nValue, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitJoinCampaignTransaction(address, wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} +UniValue decodeinfobyaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "decodeinfobyaddress \"address\"\n" + "\ndecode info by a address .\n" + "\nArguments:\n" + "1. \"address\" (string) The wallet address\n" + "\nExamples:\n" + "\ndecode the address\n" + + HelpExampleCli("decodeinfobyaddress", "\"address\"") + ); + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid IPChain address"); + CKeyID keyid; + if (!address.GetKeyID(keyid)) + throw JSONRPCError(RPC_WALLET_ERROR, "Can't get CKeyID of the address"); + uint160 pkhash = keyid; + CKey vchPrivKey; + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Please enter the wallet passphrase with walletpassphrase first."); + if (!pwalletMain->GetKey(keyid, vchPrivKey)) + throw JSONRPCError(RPC_WALLET_ERROR, "Can't get CKey of the address"); + CPubKey pubkey = vchPrivKey.GetPubKey(); + assert(vchPrivKey.VerifyPubKey(pubkey)); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("pkhash160", pkhash.GetHex())); + result.push_back(Pair("privatekey", CBitcoinSecret(vchPrivKey).ToString())); + return result; +} +UniValue getcurdeposit(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 0) + throw runtime_error( + "getcurdeposit \"\" \n" + "\nget the current deposit.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "\nResult:\n" + "\"amount\" (string) The deposit amount.\n" + "\nExamples:\n" + + HelpExampleCli("getcurdeposit","") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CAmount deposit = pwalletMain->GetDeposit(); + + return ValueFromAmount(deposit); +} + + +UniValue joincampaign(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() !=2) + throw runtime_error( + "joincampaign \"address\" \"amount\" \n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("joincampaign", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 50") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = AmountFromValue(request.params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + + // Wallet comments + CWalletTx wtx; + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + JoinCampaignCommand(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +static void ExitCampaignCommand(bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + CAmount nValue = 0; + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + int nChangePosRet = -1; + if (!pwalletMain->ExitCampaign(wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + +static void PunishRequestCommand(const CTxDestination &address, const std::string evidence, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + //CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired = 0; + std::string strError; + int nChangePosRet = -1; + //std::cout << "PunishRequestCommand:evidence=" << evidence << std::endl; + if (!pwalletMain->PunishRequest(address, evidence, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + +UniValue punishrequest(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 2) + throw runtime_error( + "punishrequest \"address\" \n" + "\nSend an punish request to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to punish to.\n" + "2. \"evidence\" (string, required) the evidence.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("punishrequest", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"{badblockpubtexthex:Reconstructs the bad block signature plaintext hex string,badblocksign:The signature field of the bad block}\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + const std::string evidence = request.params[1].get_str(); + if (evidence.empty()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Evidence"); + + std::cout << "punishrequest:evidence=" << evidence << std::endl; + + // Wallet comments + CWalletTx wtx; + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + PunishRequestCommand(address.Get(), evidence, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +UniValue exitcampaign(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "exitcampaign \n" + "\exitcampaign from the address.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("exitcampaign" ," ") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Wallet comments + CWalletTx wtx; + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + ExitCampaignCommand(fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +UniValue isdepositabled(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "isdepositabled \"txid\" \"index\" \n" + "\n Isadled or not of the deposit.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"txid\" (string) The transaction id of putin.\n" + "2. \"index\" (string)The id of preVout.\n" + "\nResult:\n" + "\"bool\" (bool) able disable.\n" + "\nExamples:\n" + + HelpExampleCli("isdepositabled", "txid" "index" ) + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Wallet comments + std::string TXid = request.params[0].get_str(); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + std::string iIndex = request.params[1].get_str(); + int Index = std::atoi(iIndex.c_str()); + if (Index < 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid prevout index"); + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + if (tx->vout[Index].txType != TXOUT_CAMPAIGN || tx->vout[Index].devoteLabel.ExtendType != TYPE_CONSENSUS_REGISTER) + throw JSONRPCError(RPC_INVALID_PARAMETER, "This is not a deposit"); + bool isAbled = CConsensusAccountPool::Instance().IsAviableUTXO(tx->GetHash()); + UniValue results(UniValue::VARR); + results.push_back(isAbled); + + return results; +} + +UniValue listreward(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "listreward \n" + "\nlistreward .\n" + "\nResult:\n" + "\"\" (string) the list.\n" + "\nExamples:\n" + + HelpExampleCli("listreward", " ") + ); + UniValue results(UniValue::VARR); + vector rewardtimelist; + vector rewardvaluelist; + + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + { + pwalletMain->GetCurrentRewards(rewardtimelist, rewardvaluelist); + } + int listcount = rewardvaluelist.size(); + for (int i = 0; i < listcount; i++) + { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("Time", rewardtimelist.at(i))); + entry.push_back(Pair("Reward", rewardvaluelist.at(i))); + results.push_back(entry); + } + + return results; +} + +//Tokens Reg Transaction +static void IPCTokenRegSendMoney(std::string strReglabel, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateTokenRegTransaction(strReglabel, vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + + +UniValue IPCTokenRegToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "IPCTokenRegToAddress \"address\" \"icplabel\" ( \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"tokenlabel\" (string) ipclabel...\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = 0; + CWalletTx wtx; + std::string strReglabel = ""; + if (request.params.size() > 1 && !request.params[1].isNull() && !request.params[1].get_str().empty()) + strReglabel = request.params[1].get_str(); + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + IPCTokenRegSendMoney(strReglabel, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//Tokens trading +static void IPCTokenSendMoney(std::string& tokensymbol, uint64_t value, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + + if (!pwalletMain->CreateTokenTransaction(tokensymbol, value, vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCTokenSendToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 3) + throw runtime_error( + "IPCTokenSendToAddress \"tokensymbol\" \"address\" \n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"tokensymbol\" (string) The tokensymbol of what you want to send.\n" + "2. \"address\" (string, required) The bitcoin address to send to.\n" + "3. \"value\" (int)The value of Tokentransaction.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCTokenSendToAddress", "tokensymbol \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1 ") + + HelpExampleCli("IPCTokenSendToAddress", "tokensymbol \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1 ") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::string tokensymbol = request.params[0].get_str(); + + CBitcoinAddress address(request.params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + int nacc = (int)tokenDataMap[tokensymbol].accuracy; + int64_t TokenValue = TCoinsFromValue(request.params[2], nacc); + if (TokenValue <= 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Tokentransaction value"); + // Amount + CAmount nAmount = 0; + uint64_t nTokenCount = TokenValue; + // Wallet comments + CWalletTx wtx; + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + IPCTokenSendMoney(tokensymbol, nTokenCount, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} +//end + + + +UniValue listaddressgroupings(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp) + throw runtime_error( + "listaddressgroupings\n" + "\nLists groups of addresses which have had their common ownership\n" + "made public by common use as inputs or as the resulting change\n" + "in past transactions\n" + "\nResult:\n" + "[\n" + " [\n" + " [\n" + " \"address\", (string) The bitcoin address\n" + " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" + " \"account\" (string, optional) DEPRECATED. The account\n" + " ]\n" + " ,...\n" + " ]\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("listaddressgroupings", "") + + HelpExampleRpc("listaddressgroupings", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + UniValue jsonGroupings(UniValue::VARR); + map balances = pwalletMain->GetAddressBalances(); + BOOST_FOREACH(set grouping, pwalletMain->GetAddressGroupings()) + { + UniValue jsonGrouping(UniValue::VARR); + BOOST_FOREACH(CTxDestination address, grouping) + { + UniValue addressInfo(UniValue::VARR); + addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(ValueFromAmount(balances[address])); + { + if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) + addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); + } + jsonGrouping.push_back(addressInfo); + } + jsonGroupings.push_back(jsonGrouping); + } + return jsonGroupings; +} + +UniValue signmessage(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 2) + throw runtime_error( + "signmessage \"address\" \"message\"\n" + "\nSign a message with the private key of an address" + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to use for the private key.\n" + "2. \"message\" (string, required) The message to create a signature of.\n" + "\nResult:\n" + "\"signature\" (string) The signature of the message encoded in base 64\n" + "\nExamples:\n" + "\nUnlock the wallet for 30 seconds\n" + + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + + "\nCreate the signature\n" + + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + + "\nVerify the signature\n" + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + + "\nAs json rpc\n" + + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + string strAddress = request.params[0].get_str(); + string strMessage = request.params[1].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + vector vchSig; + if (!key.SignCompact(ss.GetHash(), vchSig)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); + + return EncodeBase64(&vchSig[0], vchSig.size()); +} + +//Debug the interface to generate the package person's signature in Coinbase +UniValue dpocsign(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "dpocsign \"hexdata\"\n" + "\nSign a hexdata with the private key of current dpoc account" + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"hexdata\" (string, required) The hexstring of data to create a signature of.\n" + "\nResult:\n" + "\"signature\" (string) The signature of the message with the format in coinbase\n" + "\nExamples:\n" + "\nTo Do\n" + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + string hexdata = request.params[0].get_str(); + if (!IsHex(hexdata)) + { + return false; + } + + std::string pubkeyhex; + if (!CDpocInfo::Instance().GetLocalAccount(pubkeyhex)) + return false; + + uint160 devoterhash; + devoterhash.SetHex(pubkeyhex); + + CKeyID publickeyID(devoterhash); + CPubKey vchPubKeyOut; + if (pwalletMain->GetPubKey(publickeyID, vchPubKeyOut)) { + //LogPrintf("[addRulersToCoinbase] public keyhash=%s", vchPubKeyOut.GetHash().ToString().c_str()); + } + else { + //LogPrintf("[addSignToCoinBase] don't find public key "); + return false; + } + + CKey vchPrivKeyOut; + if (pwalletMain->GetKey(publickeyID, vchPrivKeyOut)) { + } + else { + //LogPrintf("[addSignToCoinBase] don't find private key"); + return false; + } + + uint256 hash; + std::vector data = ParseHex(hexdata); + CHash256 hashoperator; + for (std::vector::iterator it = data.begin(); it != data.end(); it++) + { + hashoperator.Write((unsigned char*)&(*it), sizeof(unsigned char)); + } + hashoperator.Finalize(hash.begin()); + + std::vector vchSig; + vchPrivKeyOut.Sign(hash, vchSig); + + // Generate signature cache + std::vector vchSigSend; + vchSigSend.resize(2 + vchPubKeyOut.size() + vchSig.size()); + + //Cached signature + unsigned char vchSigLen = (unsigned char)vchSig.size(); + vchSigSend[0] = vchSigLen; + for (std::vector::size_type ix = 0; ix < vchSig.size(); ++ix) { + vchSigSend[ix + 1] = vchSig[ix]; + } + + //The cache of public key + vchSigSend[vchSigLen + 1] = (unsigned char)vchPubKeyOut.size(); + for (std::vector::size_type ix = 0; ix < vchPubKeyOut.size(); ++ix) { + vchSigSend[ix + 2 + vchSigLen] = vchPubKeyOut[ix]; + } + + // std::cout << "vchSig.size=" << vchSig.size() << " vchPubKeyOut.size=" << vchPubKeyOut.size() << " vchSigSend.size=" << vchSigSend.size() << std::endl; + std::string strSin2Publickey = EncodeBase58(vchSigSend); + + return strSin2Publickey; +} + +UniValue getreceivedbyaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "getreceivedbyaddress \"address\" ( minconf )\n" + "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address for transactions.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" + "\nExamples:\n" + "\nThe amount from transactions with at least 1 confirmation\n" + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + + "\nThe amount including unconfirmed transactions, zero confirmations\n" + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + + "\nThe amount with at least 6 confirmation, very safe\n" + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + + "\nAs a json rpc call\n" + + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Bitcoin address + CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CScript scriptPubKey = GetScriptForDestination(address.Get()); + if (!IsMine(*pwalletMain, scriptPubKey)) + return ValueFromAmount(0); + + // Minimum confirmations + int nMinDepth = 1; + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); + + // Tally + CAmount nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + + return ValueFromAmount(nAmount); +} + + +UniValue getreceivedbyaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "getreceivedbyaccount \"account\" ( minconf )\n" + "\nDEPRECATED. Returns the total amount received by addresses with in transactions with at least [minconf] confirmations.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The selected account, may be the default account using \"\".\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" + "\nExamples:\n" + "\nAmount received by the default account with at least 1 confirmation\n" + + HelpExampleCli("getreceivedbyaccount", "\"\"") + + "\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n" + + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") + + "\nThe amount with at least 6 confirmation, very safe\n" + + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") + + "\nAs a json rpc call\n" + + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Minimum confirmations + int nMinDepth = 1; + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); + + // Get the set of pub keys assigned to account + string strAccount = AccountFromValue(request.params[0]); + set setAddress = pwalletMain->GetAccountAddresses(strAccount); + + // Tally + CAmount nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) + { + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return ValueFromAmount(nAmount); +} + + +UniValue getbalance(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 3) + throw runtime_error( + "getbalance ( \"account\" minconf include_watchonly )\n" + "\nIf account is not specified, returns the server's total available balance.\n" + "If account is specified (DEPRECATED), returns the balance in the account.\n" + "Note that the account \"\" is not the same as leaving the parameter out.\n" + "The server total may be different to the balance in the default \"\" account.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) DEPRECATED. The account string may be given as a\n" + " specific account name to find the balance associated with wallet keys in\n" + " a named account, or as the empty string (\"\") to find the balance\n" + " associated with wallet keys not in any named account, or as \"*\" to find\n" + " the balance associated with all wallet keys regardless of account.\n" + " When this option is specified, it calculates the balance in a different\n" + " way than when it is not specified, and which can count spends twice when\n" + " there are conflicting pending transactions (such as those created by\n" + " the bumpfee command), temporarily resulting in low or even negative\n" + " balances. In general, account balance calculation is not considered\n" + " reliable and has resulted in confusing outcomes, so it is recommended to\n" + " avoid passing this argument.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" + "\nExamples:\n" + "\nThe total amount in the wallet\n" + + HelpExampleCli("getbalance", "") + + "\nThe total amount in the wallet at least 5 blocks confirmed\n" + + HelpExampleCli("getbalance", "\"*\" 6") + + "\nAs a json rpc call\n" + + HelpExampleRpc("getbalance", "\"*\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.params.size() == 0) + return ValueFromAmount(pwalletMain->GetBalance()); + + int nMinDepth = 1; + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if(request.params.size() > 2) + if(request.params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + if (request.params[0].get_str() == "*") { + // Calculate total balance in a very different way from GetBalance(). + // The biggest difference is that GetBalance() sums up all unspent + // TxOuts paying to the wallet, while this sums up both spent and + // unspent TxOuts paying to the wallet, and then subtracts the values of + // TxIns spending from the wallet. This also has fewer restrictions on + // which unconfirmed transactions are considered trusted. + CAmount nBalance = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + continue; + + CAmount allFee; + string strSentAccount; + list listReceived; + list listSent; + wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const COutputEntry& r, listReceived) + nBalance += r.amount; + } + BOOST_FOREACH(const COutputEntry& s, listSent) + nBalance -= s.amount; + nBalance -= allFee; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(request.params[0]); + + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, filter); + + return ValueFromAmount(nBalance); +} + +UniValue getunconfirmedbalance(const JSONRPCRequest &request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 0) + throw runtime_error( + "getunconfirmedbalance\n" + "Returns the server's total unconfirmed balance\n"); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); +} + + +UniValue movecmd(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 5) + throw runtime_error( + "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" + "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) The name of the account to move funds from. May be the default account using \"\".\n" + "2. \"toaccount\" (string, required) The name of the account to move funds to. May be the default account using \"\".\n" + "3. amount (numeric) Quantity of " + CURRENCY_UNIT + " to move between accounts.\n" + "4. (dummy) (numeric, optional) Ignored. Remains for backward compatibility.\n" + "5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" + "\nResult:\n" + "true|false (boolean) true if successful.\n" + "\nExamples:\n" + "\nMove 0.01 " + CURRENCY_UNIT + " from the default account to the account named tabby\n" + + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + + "\nMove 0.01 " + CURRENCY_UNIT + " timotei to akiko with a comment and funds have 6 confirmations\n" + + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strFrom = AccountFromValue(request.params[0]); + string strTo = AccountFromValue(request.params[1]); + CAmount nAmount = AmountFromValue(request.params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + if (request.params.size() > 3) + // unused parameter, used to be nMinDepth, keep type-checking it though + (void)request.params[3].get_int(); + string strComment; + if (request.params.size() > 4) + strComment = request.params[4].get_str(); + + if (!pwalletMain->AccountMove(strFrom, strTo, nAmount, strComment)) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + return true; +} + + +UniValue sendfrom(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 6) + throw runtime_error( + "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf \"comment\" \"comment_to\" )\n" + "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a bitcoin address." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n" + " Specifying an account does not influence coin selection, but it does associate the newly created\n" + " transaction with the account, so the account's balance computation and transaction history can reflect\n" + " the spend.\n" + "2. \"toaddress\" (string, required) The ipchain address to send funds to.\n" + "3. amount (numeric or string, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n" + "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" + "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "6. \"comment_to\" (string, optional) An optional comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the transaction, \n" + " it is just kept in your wallet.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + "\nSend 0.01 " + CURRENCY_UNIT + " from the default account to the address, must have at least 1 confirmation\n" + + HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + + "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" + + HelpExampleCli("sendfrom", "\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount = AccountFromValue(request.params[0]); + CBitcoinAddress address(request.params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CAmount nAmount = AmountFromValue(request.params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + int nMinDepth = 1; + if (request.params.size() > 3) + nMinDepth = request.params[3].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty()) + wtx.mapValue["comment"] = request.params[4].get_str(); + if (request.params.size() > 5 && !request.params[5].isNull() && !request.params[5].get_str().empty()) + wtx.mapValue["to"] = request.params[5].get_str(); + + EnsureWalletIsUnlocked(); + + // Check funds + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + if (nAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + SendMoney(address.Get(), nAmount, false, wtx); + + return wtx.GetHash().GetHex(); +} + + +UniValue sendmany(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" + "\nSend multiple times. Amounts are double-precision floating point numbers." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) DEPRECATED. The account to send the funds from. Should be \"\" for the default account\n" + "2. \"amounts\" (string, required) A json object with addresses and amounts\n" + " {\n" + " \"address\":amount (numeric or string) The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" + " ,...\n" + " }\n" + "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" + "4. \"comment\" (string, optional) A comment\n" + "5. subtractfeefrom (array, optional) A json array with addresses.\n" + " The fee will be equally deducted from the amount of each selected address.\n" + " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" + " If no addresses are specified here, the sender pays the fee.\n" + " [\n" + " \"address\" (string) Subtract fee from this address\n" + " ,...\n" + " ]\n" + "\nResult:\n" + "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + + "\nSend two amounts to two different addresses, subtract fee from amount:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + string strAccount = AccountFromValue(request.params[0]); + UniValue sendTo = request.params[1].get_obj(); + int nMinDepth = 1; + if (request.params.size() > 2) + nMinDepth = request.params[2].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["comment"] = request.params[3].get_str(); + + UniValue subtractFeeFromAmount(UniValue::VARR); + if (request.params.size() > 4) + subtractFeeFromAmount = request.params[4].get_array(); + + set setAddress; + vector vecSend; + + CAmount totalAmount = 0; + vector keys = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, keys) + { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(sendTo[name_]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + totalAmount += nAmount; + + bool fSubtractFeeFromAmount = false; + for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) { + const UniValue& addr = subtractFeeFromAmount[idx]; + if (addr.get_str() == name_) + fSubtractFeeFromAmount = true; + } + + CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; + vecSend.push_back(recipient); + } + + EnsureWalletIsUnlocked(); + + // Check funds + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + CAmount nFeeRequired = 0; + int nChangePosRet = -1; + string strFailReason; + bool fCreated = pwalletMain->CreateNormalTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + CValidationState state; + if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), state)) { + strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); + } + + return wtx.GetHash().GetHex(); +} + +UniValue sendtokenmany(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 3) + throw runtime_error( + "sendtokenmany \"fromaccount\" \"tokensymbol\" {\"address\":amount,...}\n" + "\nSend multiple times. Amounts are double-precision floating point numbers." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) DEPRECATED. The account to send the funds from. Should be \"\" for the default account\n" + "2. \"tokensymbol\" (string, required) Tokensymbol which you want to send\n" + "3. \"amounts\" (string, required) A json object with addresses and amounts\n" + " {\n" + " \"address\":amount (numeric or string) The ipchain address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" + " ,...\n" + " }\n" + "\nResult:\n" + "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli("sendtokenmany", "\"\" \"tokensymbol\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + string strAccount = AccountFromValue(request.params[0]); + string tokensymbol = request.params[1].get_str(); + if (tokenDataMap.count(tokensymbol) == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Can't found 'accuracy' of the Token"); + + int tokenaccuracy = tokenDataMap[tokensymbol].accuracy; + UniValue sendTo = request.params[2].get_obj(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + + set setAddress; + vector vecSend; + vecSend.clear(); + uint64_t totalAmount = 0; + vector keys = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, keys) + { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid IPchain address: ") + name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + int64_t nAmount = TCoinsFromValue(sendTo[name_], tokenaccuracy); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid token amount for send"); + totalAmount += nAmount; + + bool fSubtractFeeFromAmount = false; + CAmount nipcAmount = 0; + uint64_t ntokenamount = nAmount; + CRecipientToken recipient = { scriptPubKey, nipcAmount, ntokenamount, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + } + + EnsureWalletIsUnlocked(); + + // Check funds + uint64_t nBalance = 0; + pwalletMain->GetSymbolbalance(tokensymbol, nBalance); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + CAmount nFeeRequired = 0; + int nChangePosRet = -1; + string strFailReason; + bool fCreated = pwalletMain->CreateTokenTransactionM(tokensymbol,vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + CValidationState state; + if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), state)) { + strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); + } + + return wtx.GetHash().GetHex(); +} +// Defined in rpc/misc.cpp +extern CScript _createmultisig_redeemScript(const UniValue& params); + +UniValue addmultisigaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) + { + string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" + "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" + "Each key is a Bitcoin address or hex-encoded public key.\n" + "If 'account' is specified (DEPRECATED), assign address to that account.\n" + + "\nArguments:\n" + "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" + "2. \"keys\" (string, required) A json array of bitcoin addresses or hex-encoded public keys\n" + " [\n" + " \"address\" (string) bitcoin address or hex-encoded public key\n" + " ...,\n" + " ]\n" + "3. \"account\" (string, optional) DEPRECATED. An account to assign the addresses to.\n" + + "\nResult:\n" + "\"address\" (string) A bitcoin address associated with the keys.\n" + + "\nExamples:\n" + "\nAdd a multisig address from 2 addresses\n" + + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + "\nAs json rpc call\n" + + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + ; + throw runtime_error(msg); + } + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount; + if (request.params.size() > 2) + strAccount = AccountFromValue(request.params[2]); + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig_redeemScript(request.params); + CScriptID innerID(inner); + pwalletMain->AddCScript(inner); + + pwalletMain->SetAddressBook(innerID, strAccount, "send"); + return CBitcoinAddress(innerID).ToString(); +} + +class Witnessifier : public boost::static_visitor +{ +public: + CScriptID result; + + bool operator()(const CNoDestination &dest) const { return false; } + + bool operator()(const CKeyID &keyID) { + CPubKey pubkey; + if (pwalletMain) { + CScript basescript = GetScriptForDestination(keyID); + isminetype typ; + typ = IsMine(*pwalletMain, basescript, SIGVERSION_WITNESS_V0); + if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE) + return false; + CScript witscript = GetScriptForWitness(basescript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } + + bool operator()(const CScriptID &scriptID) { + CScript subscript; + if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { + int witnessversion; + std::vector witprog; + if (subscript.IsWitnessProgram(witnessversion, witprog)) { + result = scriptID; + return true; + } + isminetype typ; + typ = IsMine(*pwalletMain, subscript, SIGVERSION_WITNESS_V0); + if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE) + return false; + CScript witscript = GetScriptForWitness(subscript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } +}; + +UniValue addwitnessaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + { + string msg = "addwitnessaddress \"address\"\n" + "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" + "It returns the witness script.\n" + + "\nArguments:\n" + "1. \"address\" (string, required) An address known to the wallet\n" + + "\nResult:\n" + "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "}\n" + ; + throw runtime_error(msg); + } + + { + LOCK(cs_main); + if (!IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()) && !GetBoolArg("-walletprematurewitness", false)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Segregated witness not enabled on network"); + } + } + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + Witnessifier w; + CTxDestination dest = address.Get(); + bool ret = boost::apply_visitor(w, dest); + if (!ret) { + throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); + } + + pwalletMain->SetAddressBook(w.result, "", "receive"); + + return CBitcoinAddress(w.result).ToString(); +} + +struct tallyitem +{ + CAmount nAmount; + int nConf; + vector txids; + bool fIsWatchonly; + tallyitem() + { + nAmount = 0; + nConf = std::numeric_limits::max(); + fIsWatchonly = false; + } +}; + +UniValue ListReceived(const UniValue& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + isminefilter filter = ISMINE_SPENDABLE; + if(params.size() > 2) + if(params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + // Tally + map mapTally; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) + { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + continue; + + isminefilter mine = IsMine(*pwalletMain, address); + if(!(mine & filter)) + continue; + + tallyitem& item = mapTally[address]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + item.txids.push_back(wtx.GetHash()); + if (mine & ISMINE_WATCH_ONLY) + item.fIsWatchonly = true; + } + } + + // Reply + UniValue ret(UniValue::VARR); + map mapAccountTally; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strAccount = item.second.name; + map::iterator it = mapTally.find(address); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + CAmount nAmount = 0; + int nConf = std::numeric_limits::max(); + bool fIsWatchonly = false; + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + fIsWatchonly = (*it).second.fIsWatchonly; + } + + if (fByAccounts) + { + tallyitem& _item = mapAccountTally[strAccount]; + _item.nAmount += nAmount; + _item.nConf = min(_item.nConf, nConf); + _item.fIsWatchonly = fIsWatchonly; + } + else + { + UniValue obj(UniValue::VOBJ); + if(fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); + obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + if (!fByAccounts) + obj.push_back(Pair("label", strAccount)); + UniValue transactions(UniValue::VARR); + if (it != mapTally.end()) + { + BOOST_FOREACH(const uint256& _item, (*it).second.txids) + { + transactions.push_back(_item.GetHex()); + } + } + obj.push_back(Pair("txids", transactions)); + ret.push_back(obj); + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + CAmount nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + UniValue obj(UniValue::VOBJ); + if((*it).second.fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + ret.push_back(obj); + } + } + + return ret; +} + +UniValue listreceivedbyaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 3) + throw runtime_error( + "listreceivedbyaddress ( minconf include_empty include_watchonly)\n" + "\nList balances by receiving address.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" + "2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" + " \"address\" : \"receivingaddress\", (string) The receiving address\n" + " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" + " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" + " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"txids\": [\n" + " n, (numeric) The ids of transactions received with the address \n" + " ...\n" + " ]\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listreceivedbyaddress", "") + + HelpExampleCli("listreceivedbyaddress", "6 true") + + HelpExampleRpc("listreceivedbyaddress", "6, true, true") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + return ListReceived(request.params, false); +} + +UniValue listreceivedbyaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 3) + throw runtime_error( + "listreceivedbyaccount ( minconf include_empty include_watchonly)\n" + "\nDEPRECATED. List balances by account.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" + "2. include_empty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" + " \"account\" : \"accountname\", (string) The account name of the receiving account\n" + " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" + " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" + " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listreceivedbyaccount", "") + + HelpExampleCli("listreceivedbyaccount", "6 true") + + HelpExampleRpc("listreceivedbyaccount", "6, true, true") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + return ListReceived(request.params, true); +} + +static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) +{ + CBitcoinAddress addr; + if (addr.Set(dest)) + entry.push_back(Pair("address", addr.ToString())); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) +{ + CAmount nFee; + string strSentAccount; + list listReceived; + list listSent; + + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); + + bool fAllAccounts = (strAccount == string("*")); + bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const COutputEntry& s, listSent) + { + UniValue entry(UniValue::VOBJ); + if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + entry.push_back(Pair("account", strSentAccount)); + MaybePushAddress(entry, s.destination); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); + if (pwalletMain->mapAddressBook.count(s.destination)) + entry.push_back(Pair("label", pwalletMain->mapAddressBook[s.destination].name)); + entry.push_back(Pair("vout", s.vout)); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + entry.push_back(Pair("abandoned", wtx.isAbandoned())); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const COutputEntry& r, listReceived) + { + string account; + if (pwalletMain->mapAddressBook.count(r.destination)) + account = pwalletMain->mapAddressBook[r.destination].name; + if (fAllAccounts || (account == strAccount)) + { + UniValue entry(UniValue::VOBJ); + if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + entry.push_back(Pair("account", account)); + MaybePushAddress(entry, r.destination); + if (wtx.IsCoinBase()) + { + if (wtx.GetDepthInMainChain() < 1) + entry.push_back(Pair("category", "orphan")); + else if (wtx.GetBlocksToMaturity() > 0) + entry.push_back(Pair("category", "immature")); + else + entry.push_back(Pair("category", "generate")); + } + else + { + entry.push_back(Pair("category", "receive")); + } + entry.push_back(Pair("amount", ValueFromAmount(r.amount))); + if (pwalletMain->mapAddressBook.count(r.destination)) + entry.push_back(Pair("label", account)); + entry.push_back(Pair("vout", r.vout)); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) + { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +UniValue listtransactions(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listtransactions ( \"account\" count skip include_watchonly)\n" + "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" + "2. count (numeric, optional, default=10) The number of transactions to return\n" + "3. skip (numeric, optional, default=0) The number of transactions to skip\n" + "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "[\n" + " {\n" + " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" + " It will be \"\" for the default account.\n" + " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for \n" + " move transactions (category = move).\n" + " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" + " transaction between accounts, and not associated with an address,\n" + " transaction id or block. 'send' and 'receive' transactions are \n" + " associated with an address, transaction id and block details\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" + " 'move' category for moves outbound. It is positive for the 'receive' category,\n" + " and for the 'move' category for inbound funds.\n" + " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" + " \"vout\": n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" + " 'receive' category of transactions. Negative confirmations indicate the\n" + " transaction conflicts with the block chain\n" + " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" + " category of transactions.\n" + " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive'\n" + " category of transactions.\n" + " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" + " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" + " for 'send' and 'receive' category of transactions.\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " \"otheraccount\": \"accountname\", (string) DEPRECATED. For the 'move' category of transactions, the account the funds came \n" + " from (for receiving funds, positive amounts), or went to (for sending funds,\n" + " negative amounts).\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" + " }\n" + "]\n" + + "\nExamples:\n" + "\nList the most recent 10 transactions in the systems\n" + + HelpExampleCli("listtransactions", "") + + "\nList transactions 100 to 120\n" + + HelpExampleCli("listtransactions", "\"*\" 20 100") + + "\nAs a json rpc call\n" + + HelpExampleRpc("listtransactions", "\"*\", 20, 100") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount = "*"; + if (request.params.size() > 0) + strAccount = request.params[0].get_str(); + int nCount = 10; + if (request.params.size() > 1) + nCount = request.params[1].get_int(); + int nFrom = 0; + if (request.params.size() > 2) + nFrom = request.params[2].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if(request.params.size() > 3) + if(request.params[3].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + if (nCount < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); + if (nFrom < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); + + UniValue ret(UniValue::VARR); + + const CWallet::TxItems & txOrdered = pwalletMain->wtxOrdered; + + // iterate backwards until we have nCount items to return: + for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret, filter); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if ((int)ret.size() >= (nCount+nFrom)) break; + } + // ret is newest to oldest + + if (nFrom > (int)ret.size()) + nFrom = ret.size(); + if ((nFrom + nCount) > (int)ret.size()) + nCount = ret.size() - nFrom; + + vector arrTmp = ret.getValues(); + + vector::iterator first = arrTmp.begin(); + std::advance(first, nFrom); + vector::iterator last = arrTmp.begin(); + std::advance(last, nFrom+nCount); + + if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); + if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); + + std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest + + ret.clear(); + ret.setArray(); + ret.push_backV(arrTmp); + + return ret; +} + +UniValue listaccounts(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 2) + throw runtime_error( + "listaccounts ( minconf include_watchonly)\n" + "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" + "2. include_watchonly (bool, optional, default=false) Include balances in watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "{ (json object where keys are account names, and values are numeric balances\n" + " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" + " ...\n" + "}\n" + "\nExamples:\n" + "\nList account balances where there at least 1 confirmation\n" + + HelpExampleCli("listaccounts", "") + + "\nList account balances including zero confirmation transactions\n" + + HelpExampleCli("listaccounts", "0") + + "\nList account balances for 6 or more confirmations\n" + + HelpExampleCli("listaccounts", "6") + + "\nAs json rpc call\n" + + HelpExampleRpc("listaccounts", "6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + int nMinDepth = 1; + if (request.params.size() > 0) + nMinDepth = request.params[0].get_int(); + isminefilter includeWatchonly = ISMINE_SPENDABLE; + if(request.params.size() > 1) + if(request.params[1].get_bool()) + includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; + + map mapAccountBalances; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { + if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me + mapAccountBalances[entry.second.name] = 0; + } + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + CAmount nFee; + string strSentAccount; + list listReceived; + list listSent; + int nDepth = wtx.GetDepthInMainChain(); + if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) + continue; + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const COutputEntry& s, listSent) + mapAccountBalances[strSentAccount] -= s.amount; + if (nDepth >= nMinDepth) + { + BOOST_FOREACH(const COutputEntry& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.destination)) + mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; + else + mapAccountBalances[""] += r.amount; + } + } + + const list & acentries = pwalletMain->laccentries; + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + UniValue ret(UniValue::VOBJ); + BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +UniValue listsinceblock(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp) + throw runtime_error( + "listsinceblock ( \"blockhash\" target_confirmations include_watchonly)\n" + "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" + "\nArguments:\n" + "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" + "2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" + "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')" + "\nResult:\n" + "{\n" + " \"transactions\": [\n" + " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" + " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" + " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" + " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" + " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" + " When it's < 0, it means the transaction conflicted that many blocks ago.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" + " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive' category of transactions.\n" + " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" + " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" + " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" + " ],\n" + " \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("listsinceblock", "") + + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") + + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + const CBlockIndex *pindex = NULL; + int target_confirms = 1; + isminefilter filter = ISMINE_SPENDABLE; + + if (request.params.size() > 0) + { + uint256 blockId; + + blockId.SetHex(request.params[0].get_str()); + BlockMap::iterator it = mapBlockIndex.find(blockId); + if (it != mapBlockIndex.end()) + { + pindex = it->second; + if (chainActive[pindex->nHeight] != pindex) + { + // the block being asked for is a part of a deactivated chain; + // we don't want to depend on its perceived height in the block + // chain, we want to instead use the last common ancestor + pindex = chainActive.FindFork(pindex); + } + } + } + + if (request.params.size() > 1) + { + target_confirms = request.params[1].get_int(); + + if (target_confirms < 1) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + } + + if (request.params.size() > 2 && request.params[2].get_bool()) + { + filter = filter | ISMINE_WATCH_ONLY; + } + + int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; + + UniValue transactions(UniValue::VARR); + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) + { + CWalletTx tx = (*it).second; + + if (depth == -1 || tx.GetDepthInMainChain() < depth) + ListTransactions(tx, "*", 0, true, transactions, filter); + } + + CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; + uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("transactions", transactions)); + ret.push_back(Pair("lastblock", lastblock.GetHex())); + + return ret; +} + +UniValue gettransaction(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "gettransaction \"txid\" ( include_watchonly )\n" + "\nGet detailed information about in-wallet transaction \n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]\n" + "\nResult:\n" + "{\n" + " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"blockhash\" : \"hash\", (string) The block hash\n" + " \"blockindex\" : xx, (numeric) The index of the transaction in the block that includes it\n" + " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"txid\" : \"transactionid\", (string) The transaction id.\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"details\" : [\n" + " {\n" + " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" + " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" + " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" + " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" + " }\n" + " ,...\n" + " ],\n" + " \"hex\" : \"data\" (string) Raw data for transaction\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") + + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + uint256 hash; + hash.SetHex(request.params[0].get_str()); + + isminefilter filter = ISMINE_SPENDABLE; + if (request.params.size() > 1) + if (request.params[1].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + UniValue entry(UniValue::VOBJ); + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + CAmount nCredit = wtx.GetCredit(filter); + CAmount nDebit = wtx.GetDebit(filter); + CAmount nNet = nCredit - nDebit; + CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe(filter)) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(wtx, entry); + + UniValue details(UniValue::VARR); + ListTransactions(wtx, "*", 0, false, details, filter); + entry.push_back(Pair("details", details)); + + string strHex = EncodeHexTx(static_cast(wtx), RPCSerializationFlags()); + entry.push_back(Pair("hex", strHex)); + + return entry; +} + + +UniValue abandontransaction(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "abandontransaction \"txid\"\n" + "\nMark in-wallet transaction as abandoned\n" + "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n" + "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n" + "It only works on transactions which are not included in a block and are not currently in the mempool.\n" + "It has no effect on transactions which are already conflicted or abandoned.\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + uint256 hash; + hash.SetHex(request.params[0].get_str()); + + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + if (!pwalletMain->AbandonTransaction(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment"); + + return NullUniValue; +} + + +UniValue backupwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "backupwallet \"destination\"\n" + "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n" + "\nArguments:\n" + "1. \"destination\" (string) The destination directory or file\n" + "\nExamples:\n" + + HelpExampleCli("backupwallet", "\"backup.dat\"") + + HelpExampleRpc("backupwallet", "\"backup.dat\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strDest = request.params[0].get_str(); + if (!pwalletMain->BackupWallet(strDest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); + + return NullUniValue; +} + + +UniValue keypoolrefill(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1) + throw runtime_error( + "keypoolrefill ( newsize )\n" + "\nFills the keypool." + + HelpRequiringPassphrase() + "\n" + "\nArguments\n" + "1. newsize (numeric, optional, default=100) The new keypool size\n" + "\nExamples:\n" + + HelpExampleCli("keypoolrefill", "") + + HelpExampleRpc("keypoolrefill", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool + unsigned int kpSize = 0; + if (request.params.size() > 0) { + if (request.params[0].get_int() < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); + kpSize = (unsigned int)request.params[0].get_int(); + } + + EnsureWalletIsUnlocked(); + pwalletMain->TopUpKeyPool(kpSize); + + if (pwalletMain->GetKeyPoolSize() < kpSize) + throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); + + return NullUniValue; +} + + +static void LockWallet(CWallet* pWallet) +{ + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = 0; + pWallet->Lock(); +} + +UniValue walletpassphrase(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2)) + throw runtime_error( + "walletpassphrase \"passphrase\" timeout\n" + "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" + "This is needed prior to performing transactions related to private keys such as sending bitcoins\n" + "\nArguments:\n" + "1. \"passphrase\" (string, required) The wallet passphrase\n" + "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" + "\nNote:\n" + "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" + "time that overrides the old one.\n" + "\nExamples:\n" + "\nunlock the wallet for 60 seconds\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + + "\nLock the wallet again (before 60 seconds)\n" + + HelpExampleCli("walletlock", "") + + "\nAs json rpc call\n" + + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + + // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed + SecureString strWalletPass; + strWalletPass.reserve(100); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + strWalletPass = request.params[0].get_str().c_str(); + + if (strWalletPass.length() > 0) + { + if (!pwalletMain->Unlock(strWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + } + else + throw runtime_error( + "walletpassphrase \n" + "Stores the wallet decryption key in memory for seconds."); + + pwalletMain->TopUpKeyPool(); + + int64_t nSleepTime = request.params[1].get_int64(); + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = GetTime() + nSleepTime; + RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); + + return NullUniValue; +} + + +UniValue walletpassphrasechange(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2)) + throw runtime_error( + "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" + "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" + "\nArguments:\n" + "1. \"oldpassphrase\" (string) The current passphrase\n" + "2. \"newpassphrase\" (string) The new passphrase\n" + "\nExamples:\n" + + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + + // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + SecureString strOldWalletPass; + strOldWalletPass.reserve(100); + strOldWalletPass = request.params[0].get_str().c_str(); + + SecureString strNewWalletPass; + strNewWalletPass.reserve(100); + strNewWalletPass = request.params[1].get_str().c_str(); + + if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + + return NullUniValue; +} + + +UniValue walletlock(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 0)) + throw runtime_error( + "walletlock\n" + "\nRemoves the wallet encryption key from memory, locking the wallet.\n" + "After calling this method, you will need to call walletpassphrase again\n" + "before being able to call any methods which require the wallet to be unlocked.\n" + "\nExamples:\n" + "\nSet the passphrase for 2 minutes to perform a transaction\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + + "\nPerform a send (requires passphrase set)\n" + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") + + "\nClear the passphrase since we are done before 2 minutes is up\n" + + HelpExampleCli("walletlock", "") + + "\nAs json rpc call\n" + + HelpExampleRpc("walletlock", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); + + { + LOCK(cs_nWalletUnlockTime); + pwalletMain->Lock(); + nWalletUnlockTime = 0; + } + + return NullUniValue; +} + + +UniValue encryptwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (!pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 1)) + throw runtime_error( + "encryptwallet \"passphrase\"\n" + "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" + "After this, any calls that interact with private keys such as sending or signing \n" + "will require the passphrase to be set prior the making these calls.\n" + "Use the walletpassphrase call for this, and then walletlock call.\n" + "If the wallet is already encrypted, use the walletpassphrasechange call.\n" + "Note that this will shutdown the server.\n" + "\nArguments:\n" + "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n" + "\nExamples:\n" + "\nEncrypt you wallet\n" + + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + + "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + + "\nNow we can so something like sign\n" + + HelpExampleCli("signmessage", "\"address\" \"test message\"") + + "\nNow lock the wallet again by removing the passphrase\n" + + HelpExampleCli("walletlock", "") + + "\nAs a json rpc call\n" + + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); + + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + SecureString strWalletPass; + strWalletPass.reserve(100); + strWalletPass = request.params[0].get_str().c_str(); + + if (strWalletPass.length() < 1) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + + if (!pwalletMain->EncryptWallet(strWalletPass)) + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); + + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + StartShutdown(); + return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; +} + +UniValue lockunspent(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n" + "\nUpdates list of temporarily unspendable outputs.\n" + "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" + "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n" + "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n" + "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" + "is always cleared (by virtue of process exit) when a node stops or fails.\n" + "Also see the listunspent call\n" + "\nArguments:\n" + "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n" + "2. \"transactions\" (string, optional) A json array of objects. Each object the txid (string) vout (numeric)\n" + " [ (json array of json objects)\n" + " {\n" + " \"txid\":\"id\", (string) The transaction id\n" + " \"vout\": n (numeric) The output number\n" + " }\n" + " ,...\n" + " ]\n" + + "\nResult:\n" + "true|false (boolean) Whether the command was successful or not\n" + + "\nExamples:\n" + "\nList the unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nLock an unspent transaction\n" + + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nList the locked transactions\n" + + HelpExampleCli("listlockunspent", "") + + "\nUnlock the transaction again\n" + + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.params.size() == 1) + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL)); + else + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); + + bool fUnlock = request.params[0].get_bool(); + + if (request.params.size() == 1) { + if (fUnlock) + pwalletMain->UnlockAllCoins(); + return true; + } + + UniValue outputs = request.params[1].get_array(); + for (unsigned int idx = 0; idx < outputs.size(); idx++) { + const UniValue& output = outputs[idx]; + if (!output.isObject()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); + const UniValue& o = output.get_obj(); + + RPCTypeCheckObj(o, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + }); + + string txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + + int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + COutPoint outpt(uint256S(txid), nOutput); + + if (fUnlock) + pwalletMain->UnlockCoin(outpt); + else + pwalletMain->LockCoin(outpt); + } + + return true; +} + +UniValue listlockunspent(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 0) + throw runtime_error( + "listlockunspent\n" + "\nReturns list of temporarily unspendable outputs.\n" + "See the lockunspent call to lock and unlock transactions for spending.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"txid\" : \"transactionid\", (string) The transaction id locked\n" + " \"vout\" : n (numeric) The vout value\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + "\nList the unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nLock an unspent transaction\n" + + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nList the locked transactions\n" + + HelpExampleCli("listlockunspent", "") + + "\nUnlock the transaction again\n" + + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("listlockunspent", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + vector vOutpts; + pwalletMain->ListLockedCoins(vOutpts); + + UniValue ret(UniValue::VARR); + + BOOST_FOREACH(COutPoint &outpt, vOutpts) { + UniValue o(UniValue::VOBJ); + + o.push_back(Pair("txid", outpt.hash.GetHex())); + o.push_back(Pair("vout", (int)outpt.n)); + ret.push_back(o); + } + + return ret; +} + +UniValue settxfee(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + throw runtime_error( + "settxfee amount\n" + "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n" + "\nArguments:\n" + "1. amount (numeric or string, required) The transaction fee in " + CURRENCY_UNIT + "/kB\n" + "\nResult\n" + "true|false (boolean) Returns true if successful\n" + "\nExamples:\n" + + HelpExampleCli("settxfee", "0.00001") + + HelpExampleRpc("settxfee", "0.00001") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Amount + CAmount nAmount = AmountFromValue(request.params[0]); + + payTxFee = CFeeRate(nAmount, 1000); + return true; +} + +UniValue getwalletinfo(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "getwalletinfo\n" + "Returns an object containing various wallet state info.\n" + "\nResult:\n" + "{\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" + " \"hdmasterkeyid\": \"\" (string) the Hash160 of the HD master pubkey\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getwalletinfo", "") + + HelpExampleRpc("getwalletinfo", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); + obj.push_back(Pair("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance()))); + obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size())); + obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + if (pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); + CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID; + if (!masterKeyID.IsNull()) + obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex())); + return obj; +} + +UniValue resendwallettransactions(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "resendwallettransactions\n" + "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" + "Intended only for testing; the wallet code periodically re-broadcasts\n" + "automatically.\n" + "Returns array of transaction ids that were re-broadcast.\n" + ); + + if (!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::vector txids = pwalletMain->ResendWalletTransactionsBefore(GetTime(), g_connman.get()); + UniValue result(UniValue::VARR); + BOOST_FOREACH(const uint256& txid, txids) + { + result.push_back(txid.ToString()); + } + return result; +} + +UniValue listunspent(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspent", "") + + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableNormalCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + CTxDestination address; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} + +//add by xxy +UniValue listunspentNormal(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspentNormal ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspentNormal", "") + + HelpExampleCli("listunspentNormal", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspentNormal", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableNormalCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + CTxDestination address; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} + +UniValue listunspentIPC(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspentIPC ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspentIPC", "") + + HelpExampleCli("listunspentIPC", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspentIPC", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableIPCCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + if (out.nDepthtx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + entry.push_back(make_pair("UTXOType", out.GetType())); + entry.push_back(make_pair("GetIPCExtendType", out.GetIPCExtendType())); + entry.push_back(make_pair("GetIPCStartTime", out.GetIPCStartTime())); + entry.push_back(make_pair("GetIPCStopTime", out.GetIPCStopTime())); + entry.push_back(make_pair("GetIPCHash", out.GetIPCHash())); + entry.push_back(make_pair("GetIPCTitle", out.GetIPCTitle())); + entry.push_back(make_pair("GetIPCLabel", out.GetIPCLabel())); + entry.push_back(make_pair("CanBeSentToOhter", out.CanBeSentToOhter())); + entry.push_back(make_pair("CanBeAuthorizedToOther", out.CanBeAuthorizedToOther())); + entry.push_back(make_pair("CanBeUniqueAuthorizedToOther", out.CanBeUniqueAuthorizedToOther())); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} +UniValue listtokencoins(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1 || request.params.size() < 1) + throw runtime_error( + "listtokencoins ( [\"tokensymbol\",...] )\n" + "\nReturns the value of tokensymbol balance \n" + "\nArguments:\n" + "1. tokensymbol (string) The tokensymbol\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"symbol\" : \"symbol\", (string) the TokenSymbol id \n" + " \"balance\" : n, (numeric) the value of tokensymbol\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listtokencoins", "tokensymbol") + ); + std::string tokensymbol = request.params[0].get_str(); + uint64_t balance = 0; + bool getbalanceYet = pwalletMain->GetSymbolbalance(tokensymbol, balance); + if (!getbalanceYet) + throw JSONRPCError(RPC_DATABASE_ERROR, string("Can't find the balance of the tokensymbol!")); + UniValue results(UniValue::VOBJ); + results.push_back(Pair("Tokensymbol", tokensymbol)); + results.push_back(Pair("balance", ValueFromTCoins(balance, (int)tokenDataMap[tokensymbol].accuracy))); + + return results; +} + +UniValue listunspentToken(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspentToken ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspentToken", "") + + HelpExampleCli("listunspentToken", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspentToken", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableTokenCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + if (out.nDepth < nTxConfirmTarget) + continue; + CTxDestination address; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + entry.push_back(Pair("TokenSymbol", out.GetTokenSymbol())); + entry.push_back(Pair("tokenvalue", ValueFromTCoins(out.GetTokenvalue(), (int)out.GetTokenAccuracy()))); + entry.push_back(Pair("TokenAccuracy", out.GetTokenAccuracy())); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} +//end +UniValue fundrawtransaction(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "fundrawtransaction \"hexstring\" ( options )\n" + "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" + "This will not modify existing inputs, and will add at most one change output to the outputs.\n" + "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n" + "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" + "The inputs added will not be signed, use signrawtransaction for that.\n" + "Note that all existing inputs must have their previous output transaction be in the wallet.\n" + "Note that all inputs selected must be of standard form and P2SH scripts must be\n" + "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n" + "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n" + "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "2. options (object, optional)\n" + " {\n" + " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n" + " \"changePosition\" (numeric, optional, default random) The index of the change output\n" + " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" + " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" + " \"reserveChangeKey\" (boolean, optional, default true) Reserves the change output key from the keypool\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n" + " The fee will be equally deducted from the amount of each specified output.\n" + " The outputs are specified by their zero-based index, before any change output is added.\n" + " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" + " If no outputs are specified here, the sender pays the fee.\n" + " [vout_index,...]\n" + " }\n" + " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" + "\nResult:\n" + "{\n" + " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" + " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" + " \"changepos\": n (numeric) The position of the added change output, or -1\n" + "}\n" + "\nExamples:\n" + "\nCreate a transaction with no inputs\n" + + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + + "\nAdd sufficient unsigned inputs to meet the output value\n" + + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") + + "\nSign the transaction\n" + + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + + "\nSend the transaction\n" + + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") + ); + + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + + CTxDestination changeAddress = CNoDestination(); + int changePosition = -1; + bool includeWatching = false; + bool lockUnspents = false; + bool reserveChangeKey = true; + CFeeRate feeRate = CFeeRate(0); + bool overrideEstimatedFeerate = false; + UniValue subtractFeeFromOutputs; + set setSubtractFeeFromOutputs; + + if (request.params.size() > 1) { + if (request.params[1].type() == UniValue::VBOOL) { + // backward compatibility bool only fallback + includeWatching = request.params[1].get_bool(); + } + else { + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); + + UniValue options = request.params[1]; + + RPCTypeCheckObj(options, + { + {"changeAddress", UniValueType(UniValue::VSTR)}, + {"changePosition", UniValueType(UniValue::VNUM)}, + {"includeWatching", UniValueType(UniValue::VBOOL)}, + {"lockUnspents", UniValueType(UniValue::VBOOL)}, + {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, + {"feeRate", UniValueType()}, // will be checked below + {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, + }, + true, true); + + if (options.exists("changeAddress")) { + CBitcoinAddress address(options["changeAddress"].get_str()); + + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "changeAddress must be a valid bitcoin address"); + + changeAddress = address.Get(); + } + + if (options.exists("changePosition")) + changePosition = options["changePosition"].get_int(); + + if (options.exists("includeWatching")) + includeWatching = options["includeWatching"].get_bool(); + + if (options.exists("lockUnspents")) + lockUnspents = options["lockUnspents"].get_bool(); + + if (options.exists("reserveChangeKey")) + reserveChangeKey = options["reserveChangeKey"].get_bool(); + + if (options.exists("feeRate")) + { + feeRate = CFeeRate(AmountFromValue(options["feeRate"])); + overrideEstimatedFeerate = true; + } + + if (options.exists("subtractFeeFromOutputs")) + subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array(); + } + } + + // parse hex string from parameter + CMutableTransaction tx; + if (!DecodeHexTx(tx, request.params[0].get_str(), true)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + if (tx.vout.size() == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); + + if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds"); + + for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) { + int pos = subtractFeeFromOutputs[idx].get_int(); + if (setSubtractFeeFromOutputs.count(pos)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos)); + if (pos < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos)); + if (pos >= int(tx.vout.size())) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos)); + setSubtractFeeFromOutputs.insert(pos); + } + + CAmount nFeeOut; + string strFailReason; + + if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, reserveChangeKey, changeAddress)) + throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(tx))); + result.push_back(Pair("changepos", changePosition)); + result.push_back(Pair("fee", ValueFromAmount(nFeeOut))); + + return result; +} + +// Calculate the size of the transaction assuming all signatures are max size +// Use DummySignatureCreator, which inserts 72 byte signatures everywhere. +// TODO: re-use this in CWallet::CreateTransaction (right now +// CreateTransaction uses the constructed dummy-signed tx to do a priority +// calculation, but we should be able to refactor after priority is removed). +// NOTE: this requires that all inputs must be in mapWallet (eg the tx should +// be IsAllFromMe). +int64_t CalculateMaximumSignedTxSize(const CTransaction &tx) +{ + CMutableTransaction txNew(tx); + std::vector> vCoins; + // Look up the inputs. We should have already checked that this transaction + // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our + // wallet, with a valid index into the vout array. + for (auto& input : tx.vin) { + const auto mi = pwalletMain->mapWallet.find(input.prevout.hash); + assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); + vCoins.emplace_back(make_pair(&(mi->second), input.prevout.n)); + } + if (!pwalletMain->DummySignTx(txNew, vCoins)) { + // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) + // implies that we can sign for every input. + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed"); + } + return GetVirtualTransactionSize(txNew); +} + +UniValue bumpfee(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { + throw runtime_error( + "bumpfee \"txid\" ( options ) \n" + "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n" + "An opt-in RBF transaction with the given txid must be in the wallet.\n" + "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n" + "If the change output is not big enough to cover the increased fee, the command will currently fail\n" + "instead of adding new inputs to compensate. (A future implementation could improve this.)\n" + "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n" + "By default, the new fee will be calculated automatically using estimatefee.\n" + "The user can specify a confirmation target for estimatefee.\n" + "Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate.\n" + "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n" + "returned by getnetworkinfo) to enter the node's mempool.\n" + "\nArguments:\n" + "1. txid (string, required) The txid to be bumped\n" + "2. options (object, optional)\n" + " {\n" + " \"confTarget\" (numeric, optional) Confirmation target (in blocks)\n" + " \"totalFee\" (numeric, optional) Total fee (NOT feerate) to pay, in satoshis.\n" + " In rare cases, the actual fee paid might be slightly higher than the specified\n" + " totalFee if the tx change output has to be removed because it is too close to\n" + " the dust threshold.\n" + " \"replaceable\" (boolean, optional, default true) Whether the new transaction should still be\n" + " marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n" + " be left unchanged from the original. If false, any input sequence numbers in the\n" + " original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n" + " so the new transaction will not be explicitly bip-125 replaceable (though it may\n" + " still be replacable in practice, for example if it has unconfirmed ancestors which\n" + " are replaceable).\n" + " }\n" + "\nResult:\n" + "{\n" + " \"txid\": \"value\", (string) The id of the new transaction\n" + " \"origfee\": n, (numeric) Fee of the replaced transaction\n" + " \"fee\": n, (numeric) Fee of the new transaction\n" + " \"errors\": [ str... ] (json array of strings) Errors encountered during processing (may be empty)\n" + "}\n" + "\nExamples:\n" + "\nBump the fee, get the new transaction\'s txid\n" + + HelpExampleCli("bumpfee", "")); + } + + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); + uint256 hash; + hash.SetHex(request.params[0].get_str()); + + // retrieve the original tx from the wallet + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + if (!pwalletMain->mapWallet.count(hash)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + } + CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + if (pwalletMain->HasWalletSpend(hash)) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the wallet"); + } + + { + LOCK(mempool.cs); + auto it = mempool.mapTx.find(hash); + if (it != mempool.mapTx.end() && it->GetCountWithDescendants() > 1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the mempool"); + } + } + + if (wtx.GetDepthInMainChain() != 0) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction has been mined, or is conflicted with a mined transaction"); + } + + if (!SignalsOptInRBF(wtx)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction is not BIP 125 replaceable"); + } + + if (wtx.mapValue.count("replaced_by_txid")) { + throw JSONRPCError(RPC_INVALID_REQUEST, strprintf("Cannot bump transaction %s which was already bumped by transaction %s", hash.ToString(), wtx.mapValue.at("replaced_by_txid"))); + } + + // check that original tx consists entirely of our inputs + // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) + if (!pwalletMain->IsAllFromMe(wtx, ISMINE_SPENDABLE)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that don't belong to this wallet"); + } + + // figure out which output was change + // if there was no change output or multiple change outputs, fail + int nOutput = -1; + for (size_t i = 0; i < wtx.tx->vout.size(); ++i) { + if (pwalletMain->IsChange(wtx.tx->vout[i])) { + if (nOutput != -1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has multiple change outputs"); + } + nOutput = i; + } + } + if (nOutput == -1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output"); + } + + // Calculate the expected size of the new transaction. + int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); + const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx); + + // optional parameters + bool specifiedConfirmTarget = false; + int newConfirmTarget = nTxConfirmTarget; + CAmount totalFee = 0; + bool replaceable = true; + if (request.params.size() > 1) { + UniValue options = request.params[1]; + RPCTypeCheckObj(options, + { + {"confTarget", UniValueType(UniValue::VNUM)}, + {"totalFee", UniValueType(UniValue::VNUM)}, + {"replaceable", UniValueType(UniValue::VBOOL)}, + }, + true, true); + + if (options.exists("confTarget") && options.exists("totalFee")) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction."); + } else if (options.exists("confTarget")) { + specifiedConfirmTarget = true; + newConfirmTarget = options["confTarget"].get_int(); + if (newConfirmTarget <= 0) { // upper-bound will be checked by estimatefee/smartfee + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid confTarget (cannot be <= 0)"); + } + } else if (options.exists("totalFee")) { + totalFee = options["totalFee"].get_int64(); + CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize); + if (totalFee < requiredFee ) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + strprintf("Insufficient totalFee (cannot be less than required fee %s)", + FormatMoney(requiredFee))); + } + } + + if (options.exists("replaceable")) { + replaceable = options["replaceable"].get_bool(); + } + } + + // calculate the old fee and fee-rate + CAmount nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); + CFeeRate nOldFeeRate(nOldFee, txSize); + CAmount nNewFee; + CFeeRate nNewFeeRate; + // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to + // future proof against changes to network wide policy for incremental relay + // fee that our node may not be aware of. + CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE); + if (::incrementalRelayFee > walletIncrementalRelayFee) { + walletIncrementalRelayFee = ::incrementalRelayFee; + } + + if (totalFee > 0) { + CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize); + if (totalFee < minTotalFee) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)", + FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize)))); + } + nNewFee = totalFee; + nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); + } else { + // if user specified a confirm target then don't consider any global payTxFee + if (specifiedConfirmTarget) { + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0)); + } + // otherwise use the regular wallet logic to select payTxFee or default confirm target + else { + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool); + } + + nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); + + // New fee rate must be at least old rate + minimum incremental relay rate + // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized + // in that unit (fee per kb). + // However, nOldFeeRate is a calculated value from the tx fee/size, so + // add 1 satoshi to the result, because it may have been rounded down. + if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) { + nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()); + nNewFee = nNewFeeRate.GetFee(maxNewTxSize); + } + } + + // Check that in all cases the new fee doesn't violate maxTxFee + if (nNewFee > maxTxFee) { + throw JSONRPCError(RPC_MISC_ERROR, + strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)", + FormatMoney(nNewFee), FormatMoney(maxTxFee))); + } + + // check that fee rate is higher than mempool's minimum fee + // (no point in bumping fee if we know that the new tx won't be accepted to the mempool) + // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps, + // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a + // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment. + CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) { + throw JSONRPCError(RPC_MISC_ERROR, strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK()))); + } + + // Now modify the output to increase the fee. + // If the output is not large enough to pay the fee, fail. + CAmount nDelta = nNewFee - nOldFee; + assert(nDelta > 0); + CMutableTransaction tx(*(wtx.tx)); + CTxOut* poutput = &(tx.vout[nOutput]); + if (poutput->nValue < nDelta) { + throw JSONRPCError(RPC_MISC_ERROR, "Change output is too small to bump the fee"); + } + + // If the output would become dust, discard it (converting the dust to fee) + poutput->nValue -= nDelta; + if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) { + LogPrint("rpc", "Bumping fee and discarding dust output\n"); + nNewFee += poutput->nValue; + tx.vout.erase(tx.vout.begin() + nOutput); + } + + // Mark new tx not replaceable, if requested. + if (!replaceable) { + for (auto& input : tx.vin) { + if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe; + } + } + + // sign the new tx + CTransaction txNewConst(tx); + int nIn = 0; + for (auto& input : tx.vin) { + std::map::const_iterator mi = pwalletMain->mapWallet.find(input.prevout.hash); + assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); + const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; + const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; + SignatureData sigdata; + if (!ProduceSignature(TransactionSignatureCreator(pwalletMain, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); + } + UpdateTransaction(tx, nIn, sigdata); + nIn++; + } + + // commit/broadcast the tx + CReserveKey reservekey(pwalletMain); + CWalletTx wtxBumped(pwalletMain, MakeTransactionRef(std::move(tx))); + wtxBumped.mapValue = wtx.mapValue; + wtxBumped.mapValue["replaces_txid"] = hash.ToString(); + wtxBumped.vOrderForm = wtx.vOrderForm; + wtxBumped.strFromAccount = wtx.strFromAccount; + wtxBumped.fTimeReceivedIsTxTime = true; + wtxBumped.fFromMe = true; + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { + // NOTE: CommitTransaction never returns false, so this should never happen. + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); + } + + UniValue vErrors(UniValue::VARR); + if (state.IsInvalid()) { + // This can happen if the mempool rejected the transaction. Report + // what happened in the "errors" response. + vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); + } + + // mark the original tx as bumped + if (!pwalletMain->MarkReplaced(wtx.GetHash(), wtxBumped.GetHash())) { + // TODO: see if JSON-RPC has a standard way of returning a response + // along with an exception. It would be good to return information about + // wtxBumped to the caller even if marking the original transaction + // replaced does not succeed for some reason. + vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("txid", wtxBumped.GetHash().GetHex())); + result.push_back(Pair("origfee", ValueFromAmount(nOldFee))); + result.push_back(Pair("fee", ValueFromAmount(nNewFee))); + result.push_back(Pair("errors", vErrors)); + + return result; +} + +UniValue loadwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "loadwallet \"filename\"\n" + "\nload a wallet from the wallet file .\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The wallet file\n" + "\nExamples:\n" + "\nload the wallet\n" + + HelpExampleCli("loadwallet", "\"test\"") + + "\nloadwallet the wallet\n" + + HelpExampleCli("loadwallet", "\"test\"") + + "\nloadwallet using the json rpc call\n" + + HelpExampleRpc("loadwallet", "\"test\"") + ); + + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode"); + + CWallet::LoadWalletFromFile(request.params[0].get_str()); + return NullUniValue; +} + +UniValue exportwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "exportwallet \"filename\"\n" + "\nexport a wallet to the file .\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The wallet file\n" + "\nExamples:\n" + "\export the wallet\n" + + HelpExampleRpc("exportwallet", "\"test\"") + ); + + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Exporting wallets is disabled in pruned mode"); + + CWallet::ExportWalletToFile(request.params[0].get_str()); + return NullUniValue; +} + + +extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp +extern UniValue importprivkey(const JSONRPCRequest& request); +extern UniValue importaddress(const JSONRPCRequest& request); +extern UniValue importpubkey(const JSONRPCRequest& request); +extern UniValue dumpwallet(const JSONRPCRequest& request); +extern UniValue importwallet(const JSONRPCRequest& request); +extern UniValue importprunedfunds(const JSONRPCRequest& request); +extern UniValue removeprunedfunds(const JSONRPCRequest& request); +extern UniValue importmulti(const JSONRPCRequest& request); + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} }, + { "hidden", "resendwallettransactions", &resendwallettransactions, true, {} }, + { "wallet", "abandontransaction", &abandontransaction, false, {"txid"} }, + { "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} }, + { "wallet", "backupwallet", &backupwallet, true, {"destination"} }, + { "wallet", "bumpfee", &bumpfee, true, {"txid", "options"} }, + { "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} }, + { "wallet", "dumpwallet", &dumpwallet, true, {"filename"} }, + { "wallet", "encryptwallet", &encryptwallet, true, {"passphrase"} }, + { "wallet", "getaccountaddress", &getaccountaddress, true, {"account"} }, + { "wallet", "getaccount", &getaccount, true, {"address"} }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, {"account"} }, + { "wallet", "getbalance", &getbalance, false, {"account","minconf","include_watchonly"} }, + { "wallet", "getnewaddress", &getnewaddress, true, {"account"} }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf"} }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf"} }, + { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} }, + { "wallet", "getwalletinfo", &getwalletinfo, false, {} }, + { "wallet", "importmulti", &importmulti, true, {"requests","options"} }, + { "wallet", "importprivkey", &importprivkey, true, {"privkey","label","rescan"} }, + { "wallet", "importwallet", &importwallet, true, {"filename"} }, + { "wallet", "importaddress", &importaddress, true, {"address","label","rescan","p2sh"} }, + { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} }, + { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} }, + { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} }, + { "wallet", "listaccounts", &listaccounts, false, {"minconf","include_watchonly"} }, + { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} }, + { "wallet", "listlockunspent", &listlockunspent, false, {} }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, + { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, + { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe"} }, + { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, + + { "wallet", "NormalSendToAddress", &NormalSendToAddress, true, { "address", "amount", "comment", "comment_to", "subtractfeefromamount" } }, + { "wallet", "IPCRegisterToAddress", &IPCRegisterToAddress, false, { "address", "ipclabel" } }, + { "wallet", "IPCSendToAddress", &IPCSendToAddress, false, { "txid", "index", "address" } }, + { "wallet", "IPCAuthorToAddress", &IPCAuthorToAddress, false, { "txid", "index", "address", "ipclabel" } }, + { "wallet", "IPCTokenRegToAddress", &IPCTokenRegToAddress, false, { "address", "tokenlabel" } }, + { "wallet", "IPCTokenSendToAddress", &IPCTokenSendToAddress, false, { "tokensymbol", "address", "value" } }, + { "wallet", "listunspentNormal", &listunspentNormal, false, { "minconf", "maxconf", "addresses", "include_unsafe" } }, + { "wallet", "listunspentIPC", &listunspentIPC, false, { "minconf", "maxconf", "addresses", "include_unsafe" } }, + { "wallet", "listunspentToken", &listunspentToken, false, { "minconf", "maxconf", "addresses", "include_unsafe" } }, + { "wallet", "listtokencoins", &listtokencoins, false, { "tokensymbol"} }, + + { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, + { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, + { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} }, + { "wallet", "sendtokenmany", &sendtokenmany, true, { "fromaccount", "tokensymbol", "amounts" } }, + { "wallet", "sendtoaddress", &sendtoaddress, true, { "address", "amount", "comment", "comment_to", "subtractfeefromamount" } }, + { "wallet", "setaccount", &setaccount, true, {"address","account"} }, + { "wallet", "settxfee", &settxfee, true, {"amount"} }, + { "wallet", "signmessage", &signmessage, true, {"address","message"} }, + { "wallet", "walletlock", &walletlock, true, {} }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, + { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, + { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} }, + { "wallet", "loadwallet", &loadwallet, true, {"filename"} }, + { "wallet", "exportwallet", &exportwallet, true, {"filename"} }, + { "wallet", "decodeinfobyaddress", &decodeinfobyaddress, true, { "address" } }, + + //Add command line operations that exit consensus + { "wallet", "getcurdeposit", &getcurdeposit, true, {} }, + { "wallet", "joincampaign", &joincampaign, true, {"address","deposi"} }, + { "wallet", "exitcampaign", &exitcampaign, true, {} }, + { "wallet", "isdepositabled", &isdepositabled, true, { "txid", "index" } }, + { "wallet", "punishrequest", &punishrequest, true, { "address" } }, + { "wallet", "listreward", &listreward, true, {} }, + + { "wallet", "dpocsign", &dpocsign, true,{ "hexdata" } }, + +}; + +void RegisterWalletRPCCommands(CRPCTable &t) +{ + if (GetBoolArg("-disablewallet", false)) + return; + + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + t.appendCommand(commands[vcidx].name, &commands[vcidx]); +} diff --git a/.svn/pristine/13/130602489045b8aa886ebda3799f05b463689a0a.svn-base b/.svn/pristine/13/130602489045b8aa886ebda3799f05b463689a0a.svn-base new file mode 100644 index 0000000..c72fbd0 --- /dev/null +++ b/.svn/pristine/13/130602489045b8aa886ebda3799f05b463689a0a.svn-base @@ -0,0 +1,786 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_PRIMITIVES_TRANSACTION_H +#define BITCOIN_PRIMITIVES_TRANSACTION_H + +#include "amount.h" +#include "script/script.h" +#include "serialize.h" +#include "uint256.h" + +static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; + +static const int WITNESS_SCALE_FACTOR = 4; + +extern bool b_TestTxLarge; +/** An outpoint - a combination of a transaction hash and an index n into its vout */ +class COutPoint +{ +public: + uint256 hash; + uint32_t n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(hash); + READWRITE(n); + } + + void SetNull() { hash.SetNull(); n = (uint32_t) -1; } + bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + int cmp = a.hash.Compare(b.hash); + return cmp < 0 || (cmp == 0 && a.n < b.n); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const; +}; + +/** An input of a transaction. It contains the location of the previous + * transaction's output that it claims and a signature that matches the + * output's public key. + */ +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + uint32_t nSequence; + CScriptWitness scriptWitness; //! Only serialized through CTransaction + + /* Setting nSequence to this value for every input in a transaction + * disables nLockTime. */ + static const uint32_t SEQUENCE_FINAL = 0xffffffff; + + /* Below flags apply in the context of BIP 68*/ + /* If this flag set, CTxIn::nSequence is NOT interpreted as a + * relative lock-time. */ + static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31); + + /* If CTxIn::nSequence encodes a relative lock-time and this flag + * is set, the relative lock-time has units of 512 seconds, + * otherwise it specifies blocks with a granularity of 1. */ + static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22); + + /* If CTxIn::nSequence encodes a relative lock-time, this mask is + * applied to extract that lock-time from the sequence field. */ + static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff; + + /* In order to use the same number of bits to encode roughly the + * same wall-clock duration, and because blocks are naturally + * limited to occur every 600s on average, the minimum granularity + * for time-based relative lock-time is fixed at 512 seconds. + * Converting from CTxIn::nSequence to seconds is performed by + * multiplying by 512 = 2^9, or equivalently shifting up by + * 9 bits. */ + static const int SEQUENCE_LOCKTIME_GRANULARITY = 9; + + CTxIn() + { + nSequence = SEQUENCE_FINAL; + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL); + CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(prevout); + READWRITE(*(CScriptBase*)(&scriptSig)); + READWRITE(nSequence); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToString() const; +}; + +enum TransactionOutType_t{ + TXOUT_INVALID = -1, //invalid + TXOUT_NORMAL = 0, // Ordinary output type + TXOUT_CAMPAIGN = 1, // Campaign model output type + TXOUT_IPCOWNER = 2, // Type of ownership output + TXOUT_IPCAUTHORIZATION = 3, // Authorized output type + TXOUT_TOKENREG = 4, // Type of token export + TXOUT_TOKEN = 5 // Token output type +}; + +//The IP tag class in the output structure +class IPCLabel +{ +public: + uint8_t ExtendType; //Extension type + uint32_t startTime; //Start time .4 bytes + uint32_t stopTime; //Cutoff time .4 bytes + uint8_t reAuthorize; //Reauthorization flag bit + uint8_t uniqueAuthorize; //Unique authorization mark bit + uint8_t hashLen = 16; //Known content HASH value length. The MD5 format is fixed to 16 bytes, or 128bit + uint128 hash; //Known content HASH value + std::string labelTitle; //Knowledge of content titles + + IPCLabel() { SetNull(); } + + uint32_t size() const { return 1 + 4 + 4 + 1 + 1 + 1 + 16 + 1 + labelTitle.size(); }; + ADD_SERIALIZE_METHODS; + + void SetNull() { hash.SetNull(); ExtendType = (uint8_t)-1; } + bool IsNull() const { return (hash.IsNull() && ExtendType == (uint8_t)-1); } + + void operator=(const IPCLabel& a) + { + ExtendType = a.ExtendType; + startTime = a.startTime; + stopTime = a.stopTime; + reAuthorize = a.reAuthorize; + uniqueAuthorize = a.uniqueAuthorize; + hash = a.hash; + labelTitle = a.labelTitle; + } + + friend bool operator==(const IPCLabel& a, const IPCLabel& b) + { + return (a.ExtendType == b.ExtendType && + a.startTime == b.startTime && + a.stopTime == b.stopTime && + a.reAuthorize == b.reAuthorize && + a.uniqueAuthorize == b.uniqueAuthorize && + a.hash == b.hash && + a.labelTitle == b.labelTitle); + } + + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(ExtendType); + READWRITE(startTime); + READWRITE(stopTime); + READWRITE(reAuthorize); + READWRITE(uniqueAuthorize); + READWRITE(hashLen); + READWRITE(hash); + READWRITE(labelTitle); + } +}; + +enum CampaignType_t{ + TYPE_CONSENSUS_NULL = -1, //Not on the campaign trail + TYPE_CONSENSUS_REGISTER = 0, // Apply to join + TYPE_CONSENSUS_QUITE = 1, // To quit + TYPE_CONSENSUS_ORDINARY_PUSNISHMENT = 2, // Ordinary punishment + TYPE_CONSENSUS_SEVERE_PUNISHMENT = 3, // severe punishment + TYPE_CONSENSUS_RETURN_DEPOSI = 4, // Return the deposi + TYPE_CONSENSUS_SEVERE_PUNISHMENT_REQUEST = 5 //Severe punishment application +}; + +//Campaign label classes in IPC output structure +class DevoteLabel +{ +public: + uint8_t ExtendType; //Extension type + uint160 hash; //PubKey HASH OF A candidate + + DevoteLabel() { SetNull(); } + + + uint32_t size() const { return 1+sizeof(uint160); }; + ADD_SERIALIZE_METHODS; + + void SetNull() { hash.SetNull(); ExtendType = (uint8_t)-1; } + bool IsNull() const { return (hash.IsNull() && ExtendType == (uint8_t)-1); } + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(ExtendType); + READWRITE(hash); + } + + void operator=(const DevoteLabel& a) + { + ExtendType = a.ExtendType; + hash = a.hash; + } +}; + +//IPC output structure of the token class +class TokenRegLabel +{ +public: + uint8_t TokenSymbol[9]; //token Symbolunique + uint64_t value; // + uint128 hash; //Token HASH tagMD5 + uint8_t label[17]; //Tokens label + uint32_t issueDate; //Scrip release time + uint64_t totalCount; //The total amount of tokens issued + uint8_t accuracy; //precision + TokenRegLabel() { SetNull(); } + + + uint32_t size() const { return 8+8+16+16+4+8+1; }; + + + std::string getTokenSymbol() const { return std::string((char*)TokenSymbol); }; + std::string getTokenLabel() const { return std::string((char*)label); }; + + ADD_SERIALIZE_METHODS; + + void SetNull() { hash.SetNull(); value = 0; memset(TokenSymbol, 0x00, 9); memset(label, 0x00, 17); } + bool IsNull() const { return (hash.IsNull() && value == 0); } + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(TokenSymbol[0]); + READWRITE(TokenSymbol[1]); + READWRITE(TokenSymbol[2]); + READWRITE(TokenSymbol[3]); + READWRITE(TokenSymbol[4]); + READWRITE(TokenSymbol[5]); + READWRITE(TokenSymbol[6]); + READWRITE(TokenSymbol[7]); + READWRITE(value); + READWRITE(hash); + READWRITE(label[0]); + READWRITE(label[1]); + READWRITE(label[2]); + READWRITE(label[3]); + READWRITE(label[4]); + READWRITE(label[5]); + READWRITE(label[6]); + READWRITE(label[7]); + READWRITE(label[8]); + READWRITE(label[9]); + READWRITE(label[10]); + READWRITE(label[11]); + READWRITE(label[12]); + READWRITE(label[13]); + READWRITE(label[14]); + READWRITE(label[15]); + READWRITE(issueDate); + READWRITE(totalCount); + READWRITE(accuracy); + } +}; + +//IPC output structure of the token class +class TokenLabel +{ +public: + uint8_t TokenSymbol[9]; //tokenSymbolunique + uint64_t value; //Number of tokens + uint8_t accuracy; //precision + TokenLabel() { SetNull(); } + + + uint32_t size() const { return 8 + 8 + 1; }; + + + std::string getTokenSymbol() const { return std::string((char*)TokenSymbol); }; + + ADD_SERIALIZE_METHODS; + + void SetNull() { value = 0; memset(TokenSymbol, 0x00, 9); } + bool IsNull() const { return value == 0; } + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(TokenSymbol[0]); + READWRITE(TokenSymbol[1]); + READWRITE(TokenSymbol[2]); + READWRITE(TokenSymbol[3]); + READWRITE(TokenSymbol[4]); + READWRITE(TokenSymbol[5]); + READWRITE(TokenSymbol[6]); + READWRITE(TokenSymbol[7]); + READWRITE(value); + READWRITE(accuracy); + } + +}; + +/** An output of a transaction. It contains the public key that the next input + * must be able to sign with to claim it. + */ +class CTxOut +{ +public: + CAmount nValue; + uint8_t txType; //Output UTXO type + std::string coinbaseScript = "";//This field is used only by the CoinBase output mining bonus and is considered a common transaction + uint8_t labelLen; //Length of product labels (used when serializing output) + IPCLabel ipcLabel; //Knowledge of labelling content + DevoteLabel devoteLabel; //Campaign label content + TokenRegLabel tokenRegLabel; //Scrip registration label content + TokenLabel tokenLabel; //Token content of tokens + CScript scriptPubKey; + uint16_t txLabelLen = 0; //Label/escape length + std::string txLabel; //Label/escape + std::string ipcLabelinfo; + + CTxOut() + { + SetNull(); + } + //Ordinary transaction output constructor + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn); + + //CoinBase transaction output constructor + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, std::string coinbaseScriptIn); + + + //Apply to join the transaction output constructor + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn,DevoteLabel& devoteLableIn); + //Apply to exit the transaction output constructor + CTxOut(const CAmount& nValueIn, DevoteLabel& devoteLableIn); + + // the normal penalty transaction output constructor + // severe penalty transaction output constructor + CTxOut(CampaignType_t campaignType, uint160 campaignPubkeyHash); + + // serious penalties for the application of constructors + CTxOut(uint160 campaignPubkeyHash, const std::string evidence); + + // refund trading output constructor + CTxOut(const CAmount& refund, uint160 campaignPubkeyHash); + + //The constructor of the ownership/authorization transaction output + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, TransactionOutType_t txTypeIn, IPCLabel& labelIn, std::string voutLabel = ""); + + //The ownership constructor + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, TransactionOutType_t txTypeIn, IPCLabel& labelIn, std::string voutLabel, std::string ipcinfo); + + //A constructor for the output of a token transaction + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, TokenRegLabel& regLableIn, std::string voutLabel = ""); + + //A constructor for the export of tokens + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, TokenLabel& tokenLableIn, std::string voutLabel = ""); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + + READWRITE(nValue); + READWRITE(txType); + switch (txType) + { + + case 1: + labelLen = devoteLabel.size(); + READWRITE(labelLen); + if (labelLen > 0) + { + READWRITE(devoteLabel); + } + + break; + + case 2: + case 3: + labelLen = ipcLabel.size(); + READWRITE(labelLen); + if (labelLen > 0) + { + READWRITE(ipcLabel); + } + + break; + + case 4: + labelLen = tokenRegLabel.size(); + READWRITE(labelLen); + if (labelLen > 0) + { + READWRITE(tokenRegLabel); + } + + break; + + case 5: + labelLen = tokenLabel.size(); + READWRITE(labelLen); + if (labelLen > 0) + { + READWRITE(tokenLabel); + } + + break; + case 0: + READWRITE(coinbaseScript); + break; + default: + return; + + } + + READWRITE(*(CScriptBase*)(&scriptPubKey)); + READWRITE(txLabel); + txLabelLen = txLabel.size(); + + } + + void SetNull() + { + nValue = -1; + txType = TXOUT_INVALID; + labelLen = 0; + scriptPubKey.clear(); + txLabel.clear(); + coinbaseScript.clear(); + ipcLabelinfo.clear(); + } + + bool IsNull() const + { + return (nValue == -1); + } + + //Get the transaction subtype of the campaign + CampaignType_t GetCampaignType() const; + std::string GetCheckBlockContent() const{ return coinbaseScript; }; + uint160 GetCampaignHash() const{ return devoteLabel.hash; }; + + CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const + { + //for IPC or Token, we allow the IPC/Token UTXO's value = 0 + if (txType != 0) + return 0; + + // "Dust" is defined in terms of CTransaction::minRelayTxFee, + // which has units satoshis-per-kilobyte. + // If you'd pay more than 1/3 in fees + // to spend something, then we consider it dust. + // A typical spendable non-segwit txout is 34 bytes big, and will + // need a CTxIn of at least 148 bytes to spend: + // so dust is a spendable txout less than + // 546*minRelayTxFee/1000 (in satoshis). + // A typical spendable segwit txout is 31 bytes big, and will + // need a CTxIn of at least 67 bytes to spend: + // so dust is a spendable txout less than + // 294*minRelayTxFee/1000 (in satoshis). + if (scriptPubKey.IsUnspendable()) + return 0; + + size_t nSize = GetSerializeSize(*this, SER_DISK, 0); + int witnessversion = 0; + std::vector witnessprogram; + + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + // sum the sizes of the parts of a transaction input + // with 75% segwit discount applied to the script size. + nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); + } else { + nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above + } + + return 3 * minRelayTxFee.GetFee(nSize); + } + + bool IsDust(const CFeeRate &minRelayTxFee) const + { + return (nValue < GetDustThreshold(minRelayTxFee)); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + std::string ToString() const; +}; + +struct CMutableTransaction; + +/** + * Basic transaction serialization format: + * - int32_t nVersion + * - std::vector vin + * - std::vector vout + * - uint32_t nLockTime + * + * Extended transaction serialization format: + * - int32_t nVersion + * - unsigned char dummy = 0x00 + * - unsigned char flags (!= 0) + * - std::vector vin + * - std::vector vout + * - if (flags & 1): + * - CTxWitness wit; + * - uint32_t nLockTime + */ +template +inline void UnserializeTransaction(TxType& tx, Stream& s) { + //const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS); + const bool fAllowWitness = false; + + s >> tx.nVersion; + unsigned char flags = 0; + tx.vin.clear(); + tx.vout.clear(); + /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */ + s >> tx.vin; + if (tx.vin.size() == 0 && fAllowWitness) { + /* We read a dummy or an empty vin. */ + s >> flags; + if (flags != 0) { + s >> tx.vin; + s >> tx.vout; + } + } else { + /* We read a non-empty vin. Assume a normal vout follows. */ + s >> tx.vout; + } + if ((flags & 1) && fAllowWitness) { + /* The witness flag is present, and we support witnesses. */ + flags ^= 1; + for (size_t i = 0; i < tx.vin.size(); i++) { + s >> tx.vin[i].scriptWitness.stack; + } + } + if (flags) { + /* Unknown flag in the serialization */ + throw std::ios_base::failure("Unknown transaction optional data"); + } + s >> tx.nLockTime; +} + +template +inline void SerializeTransaction(const TxType& tx, Stream& s) { + //const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS); + const bool fAllowWitness = false; + + s << tx.nVersion; + unsigned char flags = 0; + // Consistency check + if (fAllowWitness) { + /* Check whether witnesses need to be serialized. */ + if (tx.HasWitness()) { + flags |= 1; + } + } + if (flags) { + /* Use extended format in case witnesses are to be serialized. */ + std::vector vinDummy; + s << vinDummy; + s << flags; + } + s << tx.vin; + s << tx.vout; + if (flags & 1) { + for (size_t i = 0; i < tx.vin.size(); i++) { + s << tx.vin[i].scriptWitness.stack; + } + } + s << tx.nLockTime; +} + + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ +class CTransaction +{ +public: + // Default transaction version. + static const int32_t CURRENT_VERSION=2; + + // Changing the default transaction version requires a two step process: first + // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date + // bumping the default CURRENT_VERSION at which point both CURRENT_VERSION and + // MAX_STANDARD_VERSION will be equal. + static const int32_t MAX_STANDARD_VERSION=2; + + // The local variables are made const to prevent unintended modification + // without updating the cached hash value. However, CTransaction is not + // actually immutable; deserialization and assignment are implemented, + // and bypass the constness. This is safe, as they update the entire + // structure, including the hash. + const int32_t nVersion; + const std::vector vin; + const std::vector vout; + const uint32_t nLockTime; + +private: + /** Memory only. */ + const uint256 hash; + + uint256 ComputeHash() const; + +public: + /** Construct a CTransaction that qualifies as IsNull() */ + CTransaction(); + + /** Convert a CMutableTransaction into a CTransaction. */ + CTransaction(const CMutableTransaction &tx); + CTransaction(CMutableTransaction &&tx); + + template + inline void Serialize(Stream& s) const { + SerializeTransaction(*this, s); + } + + /** This deserializing constructor is provided instead of an Unserialize method. + * Unserialize is not possible, since it would require overwriting const fields. */ + template + CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {} + + bool IsNull() const { + return vin.empty() && vout.empty(); + } + + const uint256& GetHash() const { + return hash; + } + + // Compute a hash that includes both transaction and witness data + uint256 GetWitnessHash() const; + + // Return sum of txouts. + CAmount GetValueOut() const; + // GetValueIn() is a method on CCoinsViewCache, because + // inputs must be known to compute value in. + + //Acquisition transaction type + uint8_t GetTxType() const; + //Get the transaction subtype of the campaign + CampaignType_t GetCampaignType() const; + //Get the candidate pubkeyHash + uint160 GetCampaignPublickey() const; + //Get a campaign registration payment deposit + CAmount GetRegisterIPC() const; + //Obtain evidence for the punishment + std::string GetCampaignEvidence() const; + + // Compute priority, given priority of inputs and (optionally) tx size + double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; + + // Compute modified tx size for priority calculation (optionally given tx size) + unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; + + /** + * Get the total transaction size in bytes, including witness data. + * "Total Size" defined in BIP141 and BIP144. + * @return Total transaction size in bytes + */ + unsigned int GetTotalSize() const; + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return a.hash == b.hash; + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return a.hash != b.hash; + } + + std::string ToString() const; + + bool HasWitness() const + { + for (size_t i = 0; i < vin.size(); i++) { + if (!vin[i].scriptWitness.IsNull()) { + return true; + } + } + return false; + } +}; + +/** A mutable version of CTransaction. */ +struct CMutableTransaction +{ + int32_t nVersion; + std::vector vin; + std::vector vout; + uint32_t nLockTime; + + CMutableTransaction(); + CMutableTransaction(const CTransaction& tx); + + template + inline void Serialize(Stream& s) const { + SerializeTransaction(*this, s); + } + + + template + inline void Unserialize(Stream& s) { + UnserializeTransaction(*this, s); + } + + template + CMutableTransaction(deserialize_type, Stream& s) { + Unserialize(s); + } + + /** Compute the hash of this CMutableTransaction. This is computed on the + * fly, as opposed to GetHash() in CTransaction, which uses a cached result. + */ + uint256 GetHash() const; + + friend bool operator==(const CMutableTransaction& a, const CMutableTransaction& b) + { + return a.GetHash() == b.GetHash(); + } + + bool HasWitness() const + { + for (size_t i = 0; i < vin.size(); i++) { + if (!vin[i].scriptWitness.IsNull()) { + return true; + } + } + return false; + } +}; + +typedef std::shared_ptr CTransactionRef; +static inline CTransactionRef MakeTransactionRef() { return std::make_shared(); } +template static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared(std::forward(txIn)); } + +/** Compute the weight of a transaction, as defined by BIP 141 */ +int64_t GetTransactionWeight(const CTransaction &tx); + +#endif // BITCOIN_PRIMITIVES_TRANSACTION_H diff --git a/.svn/pristine/18/18b6086d23b124f26dfa5abfb3db12f08dfbeb48.svn-base b/.svn/pristine/18/18b6086d23b124f26dfa5abfb3db12f08dfbeb48.svn-base new file mode 100644 index 0000000..5cea302 --- /dev/null +++ b/.svn/pristine/18/18b6086d23b124f26dfa5abfb3db12f08dfbeb48.svn-base @@ -0,0 +1,5494 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "validation.h" + +#include "arith_uint256.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "checkqueue.h" +#include "consensus/consensus.h" +#include "consensus/merkle.h" +#include "consensus/validation.h" +#include "hash.h" +#include "init.h" +#include "policy/fees.h" +#include "policy/policy.h" +#include "pow.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "random.h" +#include "script/script.h" +#include "script/sigcache.h" +#include "script/standard.h" +#include "timedata.h" +#include "tinyformat.h" +#include "txdb.h" +#include "txmempool.h" +#include "ui_interface.h" +#include "undo.h" +#include "util.h" +#include "utilmoneystr.h" +#include "utilstrencodings.h" +#include "validationinterface.h" +#include "versionbits.h" +#include "warnings.h" +#include "base58.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "rpc/protocol.h" + +#include "dpoc/ConsensusAccountPool.h" +#include "dpoc/DpocMining.h" +#include "dpoc/SerializeDpoc.h" +#include +#include "dbwrapper.h" + +#if defined(NDEBUG) +# error "Bitcoin cannot be compiled without assertions." +#endif + +/** + * Global state + */ +bool g_bConsensus = false; + +CCriticalSection cs_main; + +BlockMap mapBlockIndex; +CChain chainActive; +CBlockIndex *pindexBestHeader = NULL; +CWaitableCriticalSection csBestBlock; +CConditionVariable cvBlockChange; +int nScriptCheckThreads = 0; +std::atomic_bool fImporting(false); +bool fReindex = false; +bool fTxIndex = true; +bool fHavePruned = false; +bool fPruneMode = false; +bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; +bool fRequireStandard = true; +bool fCheckBlockIndex = false; +bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; +size_t nCoinCacheUsage = 5000 * 300; +uint64_t nPruneTarget = 0; +int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; +bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; + +TxDBProcess* pTxDB = NULL; +bool fAddressIndex = false; +extern bool g_bStdCout; + +uint256 hashAssumeValid; + +CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); +CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; + +CTxMemPool mempool(::minRelayTxFee); + +static void CheckBlockIndex(const Consensus::Params& consensusParams); + +/** Constant stuff for coinbase transactions we create: */ +CScript COINBASE_FLAGS; + +const std::string strMessageMagic = "Bitcoin Signed Message:\n"; + +// Internal stuff +namespace { + + struct CBlockIndexWorkComparator + { + bool operator()(CBlockIndex *pa, CBlockIndex *pb) const { + // First sort by most total work, ... + if (pa->nChainWork > pb->nChainWork) return false; + if (pa->nChainWork < pb->nChainWork) return true; + + // ... then by earliest time received, ... + if (pa->nSequenceId < pb->nSequenceId) return false; + if (pa->nSequenceId > pb->nSequenceId) return true; + + // Use pointer address as tie breaker (should only happen with blocks + // loaded from disk, as those all have id 0). + if (pa < pb) return false; + if (pa > pb) return true; + + // Identical blocks. + return false; + } + }; + + CBlockIndex *pindexBestInvalid; + + /** + * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and + * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be + * missing the data for the block. + */ + std::set setBlockIndexCandidates; + /** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions. + * Pruned nodes may have entries where B is missing data. + */ + std::multimap mapBlocksUnlinked; + + CCriticalSection cs_LastBlockFile; + std::vector vinfoBlockFile; + int nLastBlockFile = 0; + /** Global flag to indicate we should check to see if there are + * block/undo files that should be deleted. Set on startup + * or if we allocate more file space when we're in prune mode + */ + bool fCheckForPruning = false; + + /** + * Every received block is assigned a unique and increasing identifier, so we + * know which one to give priority in case of a fork. + */ + CCriticalSection cs_nBlockSequenceId; + /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ + int32_t nBlockSequenceId = 1; + /** Decreasing counter (used by subsequent preciousblock calls). */ + int32_t nBlockReverseSequenceId = -1; + /** chainwork for the last block that preciousblock has been applied to. */ + arith_uint256 nLastPreciousChainwork = 0; + + /** Dirty block index entries. */ + std::set setDirtyBlockIndex; + + /** Dirty block file entries. */ + std::set setDirtyFileInfo; +} // anon namespace + +/* Use this class to start tracking transactions that are removed from the + * mempool and pass all those transactions through SyncTransaction when the + * object goes out of scope. This is currently only used to call SyncTransaction + * on conflicts removed from the mempool during block connection. Applied in + * ActivateBestChain around ActivateBestStep which in turn calls: + * ConnectTip->removeForBlock->removeConflicts + */ +struct CAddressData +{ +public: + uint64_t blockIndex; //block Index + int InOrOut; // = 0txout = 1txin + int nIndex; //Index + uint8_t txType; //Transaction type + CAmount amount; //The amount of +out -in + std::string address; + uint128 hash; //IPC hash + std::string strsymbol; //Token symbol + uint64_t tokenvalue; //tokenvalue +out -:in + CAddressData(uint64_t blockIndexIn, int InOrOutIn, int n, uint8_t type, CAmount a, std::string& str){ + //time = t; + blockIndex = blockIndexIn; + InOrOut = InOrOutIn; + nIndex = n; + txType = type; + amount = a; + address = str; + hash.SetNull(); + strsymbol = ""; + tokenvalue = 0; + + } +}; + +std::map tokenDataMap; //token map key:symnbol value:TokenRegLabel +std::map newTokenDataMap; //When connectblock, the current block needs to be added to the tokenDataMap data +//IPC related unique values for the use of MAP, and the pressing operation +//Element meaning: the key value is TokenSymbol, string. Value is the corresponding txid and state bits. State =0 is a temporary, unconfirmed transaction in the pool. State =1 is permanent, is a transaction that has been booked into block. +IPCCheckMaps* pIPCCheckMaps = NULL; +IPCCheckMaps tmpIPCUniqueCheckMap; +IPCCheckMaps newIPCCKMap; + +class MemPoolConflictRemovalTracker +{ +private: + std::vector conflictedTxs; + CTxMemPool &pool; + +public: + MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) { + pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + } + + void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) { + if (reason == MemPoolRemovalReason::CONFLICT) { + conflictedTxs.push_back(txRemoved); + } + } + + ~MemPoolConflictRemovalTracker() { + pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + for (const auto& tx : conflictedTxs) { + GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); + } + conflictedTxs.clear(); + } +}; + +CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) +{ + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, locator.vHave) { + BlockMap::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (chain.Contains(pindex)) + return pindex; + if (pindex->GetAncestor(chain.Height()) == chain.Tip()) { + return chain.Tip(); + } + } + } + return chain.Genesis(); +} + +CCoinsViewCache *pcoinsTip = NULL; +CBlockTreeDB *pblocktree = NULL; +std::string IPCHashCkFileName = std::string("IPCck"); +std::string TokenSymCkFileName = std::string("TSymbolck"); +std::string TokenHashCkFileName = std::string("THashck"); +std::string FileTokenDataName = std::string("TokenData"); +//Write the Maps in memory to disk +bool CVerifyDB:: FlushICMToDisk() +{ + IPCCheckMaps ttCheckMaps; + CKSizeClass ttcksize; + ttcksize.setNull(); + + uint64_t nSeek = uint64_t(0); + std::map>::iterator TSit; + std::map>::iterator Hit; + + CKTSMapDataClass ttTSMapData; + ttTSMapData.setNull(); + CSerializeDpoc rwSerTSymbol; + IPCCheckMaps* pIPCCheck = &newIPCCKMap; + for (TSit = pIPCCheck->TokenSymbolMap.begin(); TSit != pIPCCheck->TokenSymbolMap.end(); TSit++) + { + ttTSMapData.setNull(); + ttTSMapData.tokensymbol = TSit->first; + ttTSMapData.txid = TSit->second.first; + ttTSMapData.bstate = TSit->second.second; + if (!rwSerTSymbol.WriteToDisk(ttTSMapData, TokenSymCkFileName)) + { + LogPrintf(" [FlushICMToDisk] : [WriteToDisk : %s] ,return erro! \n", TokenSymCkFileName); + return false; + } + + } + + CKHashMapDataClass ttTHMapData; + CSerializeDpoc rwSerTHash; + + for (Hit = pIPCCheck->TokenHashMap.begin(); Hit != pIPCCheck->TokenHashMap.end(); Hit++) + { + ttTHMapData.setNull(); + ttTHMapData.hash = Hit->first; + ttTHMapData.txid = Hit->second.first; + ttTHMapData.bstate = Hit->second.second; + if (!rwSerTHash.WriteToDisk(ttTHMapData, TokenHashCkFileName)) + { + LogPrintf(" [FlushICMToDisk] : [WriteToDisk : %s] ,return erro! \n", TokenHashCkFileName); + return false; + } + + } + + + for (Hit = pIPCCheck->IPCHashMap.begin(); Hit != pIPCCheck->IPCHashMap.end(); Hit++) + { + + ttTHMapData.setNull(); + ttTHMapData.hash.SetHex(Hit->first.ToString()); + ttTHMapData.txid = Hit->second.first; + ttTHMapData.bstate = Hit->second.second; + if (!rwSerTHash.WriteToDisk(ttTHMapData, IPCHashCkFileName)) + { + LogPrintf(" [FlushICMToDisk] : [WriteToDisk : %s] ,return erro! \n", IPCHashCkFileName); + return false; + } + + } + + TokenRegLabel ttTokenRegData; + ttTokenRegData.SetNull(); + CSerializeDpoc rwSerTokenData; + std::map::iterator TDit; + + for (TDit = newTokenDataMap.begin(); TDit != newTokenDataMap.end(); TDit++) + { + ttTokenRegData.SetNull(); + memcpy((char*)(ttTokenRegData.TokenSymbol), (char*)(TDit->second.TokenSymbol), sizeof(ttTokenRegData.TokenSymbol)); + ttTokenRegData.value = TDit->second.value; + ttTokenRegData.hash = TDit->second.hash; + memcpy((char*)(ttTokenRegData.label), (char*)(TDit->second.label), sizeof(ttTokenRegData.label)); + ttTokenRegData.issueDate = TDit->second.issueDate; + ttTokenRegData.totalCount = TDit->second.totalCount; + ttTokenRegData.accuracy = TDit->second.accuracy; + + if (!rwSerTokenData.WriteToDisk(ttTokenRegData, FileTokenDataName)) + { + LogPrintf(" [FlushICMToDisk] : [WriteToDisk : %s] ,return erro! \n", FileTokenDataName); + return false; + } + + } + newIPCCKMap.clear(); //Save the empty + newTokenDataMap.clear(); + LogPrintf("[CVerifyDB:: FlushICMToDisk by true]\n"); + return true; +} +//Loads the Maps data from the disk to local memory +bool CVerifyDB::LoadICMFromDisk() +{ + + uint64_t nSeek = 0; + uint64_t filesize = 0; + boost::filesystem::path pathTmpTS = GetDataDir() / TokenSymCkFileName; + if (boost::filesystem::exists(pathTmpTS)) + { + filesize = boost::filesystem::file_size(pathTmpTS); + CSerializeDpoc rwSerTS; + CKTSMapDataClass ttTSMapData; + + while (nSeek < filesize) + { + ttTSMapData.setNull(); + if (!rwSerTS.ReadFromDisk(ttTSMapData, nSeek, TokenSymCkFileName)) + { + LogPrintf(" [LoadICMFromDisk]::[ReadFromDisk%s ] return false! \n", TokenSymCkFileName); + return false; + } + pIPCCheckMaps->TokenSymbolMap.insert(std::make_pair(ttTSMapData.tokensymbol, std::make_pair(ttTSMapData.txid, ttTSMapData.bstate))); + nSeek += getSize(ttTSMapData); + } + } + filesize = 0; + nSeek = 0; + boost::filesystem::path pathTmpTH = GetDataDir() / TokenHashCkFileName; //TokenhashsMap + if (boost::filesystem::exists(pathTmpTH)) + { + filesize = boost::filesystem::file_size(pathTmpTH); + CSerializeDpoc rwSerTH; + CKHashMapDataClass ttTHMapData; + + while (nSeek < filesize) + { + ttTHMapData.setNull(); + if (!rwSerTH.ReadFromDisk(ttTHMapData, nSeek, TokenHashCkFileName)) + { + LogPrintf(" [LoadICMFromDisk]::[ReadFromDisk%s ] return false! \n", TokenHashCkFileName); + return false; + } + pIPCCheckMaps->TokenHashMap.insert(std::make_pair(ttTHMapData.hash, std::make_pair(ttTHMapData.txid, ttTHMapData.bstate))); + nSeek += getSize(ttTHMapData); + } + } + filesize = 0; + nSeek = 0; + boost::filesystem::path pathTmpIH = GetDataDir() / IPCHashCkFileName; //IPChashsMap + if (boost::filesystem::exists(pathTmpIH)) + { + filesize = boost::filesystem::file_size(pathTmpIH); + CSerializeDpoc rwSerIH; + CKHashMapDataClass ttIHMapData; + while (nSeek < filesize) + { + ttIHMapData.setNull(); + if (!rwSerIH.ReadFromDisk(ttIHMapData, nSeek, IPCHashCkFileName)) + { + LogPrintf(" [LoadICMFromDisk]::[ReadFromDisk%s ] return false! \n", IPCHashCkFileName); + return false; + } + pIPCCheckMaps->IPCHashMap.insert(std::make_pair(ttIHMapData.hash, std::make_pair(ttIHMapData.txid, ttIHMapData.bstate))); + nSeek += getSize(ttIHMapData); + } + } + filesize = 0; + nSeek = 0; + boost::filesystem::path pathTmpTD = GetDataDir() / FileTokenDataName; //TokenDataMap + if (boost::filesystem::exists(pathTmpTD)) + { + LogPrintf(" [LoadICMFromDisk]::loading file %s .\n", FileTokenDataName); + filesize = boost::filesystem::file_size(pathTmpTD); + CSerializeDpoc rwSerTD; + TokenRegLabel ttTokenRegData; + while (nSeek < filesize) + { + ttTokenRegData.SetNull(); + if (!rwSerTD.ReadFromDisk(ttTokenRegData, nSeek, FileTokenDataName)) + { + LogPrintf(" [LoadICMFromDisk]::[ReadFromDisk%s ] return false! \n", FileTokenDataName); + return false; + } + tokenDataMap.insert(std::make_pair(ttTokenRegData.getTokenSymbol(), ttTokenRegData)); + nSeek += ttTokenRegData.size(); + } + } + LogPrintf(" [LoadICMFromDisk]::load all! \n"); + return true; +} +void PushTxToIPCValuesToMap(const CTransaction& tx, IPCCheckMaps* pIPCCheckMaps) +{ + BOOST_FOREACH(const CTxOut& txout, tx.vout) { + switch (txout.txType) + { + case 2: + pIPCCheckMaps->IPCHashMap.insert(std::make_pair(txout.ipcLabel.hash, std::make_pair(tx.GetHash(), 0))); + break; + + case 4: + pIPCCheckMaps->TokenSymbolMap.insert(std::make_pair(txout.tokenRegLabel.getTokenSymbol(), std::make_pair(tx.GetHash(), 0))); + pIPCCheckMaps->TokenHashMap.insert(std::make_pair(txout.tokenRegLabel.hash, std::make_pair(tx.GetHash(), 0))); + break; + + default: + break; + } + } +} +void PushTxToTokenDataMap(const CTransaction& tx, std::map* pTokenDataMap) +{ + BOOST_FOREACH(const CTxOut& txout, tx.vout) { + switch (txout.txType) + { + case 4: + pTokenDataMap->insert(std::make_pair(txout.tokenRegLabel.getTokenSymbol(), txout.tokenRegLabel)); + break; + + default: + break; + } + } +} + +void AddTx2MapbyAddress(const CTransaction &tx,int blockIndex = -1) +{ + AssertLockHeld(cs_main); + AssertLockHeld(mempool.cs); + txnouttype type; + std::vector prevdestes; + int nRequired; + std::vector Datavectors; + Datavectors.clear(); + uint256 txhash = tx.GetHash(); //txid + uint64_t tokenbalance = 0; + if (!tx.IsCoinBase()) + { + //vin[] + for (unsigned int i = 0; i < tx.vin.size(); i++) { + CTransactionRef prevTx; + uint256 hashBlock; + + if (!GetTransaction(tx.vin[i].prevout.hash, prevTx, Params().GetConsensus(), hashBlock, true)) + return; + + CTxOut prevout = prevTx->vout[tx.vin[i].prevout.n]; + ExtractDestinations(prevout.scriptPubKey, type, prevdestes, nRequired); + BOOST_FOREACH(CTxDestination &prevdest, prevdestes){ + std::string address = CBitcoinAddress(prevdest).ToString(); + pTxDB->Insert(pTxDB->db, (char*)(address.c_str()), (char*)(txhash.GetHex().c_str())); + + CAddressData data(blockIndex, 1,i,prevout.txType ,prevout.nValue*-1,address); + CTxaddressData txdata(address, 1, prevout.nValue*-1); + switch (prevout.txType) + { + case 2: + case 3: + data.hash = prevout.ipcLabel.hash; + break; + case 4: + //Keep the symbol in token + data.strsymbol = prevout.tokenRegLabel.getTokenSymbol(); + data.tokenvalue = prevout.tokenRegLabel.totalCount*-1; + break; + case 5: + //Keep the symbol in token + data.strsymbol = prevout.tokenLabel.getTokenSymbol(); + data.tokenvalue = prevout.tokenLabel.value*-1; + break; + default: + break; + } + if ((prevout.txType == 2 || prevout.txType == 3 )) + { + pTxDB->Insert(pTxDB->db, (char*)(data.hash.GetHex().c_str()), (char*)(txhash.GetHex().c_str())); + } + if ((prevout.txType == 4 || prevout.txType == 5)) + { + pTxDB->Insert(pTxDB->db, (char*)(data.strsymbol.c_str()), (char*)(txhash.GetHex().c_str())); + } + Datavectors.push_back(txdata); + } + } + } + //vout[] + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut output = tx.vout[i]; + { + ExtractDestinations(output.scriptPubKey, type, prevdestes, nRequired); + BOOST_FOREACH(CTxDestination &prevdest, prevdestes){ + std::string address = CBitcoinAddress(prevdest).ToString(); + + pTxDB->Insert(pTxDB->db, (char*)(address.c_str()), (char*)(tx.GetHash().ToString().c_str())); + + CAddressData data(blockIndex, 0, i, output.txType, output.nValue * 1, address); + CTxaddressData txdata(address, 0, output.nValue * 1); + switch (output.txType) + { + case 2: + case 3: + data.hash = output.ipcLabel.hash; + break; + case 4: + //Keep the symbol in token + data.strsymbol = output.tokenRegLabel.getTokenSymbol(); + data.tokenvalue = output.tokenRegLabel.totalCount; + break; + case 5: + //Keep the symbol in token + data.strsymbol = output.tokenLabel.getTokenSymbol(); + data.tokenvalue = output.tokenLabel.value; + break; + default: + break; + } + if ((output.txType == 2 || output.txType == 3)) + { + pTxDB->Insert(pTxDB->db, (char*)(data.hash.GetHex().c_str()), (char*)(txhash.GetHex().c_str())); + } + if ((output.txType == 4 || output.txType == 5)) + { + pTxDB->Insert(pTxDB->db, (char*)(data.strsymbol.c_str()), (char*)(txhash.GetHex().c_str())); + } + Datavectors.push_back(txdata); + } + } + } + + pTxDB->Insert(pTxDB->db, (char*)(txhash.GetHex().c_str()), Datavectors); + +} +bool getAddressBalanceByTxlevel(std::string& address, CAmount& balance, CAmount& received, CAmount& sended, uint64_t& txidnum) +{ + AssertLockHeld(cs_main); + AssertLockHeld(mempool.cs); + std::vector txids; + txids.clear(); + CAmount nvalue = 0; + std::string strtxid = ""; + if (!pTxDB->Select(pTxDB->db,(char*)(address.c_str()),txids,0)) //Find out + { + return false; + } + txidnum = txids.size(); + std::vector txdatas; + for (int i = 0; i < txidnum; i++) + { + txdatas.clear(); + strtxid = txids[i]; + pTxDB->Select(pTxDB->db, (char*)(strtxid.c_str()), txdatas); + for (int j = 0; j < txdatas.size();j++) + { + std::string stradd = txdatas[j].address; + nvalue = txdatas[j].amount; + if (address != stradd) + continue; + if (txdatas[j].ntype == 0 && nvalue >= 0) + { + received += nvalue; //Output sum + } + else if (txdatas[j].ntype == 1 && nvalue <= 0) + { + sended -= nvalue; //Input sum + } + + balance += nvalue; //Full balance + } + + } + + return true; +} + + +uint64_t getTokenAllcoins(uint8_t accuracy) +{ + uint64_t nallTokencoins = 0; + nallTokencoins = TOKEN_MAX_COINS; + for (unsigned int i = 0; i < accuracy; i++) + nallTokencoins = nallTokencoins * 10; + return nallTokencoins; +} + +CAmount Getfeebytxid(const CTransaction tx) +{ + CAmount nValue = 0; + if (tx.IsCoinBase()) + { + return nValue; + } + CTransactionRef prevTx; + + uint256 prehashBlock; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + if (!GetTransaction(tx.vin[i].prevout.hash, prevTx, Params().GetConsensus(), prehashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + nValue += prevTx->vout[tx.vin[i].prevout.n].nValue; + } + BOOST_FOREACH(const CTxOut& txout, tx.vout) { + nValue -= txout.nValue; + } + + return nValue; +} + +//The temporary unique variable that will be saved is pressed into the global pool after the block checksum is completed +void UpdateIPCUniqueMap() +{ + std::map>::iterator tokenSymbolIteator; + std::map>::iterator tokenHashIteator; + std::map>::iterator ipcHashIteator; + + tokenSymbolIteator = tmpIPCUniqueCheckMap.TokenSymbolMap.begin(); + while (tokenSymbolIteator != tmpIPCUniqueCheckMap.TokenSymbolMap.end()) + { + pIPCCheckMaps->TokenSymbolMap.insert(std::make_pair(tokenSymbolIteator->first, tokenSymbolIteator->second)); + tokenSymbolIteator++; + } + + tokenHashIteator = tmpIPCUniqueCheckMap.TokenHashMap.begin(); + while (tokenHashIteator != tmpIPCUniqueCheckMap.TokenHashMap.end()) + { + tokenHashIteator++; + } + + ipcHashIteator = tmpIPCUniqueCheckMap.IPCHashMap.begin(); + while (ipcHashIteator != tmpIPCUniqueCheckMap.IPCHashMap.end()) + { + ipcHashIteator++; + } + +} + +enum FlushStateMode { + FLUSH_STATE_NONE, + FLUSH_STATE_IF_NEEDED, + FLUSH_STATE_PERIODIC, + FLUSH_STATE_ALWAYS +}; + +// See definition for documentation +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0); +void FindFilesToPruneManual(std::set& setFilesToPrune, int nManualPruneHeight); + +bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) +{ + if (tx.nLockTime == 0) + return true; + ////Modify: remove the original Final limit, and we now check for the pre-transaction nLocktime instead of nLocitime for the current transaction + return true; +} + +bool CheckFinalTx(const CTransaction &tx, int flags) +{ + AssertLockHeld(cs_main); + + // By convention a negative value for flags indicates that the + // current network-enforced consensus rules should be used. In + // a future soft-fork scenario that would mean checking which + // rules would be enforced for the next block and setting the + // appropriate flags. At the present time no soft-forks are + // scheduled, so no flags are set. + flags = std::max(flags, 0); + + // CheckFinalTx() uses chainActive.Height()+1 to evaluate + // nLockTime because when IsFinalTx() is called within + // CBlock::AcceptBlock(), the height of the block *being* + // evaluated is what is used. Thus if we want to know if a + // transaction can be part of the *next* block, we need to call + // IsFinalTx() with one more than chainActive.Height(). + const int nBlockHeight = chainActive.Height() + 1; + + // BIP113 will require that time-locked transactions have nLockTime set to + // less than the median time of the previous block they're contained in. + // When the next block is created its previous block will be the current + // chain tip, so we use that to calculate the median time passed to + // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set. + const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) + ? chainActive.Tip()->GetMedianTimePast() + : GetAdjustedTime(); + + return IsFinalTx(tx, nBlockHeight, nBlockTime); +} + +bool IsFinalPrevTx(const CTransactionRef prevtx, int nBlockHeight) +{ + if (prevtx->nLockTime == 0) + { + return true; + } + + else if (prevtx->nLockTime > nBlockHeight) + { + return false; + } + else + { + return true; + } +} + +bool CheckIPCFinalTx(const CTransaction &tx) +{ + AssertLockHeld(cs_main); + + const int nBlockHeight = chainActive.Height() + 1; + CTransactionRef prevTx; + uint256 hashBlock; + + if (tx.IsCoinBase()) + return true; + BOOST_FOREACH(const CTxIn& txin, tx.vin){ + if (!GetTransaction(txin.prevout.hash, prevTx, Params().GetConsensus(), hashBlock, true)) + { + if (!GetCachedChainTransaction(txin.prevout.hash, prevTx)) + return false; + } + + if (!IsFinalPrevTx(prevTx, nBlockHeight)) + { + return false; + } + + } + + return true; +} + +/** + * Calculates the block height and previous block's median time past at + * which the transaction will be considered final in the context of BIP 68. + * Also removes from the vector of input heights any entries which did not + * correspond to sequence locked inputs as they do not affect the calculation. + */ +static std::pair CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeights, const CBlockIndex& block) +{ + assert(prevHeights->size() == tx.vin.size()); + + // Will be set to the equivalent height- and time-based nLockTime + // values that would be necessary to satisfy all relative lock- + // time constraints given our view of block chain history. + // The semantics of nLockTime are the last invalid height/time, so + // use -1 to have the effect of any height or time being valid. + int nMinHeight = -1; + int64_t nMinTime = -1; + + // tx.nVersion is signed integer so requires cast to unsigned otherwise + // we would be doing a signed comparison and half the range of nVersion + // wouldn't support BIP 68. + bool fEnforceBIP68 = static_cast(tx.nVersion) >= 2 + && flags & LOCKTIME_VERIFY_SEQUENCE; + + // Do not enforce sequence numbers as a relative lock time + // unless we have been instructed to + if (!fEnforceBIP68) { + return std::make_pair(nMinHeight, nMinTime); + } + + for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { + const CTxIn& txin = tx.vin[txinIndex]; + + // Sequence numbers with the most significant bit set are not + // treated as relative lock-times, nor are they given any + // consensus-enforced meaning at this point. + if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) { + // The height of this input is not relevant for sequence locks + (*prevHeights)[txinIndex] = 0; + continue; + } + + int nCoinHeight = (*prevHeights)[txinIndex]; + + if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) { + int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast(); + // NOTE: Subtract 1 to maintain nLockTime semantics + // BIP 68 relative lock times have the semantics of calculating + // the first block or time at which the transaction would be + // valid. When calculating the effective block time or height + // for the entire transaction, we switch to using the + // semantics of nLockTime which is the last invalid block + // time or height. Thus we subtract 1 from the calculated + // time or height. + + // Time-based relative lock-times are measured from the + // smallest allowed timestamp of the block containing the + // txout being spent, which is the median time past of the + // block prior. + nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1); + } else { + nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1); + } + } + + return std::make_pair(nMinHeight, nMinTime); +} + +static bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair lockPair) +{ + assert(block.pprev); + + return true; +} + +bool SequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeights, const CBlockIndex& block) +{ + return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block)); +} + +bool TestLockPointValidity(const LockPoints* lp) +{ + AssertLockHeld(cs_main); + assert(lp); + // If there are relative lock times then the maxInputBlock will be set + // If there are no relative lock times, the LockPoints don't depend on the chain + if (lp->maxInputBlock) { + // Check whether chainActive is an extension of the block at which the LockPoints + // calculation was valid. If not LockPoints are no longer valid + if (!chainActive.Contains(lp->maxInputBlock)) { + return false; + } + } + + // LockPoints still valid + return true; +} + +bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool useExistingLockPoints) +{ + AssertLockHeld(cs_main); + AssertLockHeld(mempool.cs); + + CBlockIndex* tip = chainActive.Tip(); + CBlockIndex index; + index.pprev = tip; + // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate + // height based locks because when SequenceLocks() is called within + // ConnectBlock(), the height of the block *being* + // evaluated is what is used. + // Thus if we want to know if a transaction can be part of the + // *next* block, we need to use one more than chainActive.Height() + index.nHeight = tip->nHeight + 1; + + std::pair lockPair; + if (useExistingLockPoints) { + assert(lp); + lockPair.first = lp->height; + lockPair.second = lp->time; + } + else { + // pcoinsTip contains the UTXO set for chainActive.Tip() + CCoinsViewMemPool viewMemPool(pcoinsTip, mempool); + std::vector prevheights; + prevheights.resize(tx.vin.size()); + for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { + const CTxIn& txin = tx.vin[txinIndex]; + CCoins coins; + if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) { + return error("%s: Missing input", __func__); + } + if (coins.nHeight == MEMPOOL_HEIGHT) { + // Assume all mempool transaction confirm in the next block + prevheights[txinIndex] = tip->nHeight + 1; + } else { + prevheights[txinIndex] = coins.nHeight; + } + } + lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index); + if (lp) { + lp->height = lockPair.first; + lp->time = lockPair.second; + // Also store the hash of the block with the highest height of + // all the blocks which have sequence locked prevouts. + // This hash needs to still be on the chain + // for these LockPoint calculations to be valid + // Note: It is impossible to correctly calculate a maxInputBlock + // if any of the sequence locked inputs depend on unconfirmed txs, + // except in the special case where the relative lock time/height + // is 0, which is equivalent to no sequence lock. Since we assume + // input height of tip+1 for mempool txs and test the resulting + // lockPair from CalculateSequenceLocks against tip+1. We know + // EvaluateSequenceLocks will fail if there was a non-zero sequence + // lock on a mempool input, so we can use the return value of + // CheckSequenceLocks to indicate the LockPoints validity + int maxInputHeight = 0; + BOOST_FOREACH(int height, prevheights) { + // Can ignore mempool inputs since we'll fail if they had non-zero locks + if (height != tip->nHeight+1) { + maxInputHeight = std::max(maxInputHeight, height); + } + } + lp->maxInputBlock = tip->GetAncestor(maxInputHeight); + } + } + return EvaluateSequenceLocks(index, lockPair); +} + + +unsigned int GetLegacySigOpCount(const CTransaction& tx) +{ + unsigned int nSigOps = 0; + for (const auto& txin : tx.vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + for (const auto& txout : tx.vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; +} + +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs) +{ + if (tx.IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); + } + return nSigOps; +} + +int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags) +{ + int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR; + + if (tx.IsCoinBase()) + return nSigOps; + + if (flags & SCRIPT_VERIFY_P2SH) { + nSigOps += GetP2SHSigOpCount(tx, inputs) * WITNESS_SCALE_FACTOR; + } + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); + nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, &tx.vin[i].scriptWitness, flags); + } + return nSigOps; +} + + +bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs) +{ + // Basic checks that don't depend on any context + if (tx.vin.empty()) + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); + if (tx.vout.empty()) + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); + // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); + + // Check for negative or overflow output values + CAmount nValueOut = 0; + for (const auto& txout : tx.vout) + { + if (txout.nValue < 0) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); + if (txout.nValue > MAX_MONEY) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); + } + + // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock + if (fCheckDuplicateInputs) { + std::set vInOutPoints; + for (const auto& txin : tx.vin) + { + if (!vInOutPoints.insert(txin.prevout).second) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); + } + } + + if (tx.IsCoinBase()) + { + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) + return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); + } + else + { + for (const auto& txin : tx.vin) + if (txin.prevout.IsNull()) + return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); + } + + + return true; +} + +void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { + int expired = pool.Expire(GetTime() - age); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + std::vector vNoSpendsRemaining; + pool.TrimToSize(limit, &vNoSpendsRemaining); + BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) + pcoinsTip->Uncache(removed); +} + +/** Convert CValidationState to a human-readable message for logging */ +std::string FormatStateMessage(const CValidationState &state) +{ + return strprintf("%s%s (code %i)", + state.GetRejectReason(), + state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(), + state.GetRejectCode()); +} + +static bool IsCurrentForFeeEstimation() +{ + AssertLockHeld(cs_main); + if (IsInitialBlockDownload()) + return false; + if (chainActive.Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE)) + return false; + if (chainActive.Height() < pindexBestHeader->nHeight - 1) + return false; + return true; +} + +bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, + bool* pfMissingInputs, int64_t nAcceptTime, std::list* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector& vHashTxnToUncache) +{ + const CTransaction& tx = *ptx; + const uint256 hash = tx.GetHash(); + AssertLockHeld(cs_main); + if (pfMissingInputs) + *pfMissingInputs = false; + + if (!CheckTransaction(tx, state)) + return false; // state filled in by CheckTransaction + + // Coinbase is only valid in a block, not as a loose transaction + if (tx.IsCoinBase()) + { + return state.DoS(100, false, REJECT_INVALID, "coinbase"); + } + + // Reject transactions with witness before segregated witness activates (override with -prematurewitness) + bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()); + if (!GetBoolArg("-prematurewitness",false) && tx.HasWitness() && !witnessEnabled) { + return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); + } + + // Rather not work on nonstandard transactions (unless -testnet/-regtest) + std::string reason; + if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled)) + return state.DoS(0, false, REJECT_NONSTANDARD, reason); + + // Only accept nLockTime-using transactions that can be mined in the next + // block; we don't want our mempool filled up with transactions that can't + // be mined yet. + if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); + + + if (!CheckIPCFinalTx(tx)) + { + //return state.DoS(100, false, REJECT_INVALID, "txin-is-not-final"); + return false; + } + + + // check IPC validations + if (!AreIPCStandard(tx, state)) + return false; + + // is it already in the memory pool? + if (pool.exists(hash)) + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); + + //If this transaction is a campaign transaction (join or exit), then check that there is a campaign transaction that has the same PK Hash in the pool. If so, refuse + if (tx.GetTxType() == 1) + { + DPOC_errtype errortype; + if (!CConsensusAccountPool::Instance().verifyDPOCTx(tx, errortype)) { + switch (errortype) + { + case EXIT_PUBKEY_NOT_EXIST_IN_LIST: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-EXIT_PUBKEY_NOT_EXIST_IN_LIST"); + break; + + case EXIT_UNKNOWN_PUBKEY: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-EXIT_UNKNOWN_PUBKEY"); + break; + + case JOIN_PUBKEY_IS_DEPOSING: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-JOIN_PUBKEY_IS_DEPOSING"); + break; + + case JOIN_PUBKEY_IS_TIMEOUT_PUNISHED: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-JOIN_PUBKEY_IS_TIMEOUT_PUNISHED"); + break; + + case GET_CACHED_SNAPSHOT_FAILED: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-GET_CACHED_SNAPSHOT_FAILED"); + break; + + case EXIT_PUBKEY_IS_DEPOSING: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-EXIT_PUBKEY_IS_DEPOSING"); + break; + + case EXIT_PUBKEY_IS_TIMEOUT_PUNISHED: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-EXIT_PUBKEY_IS_TIMEOUT_PUNISHED"); + break; + + case JOIN_PUBKEY_ALREADY_EXIST_IN_LIST: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-JOIN_PUBKEY_ALREADY_EXIST_IN_LIST"); + break; + + case JOIN_TRANS_DEPOSI_TOO_SMALL: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-JOIN_TRANS_DEPOSI_TOO_SMALL"); + break; + + case GET_LAST_SNAPSHOT_FAILED: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-GET_LAST_SNAPSHOT_FAILED"); + break; + + case JOIN_PUBKEY_IS_BANNED: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-JOIN_PUBKEY_IS_BANNED"); + break; + + default: + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-campaign-chaincheck-err"); + break; + } + } + + //Enumerate all transactions in the pool + for (CTxMemPool::txiter mi = pool.mapTx.begin(); mi != pool.mapTx.end(); mi++) + { + if (mi->GetTx().GetTxType() == TXOUT_CAMPAIGN && mi->GetTx().GetCampaignType() != TYPE_CONSENSUS_SEVERE_PUNISHMENT_REQUEST) + { + //Compare the PK Hash values for the campaign + if (tx.GetCampaignPublickey() == mi->GetTx().GetCampaignPublickey()) + { + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-conflict-campaign-pubkey"); + } + } + } + } + + // Check for conflicts with in-memory transactions + std::set setConflicts; + { + LOCK(pool.cs); // protect pool.mapNextTx + BOOST_FOREACH(const CTxIn &txin, tx.vin) + { + auto itConflicting = pool.mapNextTx.find(txin.prevout); + if (itConflicting != pool.mapNextTx.end()) + { + const CTransaction *ptxConflicting = itConflicting->second; + if (!setConflicts.count(ptxConflicting->GetHash())) + { + // Allow opt-out of transaction replacement by setting + // nSequence >= maxint-1 on all inputs. + // + // maxint-1 is picked to still allow use of nLockTime by + // non-replaceable transactions. All inputs rather than just one + // is for the sake of multi-party protocols, where we don't + // want a single party to be able to disable replacement. + // + // The opt-out ignores descendants as anyone relying on + // first-seen mempool behavior should be checking all + // unconfirmed ancestors anyway; doing otherwise is hopelessly + // insecure. + bool fReplacementOptOut = true; + if (fEnableReplacement) + { + BOOST_FOREACH(const CTxIn &_txin, ptxConflicting->vin) + { + if (_txin.nSequence < std::numeric_limits::max() - 1) + { + fReplacementOptOut = false; + break; + } + } + } + if (fReplacementOptOut) + return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); + + setConflicts.insert(ptxConflicting->GetHash()); + } + } + } + } + + { + CCoinsView dummy; + CCoinsViewCache view(&dummy); + + CAmount nValueIn = 0; + LockPoints lp; + { + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(pcoinsTip, pool); + view.SetBackend(viewMemPool); + + // do we already have it? + bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(hash); + if (view.HaveCoins(hash)) { + if (!fHadTxInCache) + vHashTxnToUncache.push_back(hash); + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known"); + } + + // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // and only helps with filling in pfMissingInputs (to determine missing vs spent). + BOOST_FOREACH(const CTxIn txin, tx.vin) { + if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash)) + vHashTxnToUncache.push_back(txin.prevout.hash); + if (!view.HaveCoins(txin.prevout.hash)) { + if (pfMissingInputs) + *pfMissingInputs = true; + return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() + } + } + + // are the actual inputs available? + if (!view.HaveInputs(tx)) + return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent"); + + // Bring the best block into scope + view.GetBestBlock(); + + nValueIn = view.GetValueIn(tx); + + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); + + // Only accept BIP68 sequence locked transactions that can be mined in the next + // block; we don't want our mempool filled up with transactions that can't + // be mined yet. + // Must keep pool.cs for this unless we change CheckSequenceLocks to take a + // CoinsViewCache instead of create its own + if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) + return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); + } + + // Check for non-standard pay-to-script-hash in inputs + if (fRequireStandard && !AreInputsStandard(tx, view)) + return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); + + // Check for non-standard witness in P2WSH + if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view)) + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true); + + int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); + + CAmount nValueOut = tx.GetValueOut(); + CAmount nFees = nValueIn-nValueOut; + // nModifiedFees includes any fee deltas from PrioritiseTransaction + CAmount nModifiedFees = nFees; + double nPriorityDummy = 0; + pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees); + + CAmount inChainInputValue; + double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); + + // Keep track of transactions that spend a coinbase, which we re-scan + // during reorgs to ensure COINBASE_MATURITY is still met. + bool fSpendsCoinbase = false; + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + const CCoins *coins = view.AccessCoins(txin.prevout.hash); + if (coins->IsCoinBase()) { + fSpendsCoinbase = true; + break; + } + } + + CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, dPriority, chainActive.Height(), + inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp); + unsigned int nSize = entry.GetTxSize(); + + // Check that the transaction doesn't have an excessive number of + // sigops, making it impossible to mine. Since the coinbase transaction + // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than + // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than + // merely non-standard transaction. + if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, + strprintf("%d", nSigOpsCost)); + + CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); + if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); + } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nModifiedFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { + // Require that free transactions have sufficient priority to be mined in the next block. + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); + } + + // Continuously rate-limit free (really, very-low-fee) transactions + // This mitigates 'penny-flooding' -- sending thousands of free transactions just to + // be annoying or make others' transactions take longer to confirm. + if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) + { + static CCriticalSection csFreeLimiter; + static double dFreeCount; + static int64_t nLastTime; + int64_t nNow = GetTime(); + + LOCK(csFreeLimiter); + + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + // There is no handling charge for applying for penalties, so here is a special case + if (dFreeCount + nSize >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000 && + tx.GetCampaignType() != TYPE_CONSENSUS_SEVERE_PUNISHMENT_REQUEST) + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; + } + + if (nAbsurdFee && nFees > nAbsurdFee) + return state.Invalid(false, + REJECT_HIGHFEE, "absurdly-high-fee", + strprintf("%d > %d", nFees, nAbsurdFee)); + + // Calculate in-mempool ancestors, up to a limit. + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; + std::string errString; + if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString); + } + + // A transaction that spends outputs that would be replaced by it is invalid. Now + // that we have the set of all ancestors we can detect this + // pathological case by making sure setConflicts and setAncestors don't + // intersect. + BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors) + { + const uint256 &hashAncestor = ancestorIt->GetTx().GetHash(); + if (setConflicts.count(hashAncestor)) + { + return state.DoS(10, false, + REJECT_INVALID, "bad-txns-spends-conflicting-tx", false, + strprintf("%s spends conflicting transaction %s", + hash.ToString(), + hashAncestor.ToString())); + } + } + + // Check if it's economically rational to mine this transaction rather + // than the ones it replaces. + CAmount nConflictingFees = 0; + size_t nConflictingSize = 0; + uint64_t nConflictingCount = 0; + CTxMemPool::setEntries allConflicting; + + // If we don't hold the lock allConflicting might be incomplete; the + // subsequent RemoveStaged() and addUnchecked() calls don't guarantee + // mempool consistency for us. + LOCK(pool.cs); + const bool fReplacementTransaction = setConflicts.size(); + if (fReplacementTransaction) + { + CFeeRate newFeeRate(nModifiedFees, nSize); + std::set setConflictsParents; + const int maxDescendantsToVisit = 100; + CTxMemPool::setEntries setIterConflicting; + BOOST_FOREACH(const uint256 &hashConflicting, setConflicts) + { + CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting); + if (mi == pool.mapTx.end()) + continue; + + // Save these to avoid repeated lookups + setIterConflicting.insert(mi); + + // Don't allow the replacement to reduce the feerate of the + // mempool. + // + // We usually don't want to accept replacements with lower + // feerates than what they replaced as that would lower the + // feerate of the next block. Requiring that the feerate always + // be increased is also an easy-to-reason about way to prevent + // DoS attacks via replacements. + // + // The mining code doesn't (currently) take children into + // account (CPFP) so we only consider the feerates of + // transactions being directly replaced, not their indirect + // descendants. While that does mean high feerate children are + // ignored when deciding whether or not to replace, we do + // require the replacement to pay more overall fees too, + // mitigating most cases. + CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize()); + if (newFeeRate <= oldFeeRate) + { + return state.DoS(0, false, + REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("rejecting replacement %s; new feerate %s <= old feerate %s", + hash.ToString(), + newFeeRate.ToString(), + oldFeeRate.ToString())); + } + + BOOST_FOREACH(const CTxIn &txin, mi->GetTx().vin) + { + setConflictsParents.insert(txin.prevout.hash); + } + + nConflictingCount += mi->GetCountWithDescendants(); + } + // This potentially overestimates the number of actual descendants + // but we just want to be conservative to avoid doing too much + // work. + if (nConflictingCount <= maxDescendantsToVisit) { + // If not too many to replace, then calculate the set of + // transactions that would have to be evicted + BOOST_FOREACH(CTxMemPool::txiter it, setIterConflicting) { + pool.CalculateDescendants(it, allConflicting); + } + BOOST_FOREACH(CTxMemPool::txiter it, allConflicting) { + nConflictingFees += it->GetModifiedFee(); + nConflictingSize += it->GetTxSize(); + } + } else { + return state.DoS(0, false, + REJECT_NONSTANDARD, "too many potential replacements", false, + strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n", + hash.ToString(), + nConflictingCount, + maxDescendantsToVisit)); + } + + for (unsigned int j = 0; j < tx.vin.size(); j++) + { + // We don't want to accept replacements that require low + // feerate junk to be mined first. Ideally we'd keep track of + // the ancestor feerates and make the decision based on that, + // but for now requiring all new inputs to be confirmed works. + if (!setConflictsParents.count(tx.vin[j].prevout.hash)) + { + // Rather than check the UTXO set - potentially expensive - + // it's cheaper to just check if the new input refers to a + // tx that's in the mempool. + if (pool.mapTx.find(tx.vin[j].prevout.hash) != pool.mapTx.end()) + return state.DoS(0, false, + REJECT_NONSTANDARD, "replacement-adds-unconfirmed", false, + strprintf("replacement %s adds unconfirmed input, idx %d", + hash.ToString(), j)); + } + } + + // The replacement must pay greater fees than the transactions it + // replaces - if we did the bandwidth used by those conflicting + // transactions would not be paid for. + if (nModifiedFees < nConflictingFees) + { + return state.DoS(0, false, + REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s", + hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees))); + } + + // Finally in addition to paying more fees than the conflicts the + // new transaction must pay for its own bandwidth. + CAmount nDeltaFees = nModifiedFees - nConflictingFees; + if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize)) + { + return state.DoS(0, false, + REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", + hash.ToString(), + FormatMoney(nDeltaFees), + FormatMoney(::incrementalRelayFee.GetFee(nSize)))); + } + } + + unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; + if (!Params().RequireStandard()) { + scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags); + } + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + PrecomputedTransactionData txdata(tx); + if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, txdata)) { + // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we + // need to turn both off, and compare against just turning off CLEANSTACK + // to see if the failure is specifically due to witness validation. + CValidationState stateDummy; // Want reported failures to be from first CheckInputs + if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && + !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { + // Only the witness is missing, so the transaction itself may be fine. + state.SetCorruptionPossible(); + } + return false; // state filled in by CheckInputs + } + + // Check again against just the consensus-critical mandatory script + // verification flags, in case of bugs in the standard flags that cause + // transactions to pass as valid when they're actually invalid. For + // instance the STRICTENC flag was incorrectly allowing certain + // CHECKSIG NOT scripts to pass, even though they were invalid. + // + // There is a similar check in CreateNewBlock() to prevent creating + // invalid blocks, however allowing such transactions into the mempool + // can be exploited as a DoS attack. + if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata)) + { + return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); + } + + // Remove conflicting transactions from the mempool + BOOST_FOREACH(const CTxMemPool::txiter it, allConflicting) + { + LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", + it->GetTx().GetHash().ToString(), + hash.ToString(), + FormatMoney(nModifiedFees - nConflictingFees), + (int)nSize - (int)nConflictingSize); + if (plTxnReplaced) + plTxnReplaced->push_back(it->GetSharedTx()); + } + pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); + + // This transaction should only count for fee estimation if it isn't a + // BIP 125 replacement transaction (may not be widely supported), the + // node is not behind, and the transaction is not dependent on any other + // transactions in the mempool. + bool validForFeeEstimation = !fReplacementTransaction && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); + + // Store transaction in memory + pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); + + // trim mempool and check if tx was trimmed + if (!fOverrideMempoolLimit) { + LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + if (!pool.exists(hash)) + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); + } + + //Now that the transaction has been added to the pool, the IPC related unique field needs to be added to the set + PushTxToIPCValuesToMap(tx, pIPCCheckMaps); + PushTxToTokenDataMap(tx,&tokenDataMap); + + if (fAddressIndex) { + AddTx2MapbyAddress(tx); + } + } + + + GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); + + return true; +} + +bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, + bool* pfMissingInputs, int64_t nAcceptTime, std::list* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount nAbsurdFee) +{ + std::vector vHashTxToUncache; + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); + if (!res) { + BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) + pcoinsTip->Uncache(hashTx); + } + // After we've (potentially) uncached entries, ensure our coins cache is still within its size limits + CValidationState stateDummy; + FlushStateToDisk(stateDummy, FLUSH_STATE_PERIODIC); + return res; +} + +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, + bool* pfMissingInputs, std::list* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount nAbsurdFee) +{ + return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee); +} + +/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ +bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) +{ + CBlockIndex *pindexSlow = NULL; + + LOCK(cs_main); + + + CTransactionRef ptx = mempool.get(hash); + if (ptx) + { + txOut = ptx; + return true; + } + + + if (fTxIndex) { + + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + hashBlock = header.GetHash(); + if (txOut->GetHash() != hash) + return error("%s: txid mismatch", __func__); + return true; + } + } + + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + const CCoinsViewCache& view = *pcoinsTip; + const CCoins* coins = view.AccessCoins(hash); + if (coins) + nHeight = coins->nHeight; + } + if (nHeight > 0) + pindexSlow = chainActive[nHeight]; + } + + if (pindexSlow) { + CBlock block; + if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) { + for (const auto& tx : block.vtx) { + if (tx->GetHash() == hash) { + txOut = tx; + hashBlock = pindexSlow->GetBlockHash(); + return true; + } + } + } + } + + return false; +} +std::vector cachedChainTx; +bool GetCachedChainTransaction(const uint256 &hash, CTransactionRef &tx) +{ + BOOST_FOREACH(const auto& ptx, cachedChainTx){ + if (ptx->GetHash() == hash) + { + tx = ptx; + + return true; + } + } + return false; +} + +// +// CBlock and CBlockIndex +// + +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) +{ + // Open history file to append + CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("WriteBlockToDisk: OpenBlockFile failed"); + + // Write index header + unsigned int nSize = GetSerializeSize(fileout, block); + fileout << FLATDATA(messageStart) << nSize; + + // Write block + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("WriteBlockToDisk: ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << block; + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) +{ + block.SetNull(); + + // Open history file to read + CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); + + // Read block + try { + filein >> block; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); + } + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams)) + return false; + if (block.GetHash() != pindex->GetBlockHash()) + return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", + pindex->ToString(), pindex->GetBlockPos().ToString()); + return true; +} + +CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) +{ + CAmount nSubsidy = PAYOFMINING * COIN; + if (1 == nHeight) + nSubsidy = (IPC_ISSUE_VALUE-9600000) * COIN; + else if (nHeight <= 2100000) + nSubsidy = 2 * COIN; + else if (nHeight <= 4200000) + nSubsidy = 1.7 * COIN; + else if (nHeight <= 6300000) + nSubsidy = 1.4 * COIN; + else if (nHeight <= 8400000) + nSubsidy = 1.1 * COIN; + else if (nHeight <= 10500000) + nSubsidy = 0.8 * COIN; + return nSubsidy; +} + +bool IsInitialBlockDownload() +{ + const CChainParams& chainParams = Params(); + + // Once this function has returned false, it must remain false. + static std::atomic latchToFalse{false}; + // Optimization: pre-test latch before taking the lock. + if (latchToFalse.load(std::memory_order_relaxed)) + return false; + + LOCK(cs_main); + if (latchToFalse.load(std::memory_order_relaxed)) + return false; + if (fImporting || fReindex) + return true; + if (chainActive.Tip() == NULL) + return true; + if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) + return true; + latchToFalse.store(true, std::memory_order_relaxed); + return false; +} + +CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; + +static void AlertNotify(const std::string& strMessage) +{ + uiInterface.NotifyAlertChanged(); + std::string strCmd = GetArg("-alertnotify", ""); + if (strCmd.empty()) return; + + // Alert text should be plain ascii coming from a trusted source, but to + // be safe we first strip anything not in safeChars, then add single quotes around + // the whole string before passing it to the shell: + std::string singleQuote("'"); + std::string safeStatus = SanitizeString(strMessage); + safeStatus = singleQuote+safeStatus+singleQuote; + boost::replace_all(strCmd, "%s", safeStatus); + + boost::thread t(runCommand, strCmd); // thread runs free +} + +void CheckForkWarningConditions() +{ + AssertLockHeld(cs_main); + // Before we get past initial download, we cannot reliably alert about forks + // (we assume we don't get stuck on a fork before finishing our initial sync) + if (IsInitialBlockDownload()) + return; + + // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) + // of our head, drop it + if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) + pindexBestForkTip = NULL; + + if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) + { + if (!GetfLargeWorkForkFound() && pindexBestForkBase) + { + std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + + pindexBestForkBase->phashBlock->ToString() + std::string("'"); + AlertNotify(warning); + } + if (pindexBestForkTip && pindexBestForkBase) + { + LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, + pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), + pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); + SetfLargeWorkForkFound(true); + } + else + { + LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); + SetfLargeWorkInvalidChainFound(true); + } + } + else + { + SetfLargeWorkForkFound(false); + SetfLargeWorkInvalidChainFound(false); + } +} + +void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) +{ + AssertLockHeld(cs_main); + // If we are on a fork that is sufficiently large, set a warning flag + CBlockIndex* pfork = pindexNewForkTip; + CBlockIndex* plonger = chainActive.Tip(); + while (pfork && pfork != plonger) + { + while (plonger && plonger->nHeight > pfork->nHeight) + plonger = plonger->pprev; + if (pfork == plonger) + break; + pfork = pfork->pprev; + } + + // We define a condition where we should warn the user about as a fork of at least 7 blocks + // with a tip within 72 blocks (+/- 12 hours if no one mines it) of ours + // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network + // hash rate operating on the fork. + // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) + // We define it this way because it allows us to only store the highest fork tip (+ base) which meets + // the 7-block condition and from this always have the most-likely-to-cause-warning fork + if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && + pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && + chainActive.Height() - pindexNewForkTip->nHeight < 72) + { + pindexBestForkTip = pindexNewForkTip; + pindexBestForkBase = pfork; + } + + CheckForkWarningConditions(); +} + +void static InvalidChainFound(CBlockIndex* pindexNew) +{ + if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) + pindexBestInvalid = pindexNew; + + LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, + pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, + log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", + pindexNew->GetBlockTime())); + CBlockIndex *tip = chainActive.Tip(); + assert (tip); + LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, + tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); + CheckForkWarningConditions(); +} + +void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { + if (!state.CorruptionPossible()) { + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + setBlockIndexCandidates.erase(pindex); + InvalidChainFound(pindex); + } +} + +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight) +{ + // mark inputs spent + if (!tx.IsCoinBase()) { + txundo.vprevout.reserve(tx.vin.size()); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); + unsigned nPos = txin.prevout.n; + + if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull()) + assert(false); + // mark an outpoint spent, and construct undo information + txundo.vprevout.push_back(CTxInUndo(coins->vout[nPos])); + coins->Spend(nPos); + if (coins->vout.size() == 0) { + CTxInUndo& undo = txundo.vprevout.back(); + undo.nHeight = coins->nHeight; + undo.fCoinBase = coins->fCoinBase; + undo.nVersion = coins->nVersion; + } + } + } + // add outputs + inputs.ModifyNewCoins(tx.GetHash(), tx.IsCoinBase())->FromTx(tx, nHeight); +} + +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) +{ + CTxUndo txundo; + UpdateCoins(tx, inputs, txundo, nHeight); +} + +bool CScriptCheck::operator()() { + const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; + const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; + if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) { + return false; + } + return true; +} + +int GetSpendHeight(const CCoinsViewCache& inputs) +{ + LOCK(cs_main); + CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + return pindexPrev->nHeight + 1; +} + +namespace Consensus { +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) +{ + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. + if (!inputs.HaveInputs(tx)) + return state.Invalid(false, 0, "", "Inputs unavailable"); + + CAmount nValueIn = 0; + CAmount nFees = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins *coins = inputs.AccessCoins(prevout.hash); + assert(coins); + + // If prev is coinbase, check that it's matured + if (coins->IsCoinBase()) { + if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) + return state.Invalid(false, + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight)); + } + + // Check for negative or overflow input values + nValueIn += coins->vout[prevout.n].nValue; + if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + + } + + if (nValueIn < tx.GetValueOut()) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); + + // Tally transaction fees + CAmount nTxFee = nValueIn - tx.GetValueOut(); + if (nTxFee < 0) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); + nFees += nTxFee; + if (!MoneyRange(nFees)) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); + return true; +} +}// namespace Consensus + +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector *pvChecks) +{ + if (!tx.IsCoinBase()) + { + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) + return false; + + if (pvChecks) + pvChecks->reserve(tx.vin.size()); + + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion attacks. + + // Skip script verification when connecting blocks under the + // assumedvalid block. Assuming the assumedvalid block is valid this + // is safe because block merkle hashes are still computed and checked, + // Of course, if an assumed valid block is invalid due to false scriptSigs + // this optimization would allow an invalid chain to be accepted. + if (fScriptChecks) { + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins* coins = inputs.AccessCoins(prevout.hash); + assert(coins); + + // Verify signature + CScriptCheck check(*coins, tx, i, flags, cacheStore, &txdata); + if (pvChecks) { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } else if (!check()) { + if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { + // Check whether the failure was caused by a + // non-mandatory script verification check, such as + // non-standard DER encodings or non-null dummy + // arguments; if so, don't trigger DoS protection to + // avoid splitting the network between upgraded and + // non-upgraded nodes. + CScriptCheck check2(*coins, tx, i, + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata); + if (check2()) + return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); + } + // Failures of other flags indicate a transaction that is + // invalid in new blocks, e.g. a invalid P2SH. We DoS ban + // such nodes as they are not following the protocol. That + // said during an upgrade careful thought should be taken + // as to the correct behavior - we may want to continue + // peering with non-upgraded nodes even after soft-fork + // super-majority signaling has occurred. + return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); + } + } + } + } + + return true; +} + +namespace { + +bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) +{ + // Open history file to append + CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: OpenUndoFile failed", __func__); + + // Write index header + unsigned int nSize = GetSerializeSize(fileout, blockundo); + fileout << FLATDATA(messageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("%s: ftell failed", __func__); + pos.nPos = (unsigned int)fileOutPos; + fileout << blockundo; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + fileout << hasher.GetHash(); + + return true; +} + +bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) +{ + // Open history file to read + CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: OpenUndoFile failed", __func__); + + // Read block + uint256 hashChecksum; + try { + filein >> blockundo; + filein >> hashChecksum; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + if (hashChecksum != hasher.GetHash()) + return error("%s: Checksum mismatch", __func__); + + return true; +} + +/** Abort with a message */ +bool AbortNode(const std::string& strMessage, const std::string& userMessage="") +{ + SetMiscWarning(strMessage); + uiInterface.ThreadSafeMessageBox( + userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; +} + +bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") +{ + AbortNode(strMessage, userMessage); + return state.Error(strMessage); +} + +} // anon namespace + +/** + * Apply the undo operation of a CTxInUndo to the given chain state. + * @param undo The undo object. + * @param view The coins view to which to apply the changes. + * @param out The out point that corresponds to the tx input. + * @return True on success. + */ +bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out) +{ + bool fClean = true; + + CCoinsModifier coins = view.ModifyCoins(out.hash); + if (undo.nHeight != 0) { + // undo data contains height: this is the last output of the prevout tx being spent + if (!coins->IsPruned()) + fClean = fClean && error("%s: undo data overwriting existing transaction", __func__); + coins->Clear(); + coins->fCoinBase = undo.fCoinBase; + coins->nHeight = undo.nHeight; + coins->nVersion = undo.nVersion; + } else { + if (coins->IsPruned()) + fClean = fClean && error("%s: undo data adding output to missing transaction", __func__); + } + if (coins->IsAvailable(out.n)) + fClean = fClean && error("%s: undo data overwriting existing output", __func__); + if (coins->vout.size() < out.n+1) + coins->vout.resize(out.n+1); + coins->vout[out.n] = undo.txout; + + return fClean; +} + +bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) +{ + assert(pindex->GetBlockHash() == view.GetBestBlock()); + + if (pfClean) + *pfClean = false; + + bool fClean = true; + + CBlockUndo blockUndo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) + return error("DisconnectBlock(): no undo data available"); + if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) + return error("DisconnectBlock(): failure reading undo data"); + + if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) + return error("DisconnectBlock(): block and undo data inconsistent"); + + // undo transactions in reverse order + for (int i = block.vtx.size() - 1; i >= 0; i--) { + const CTransaction &tx = *(block.vtx[i]); + uint256 hash = tx.GetHash(); + + // Check that all outputs are available and match the outputs in the block itself + // exactly. + { + CCoinsModifier outs = view.ModifyCoins(hash); + outs->ClearUnspendable(); + + CCoins outsBlock(tx, pindex->nHeight); + // The CCoins serialization does not serialize negative numbers. + // No network rules currently depend on the version here, so an inconsistency is harmless + // but it must be corrected before txout nversion ever influences a network rule. + if (outsBlock.nVersion < 0) + outs->nVersion = outsBlock.nVersion; + if (*outs != outsBlock) + fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); + + // remove outputs + outs->Clear(); + } + + // restore inputs + if (i > 0) { // not coinbases + const CTxUndo &txundo = blockUndo.vtxundo[i-1]; + if (txundo.vprevout.size() != tx.vin.size()) + return error("DisconnectBlock(): transaction and undo data inconsistent"); + for (unsigned int j = tx.vin.size(); j-- > 0;) { + const COutPoint &out = tx.vin[j].prevout; + const CTxInUndo &undo = txundo.vprevout[j]; + if (!ApplyTxInUndo(undo, view, out)) + fClean = false; + } + } + } + + // move best block pointer to prevout block + view.SetBestBlock(pindex->pprev->GetBlockHash()); + + if (pfClean) { + *pfClean = fClean; + return true; + } + + return fClean; +} + +void static FlushBlockFile(bool fFinalize = false) +{ + LOCK(cs_LastBlockFile); + + CDiskBlockPos posOld(nLastBlockFile, 0); + + FILE *fileOld = OpenBlockFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); + FileCommit(fileOld); + fclose(fileOld); + } + + fileOld = OpenUndoFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); + FileCommit(fileOld); + fclose(fileOld); + } +} + +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); + +static CCheckQueue scriptcheckqueue(128); + +void ThreadScriptCheck() { + RenameThread("ipchain-scriptch"); + scriptcheckqueue.Thread(); +} + +// Protected by cs_main +VersionBitsCache versionbitscache; + +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + int32_t nVersion = VERSIONBITS_TOP_BITS; + + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache); + if (state == THRESHOLD_LOCKED_IN || state == THRESHOLD_STARTED) { + nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i); + } + } + + return nVersion; +} + +/** + * Threshold condition checker that triggers when unknown versionbits are seen on the network. + */ +class WarningBitsConditionChecker : public AbstractThresholdConditionChecker +{ +private: + int bit; + +public: + WarningBitsConditionChecker(int bitIn) : bit(bitIn) {} + + int64_t BeginTime(const Consensus::Params& params) const { return 0; } + int64_t EndTime(const Consensus::Params& params) const { return std::numeric_limits::max(); } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && + ((pindex->nVersion >> bit) & 1) != 0 && + ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; + } +}; + +// Protected by cs_main +static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; + +static int64_t nTimeCheck = 0; +static int64_t nTimeForks = 0; +static int64_t nTimeVerify = 0; +static int64_t nTimeConnect = 0; +static int64_t nTimeIndex = 0; +static int64_t nTimeCallbacks = 0; +static int64_t nTimeTotal = 0; + +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, + CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck) +{ + AssertLockHeld(cs_main); + + std::cout << "begin to connect block: " << pindex->nHeight << std::endl; + + int64_t nTimeStart = GetTimeMicros(); + + // Check it again in case a previous version let a bad block in + if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) + return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); + + // verify that the view's current state corresponds to the previous block + uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); + assert(hashPrevBlock == view.GetBestBlock()); + + std::shared_ptr shared_pblock = std::make_shared(block); + + // Special case for the genesis block, skipping connection of its transactions + // (its coinbase is unspendable) + if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) { + if (!fJustCheck) + { + view.SetBestBlock(pindex->GetBlockHash()); + LogPrintf("[ConnectBlock] GenesisBlock connected, SetBestBlock and pushDPOCBlock\n"); + CConsensusAccountPool::Instance().pushDPOCBlock(shared_pblock, pindex->nHeight); + } + return true; + } + + // Check the block hash of the first block + +// if (pindex->nHeight == 1) +// { +// if ((Params().NetworkIDString() == CBaseChainParams::TESTNET)) +// { +// if (NULL == pindex->phashBlock) +// { +// LogPrintf(" [connectblock error] phashBlock = NULL! \n"); +// return false; +// } +// +// if ( pindex->GetBlockHash().GetHex() != "014c41d9c6448c2d6a065a6788498c9fe639a33b121bf4ab43ca84b9696a2d53") +// { +// LogPrintf(" Check the first block hash error %s \n", pindex->GetBlockHash().GetHex()); +// return state.DoS(100, error("ConnectBlock(): failed connect to Height 1"), REJECT_INVALID, "bad-block-hash"); +// } +// } +// else +// { +// if (NULL == pindex->phashBlock) +// { +// LogPrintf(" [connectblock error] phashBlock = NULL! \n"); +// return false; +// } +// +// if (pindex->GetBlockHash().GetHex() != "47d1788fa6497f868f78ddf579b2e7d8aca60f554d425ead9533af02ed35136d") +// { +// LogPrintf(" Check the first block hash error %s \n", pindex->GetBlockHash().GetHex()); +// return state.DoS(100, error("ConnectBlock(): failed connect to Height 1"), REJECT_INVALID, "bad-block-hash"); +// } +// +// } +// +// } + + + //end + bool fScriptChecks = true; + if (!hashAssumeValid.IsNull()) { + // We've been configured with the hash of a block which has been externally verified to have a valid history. + // A suitable default value is included with the software and updated from time to time. Because validity + // relative to a piece of software is an objective fact these defaults can be easily reviewed. + // This setting doesn't force the selection of any particular chain but makes validating some faster by + // effectively caching the result of part of the verification. + BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); + if (it != mapBlockIndex.end()) { + if (it->second->GetAncestor(pindex->nHeight) == pindex && + pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && + pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + // This block is a member of the assumed verified chain and an ancestor of the best header. + // The equivalent time check discourages hashpower from extorting the network via DOS attack + // into accepting an invalid block through telling users they must manually set assumevalid. + // Requiring a software change or burying the invalid block, regardless of the setting, makes + // it hard to hide the implication of the demand. This also avoids having release candidates + // that are hardly doing any signature verification at all in testing without having to + // artificially set the default assumed verified block further back. + // The test against nMinimumChainWork prevents the skipping when denied access to any chain at + // least as good as the expected chain. + fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, chainparams.GetConsensus()) <= 60 * 60 * 24 * 7 * 2); + } + } + } + + int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart; + LogPrint("bench", " - Sanity checks: %.2fms [%.2fs]\n", 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001); + + // Do not allow blocks that contain transactions which 'overwrite' older transactions, + // unless those are already completely spent. + // If such overwrites are allowed, coinbases and transactions depending upon those + // can be duplicated to remove the ability to spend the first instance -- even after + // being sent to another address. + // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. + // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool + // already refuses previously-known transaction ids entirely. + // This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC. + // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the + // two in the chain that violate it. This prevents exploiting the issue against nodes during their + // initial block download. + +// bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. +// !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || +// (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); + + bool fEnforceBIP30 = (!pindex->phashBlock); + + + // Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting + // with the 2 existing duplicate coinbase pairs, not possible to create overwriting txs. But by the + // time BIP34 activated, in each of the existing pairs the duplicate coinbase had overwritten the first + // before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further + // duplicate transactions descending from the known pairs either. + // If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check. + + // CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height); //modified by xxy ע͵ + + //Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond. + + // fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash));//modified by xxy ע͵ + + + if (fEnforceBIP30) { + for (const auto& tx : block.vtx) { + const CCoins* coins = view.AccessCoins(tx->GetHash()); + if (coins && !coins->IsPruned()) + return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), + REJECT_INVALID, "bad-txns-BIP30"); + } + } + + unsigned int flags = SCRIPT_VERIFY_P2SH; + //end + + // Start enforcing the DERSIG (BIP66) rule + if (pindex->nHeight >= chainparams.GetConsensus().BIP66Height) { + flags |= SCRIPT_VERIFY_DERSIG; + } + + + int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; + LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); + + CBlockUndo blockundo; + + CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + + std::vector prevheights; + CAmount nFees = 0; + int nInputs = 0; + int64_t nSigOpsCost = 0; + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); + std::vector > vPos; + vPos.reserve(block.vtx.size()); + blockundo.vtxundo.reserve(block.vtx.size() - 1); + std::vector txdata; + txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated + + //Check the validity of DPOC blocks + DPOC_errtype errortype; + if (!CConsensusAccountPool::Instance().verifyDPOCBlock(shared_pblock, pindex->nHeight, errortype)) + { + if (errortype == BLOCK_TOO_NEW_FOR_SNAPSHOT) + { + g_bConsensus = true; + LogPrintf("[ConnectBlock] check dpoc block falied on block %s, reason=block-too-new\n", block.GetHash().GetHex().c_str()); + + return state.DoS(0, error("ConnectBlock(): CheckDPOCRule on block %s failed", block.GetHash().ToString()), + REJECT_INVALID, "bad-too-new"); + } + else if (errortype == PUNISH_BLOCK) + { + LogPrintf("[ConnectBlock] check dpoc block falied on block %s, reason=serious punish\n", block.GetHash().GetHex().c_str()); + + return state.DoS(100, error("ConnectBlock(): CheckDPOCRule on block %s failed", block.GetHash().ToString()), + REJECT_INVALID, "bad-dpoc-block-punished"); + } + else if (errortype == JOIN_TRANS_DEPOSI_TOO_SMALL) + { + LogPrintf("[ConnectBlock] check dpoc block falied on block %s, reason=PUBKEY_IS_BANNED\n", block.GetHash().GetHex().c_str()); + + return state.DoS(100, error("ConnectBlock(): CheckDPOCRule on block %s failed", block.GetHash().ToString()), + REJECT_INVALID, "bad-dpoc-block-punished"); + } + + g_bConsensus = true; + LogPrintf("[ConnectBlock] check dpoc block other falied on block %s, reason=other wrong\n", block.GetHash().GetHex().c_str()); + + return state.DoS(0, error("ConnectBlock(): CheckDPOCRule on block %s failed", block.GetHash().ToString()), + REJECT_INVALID, "bad-block-other-wrong"); + + } + + //Before the transaction detection starts, the cache list needs to be cleared + cachedChainTx.clear(); + + for (unsigned int i = 0; i < block.vtx.size(); i++) + { + const CTransaction &tx = *(block.vtx[i]); + + nInputs += tx.vin.size(); + + if (!tx.IsCoinBase()) + { + if (!view.HaveInputs(tx)) + { + return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), + REJECT_INVALID, "bad-txns-inputs-missingorspent"); + } + + } + + // GetTransactionSigOpCost counts 3 types of sigops: + // * legacy (always) + // * p2sh (when P2SH enabled in flags and excludes coinbase) + // * witness (when witness enabled in flags and excludes coinbase) + nSigOpsCost += GetTransactionSigOpCost(tx, view, flags); + if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST) + { + return state.DoS(100, error("ConnectBlock(): too many sigops"), + REJECT_INVALID, "bad-blk-sigops"); + } + + txdata.emplace_back(tx); + if (!tx.IsCoinBase()) + { + nFees += view.GetValueIn(tx)-tx.GetValueOut(); + + std::vector vChecks; + bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ + if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL)) + { + return error("ConnectBlock(): CheckInputs on %s failed with %s", + tx.GetHash().ToString(), FormatStateMessage(state)); + } + control.Add(vChecks); + } + + // check IPC validations + if (!AreIPCStandard(tx, state)) + { + std::cout << "ConnectBlock: " << FormatStateMessage(state) << std::endl; + return error("ConnectBlock(): CheckInputs on %s failed with %s", + tx.GetHash().ToString(), FormatStateMessage(state)); + } + + + CTxUndo undoDummy; + if (i > 0) { + blockundo.vtxundo.push_back(CTxUndo()); + } + UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); + + //The tx will be checked into the temporary tx list, otherwise the transaction cannot be found + + cachedChainTx.push_back(block.vtx[i]); + + vPos.push_back(std::make_pair(tx.GetHash(), pos)); + pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); + } + //All transactions are completed and the cache list needs to be cleared + cachedChainTx.clear(); + + int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; + LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001); + + CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); + if (block.vtx[0]->GetValueOut() > blockReward) + { + std::cout << "ConnectBlock(): Block Reward out of bound, bad-cb-amount" << std::endl; + return state.DoS(100,error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", + block.vtx[0]->GetValueOut(), blockReward), + REJECT_INVALID, "bad-cb-amount"); + } + + if (!control.Wait()) + return state.DoS(100, false); + int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2; + LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001); + + if (fJustCheck) + return true; + // Write undo information to disk + if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) + { + if (pindex->GetUndoPos().IsNull()) { + CDiskBlockPos _pos; + if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) + return error("ConnectBlock(): FindUndoPos failed"); + if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) + return AbortNode(state, "Failed to write undo data"); + + // update nUndoPos in block index + pindex->nUndoPos = _pos.nPos; + pindex->nStatus |= BLOCK_HAVE_UNDO; + } + + pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); + setDirtyBlockIndex.insert(pindex); + } + + if (fTxIndex) + if (!pblocktree->WriteTxIndex(vPos)) + return AbortNode(state, "Failed to write transaction index"); + + // add this block to the view's block chain + view.SetBestBlock(pindex->GetBlockHash()); + + int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4; + LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001); + + // Watch for changes to the previous coinbase transaction. + static uint256 hashPrevBestCoinBase; + GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); + hashPrevBestCoinBase = block.vtx[0]->GetHash(); + + + int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5; + LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001); + + //Add tx in the block to the map list required by the browser interface to maintain a list of Unique constraint values + for (const auto& tx : block.vtx) + { + if (fAddressIndex) + { + AddTx2MapbyAddress(*tx, pindex->nHeight); + } + + PushTxToIPCValuesToMap(*tx, pIPCCheckMaps); + PushTxToIPCValuesToMap(*tx,&newIPCCKMap); + PushTxToTokenDataMap(*tx, &tokenDataMap); + PushTxToTokenDataMap(*tx, &newTokenDataMap); + } + + if (pindex->nTime - pindex->pprev->nTime >20) //The adjacent block is greater than 20 seconds log file to write the height of the current block + LogPrintfQ(" nHeight = %d The interval between the block and the next block is %d seconds \n", pindex->pprev->nHeight, pindex->nTime - pindex->pprev->nTime); + + CConsensusAccountPool::Instance().pushDPOCBlock(shared_pblock, pindex->nHeight); + + return true; +} + +/** + * Update the on-disk chain state. + * The caches and indexes are flushed depending on the mode we're called with + * if they're too large, if it's been a while since the last write, + * or always and in all cases if we're in prune mode and are deleting files. + */ +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { + int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); + const CChainParams& chainparams = Params(); + LOCK2(cs_main, cs_LastBlockFile); + static int64_t nLastWrite = 0; + static int64_t nLastFlush = 0; + static int64_t nLastSetChain = 0; + std::set setFilesToPrune; + bool fFlushForPrune = false; + try { + if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { + if (nManualPruneHeight > 0) { + FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); + } else { + FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); + fCheckForPruning = false; + } + if (!setFilesToPrune.empty()) { + fFlushForPrune = true; + if (!fHavePruned) { + pblocktree->WriteFlag("prunedblockfiles", true); + fHavePruned = true; + } + } + } + int64_t nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) { + nLastWrite = nNow; + } + if (nLastFlush == 0) { + nLastFlush = nNow; + } + if (nLastSetChain == 0) { + nLastSetChain = nNow; + } + int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR; + int64_t nTotalSpace = nCoinCacheUsage + std::max(nMempoolSizeMax - nMempoolUsage, 0); + // The cache is large and we're within 10% and 200 MiB or 50% and 50MiB of the limit, but we have time now (not in the middle of a block processing). + bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::min(std::max(nTotalSpace / 2, nTotalSpace - MIN_BLOCK_COINSDB_USAGE * 1024 * 1024), + std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024)); + // The cache is over the limit, we have to write now. + bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace; + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. + bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + + bool fFullFlushByHeight = false; + { + LOCK(cs_main); + if (0 == (chainActive.Height() % (SNAPSHOTLENGTH / 2))) + { + fFullFlushByHeight = true; + } + + if (chainActive.Height() <= 0) + { + fFullFlushByHeight = false; + } + } + + // Combine all conditions that result in a full cache flush. + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune || fFullFlushByHeight; + + + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) { + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) + return state.Error("out of disk space"); + // First make sure all block and undo data is flushed to disk. + FlushBlockFile(); + // Then update all block file information (which may refer to block and undo files). + { + std::vector > vFiles; + vFiles.reserve(setDirtyFileInfo.size()); + for (std::set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); + setDirtyFileInfo.erase(it++); + } + std::vector vBlocks; + vBlocks.reserve(setDirtyBlockIndex.size()); + for (std::set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + vBlocks.push_back(*it); + setDirtyBlockIndex.erase(it++); + } + if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { + return AbortNode(state, "Failed to write to block index database"); + } + } + // Finally remove any pruned files + if (fFlushForPrune) + UnlinkPrunedFiles(setFilesToPrune); + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) { + // Typical CCoins structures on disk are around 128 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error("out of disk space"); + // Flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return AbortNode(state, "Failed to write to coin database"); + nLastFlush = nNow; + } + if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { + // Update best block in wallet (so we can detect restored wallets). + GetMainSignals().SetBestChain(chainActive.GetLocator()); + nLastSetChain = nNow; + } + } catch (const std::runtime_error& e) { + return AbortNode(state, std::string("System error while flushing: ") + e.what()); + } + return true; +} + +void FlushStateToDisk() { + CValidationState state; + FlushStateToDisk(state, FLUSH_STATE_ALWAYS); +} + +void PruneAndFlush() { + CValidationState state; + fCheckForPruning = true; + FlushStateToDisk(state, FLUSH_STATE_NONE); +} + +/** Update chainActive and related internal data structures. */ +void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { + chainActive.SetTip(pindexNew); + + // New best block + mempool.AddTransactionsUpdated(1); + + cvBlockChange.notify_all(); + + static bool fWarned = false; + std::vector warningMessages; + if (!IsInitialBlockDownload()) + { + int nUpgraded = 0; + const CBlockIndex* pindex = chainActive.Tip(); + for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { + WarningBitsConditionChecker checker(bit); + ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]); + if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) { + if (state == THRESHOLD_ACTIVE) { + std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); + SetMiscWarning(strWarning); + if (!fWarned) { + AlertNotify(strWarning); + fWarned = true; + } + } else { + warningMessages.push_back(strprintf("unknown new rules are about to activate (versionbit %i)", bit)); + } + } + } + // Check the version of the last 100 blocks to see if we need to upgrade: + for (int i = 0; i < 100 && pindex != NULL; i++) + { + int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); + if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) + ++nUpgraded; + pindex = pindex->pprev; + } + if (nUpgraded > 0) + warningMessages.push_back(strprintf("%d of last 100 blocks have unexpected version", nUpgraded)); + if (nUpgraded > 100/2) + { + std::string strWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); + // notify GetWarnings(), called by Qt and the JSON-RPC code to warn the user: + SetMiscWarning(strWarning); + if (!fWarned) { + AlertNotify(strWarning); + fWarned = true; + } + } + } + LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)\n", __func__, + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, + log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + + CConsensusAccountPool::Instance().popDPOCBlock(chainActive.Height()); + + if (!warningMessages.empty()) + LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); + LogPrintf("\n"); + +} + +/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */ +bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams, bool fBare = false) +{ + CBlockIndex *pindexDelete = chainActive.Tip(); + assert(pindexDelete); + // Read block from disk. + CBlock block; + if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus())) + return AbortNode(state, "Failed to read block"); + + //Roll back the totals + CConsensusAccountPool::Instance().setTotalAmount(block, false); + + // Apply the block atomically to the chain state. + int64_t nStart = GetTimeMicros(); + { + CCoinsViewCache view(pcoinsTip); + if (!DisconnectBlock(block, state, pindexDelete, view)) + return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); + bool flushed = view.Flush(); + assert(flushed); + } + LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + // Write the chain state to disk, if necessary. + if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) + return false; + + if (!fBare) { + // Resurrect mempool transactions from the disconnected block. + std::vector vHashUpdate; + for (const auto& it : block.vtx) { + const CTransaction& tx = *it; + // ignore validation errors in resurrected transactions + CValidationState stateDummy; + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, NULL, true)) { + mempool.removeRecursive(tx, MemPoolRemovalReason::REORG); + } else if (mempool.exists(tx.GetHash())) { + vHashUpdate.push_back(tx.GetHash()); + } + } + // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have + // no in-mempool children, which is generally not true when adding + // previously-confirmed transactions back to the mempool. + // UpdateTransactionsFromBlock finds descendants of any transactions in this + // block that were added back and cleans up the mempool state. + mempool.UpdateTransactionsFromBlock(vHashUpdate); + } + + // Update chainActive and related variables. + UpdateTip(pindexDelete->pprev, chainparams); + // Let wallets know transactions went from 1-confirmed to + // 0-confirmed or conflicted: + for (const auto& tx : block.vtx) { + GetMainSignals().SyncTransaction(*tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); + } + return true; +} + +static int64_t nTimeReadFromDisk = 0; +static int64_t nTimeConnectTotal = 0; +static int64_t nTimeFlush = 0; +static int64_t nTimeChainState = 0; +static int64_t nTimePostConnect = 0; + +/** + * Used to track blocks whose transactions were applied to the UTXO state as a + * part of a single ActivateBestChainStep call. + */ +struct ConnectTrace { + std::vector > > blocksConnected; +}; + +/** + * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock + * corresponding to pindexNew, to bypass loading it again from disk. + * + * The block is always added to connectTrace (either after loading from disk or by copying + * pblock) - if that is not intended, care must be taken to remove the last entry in + * blocksConnected in case of failure. + */ +bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr& pblock, ConnectTrace& connectTrace) +{ + assert(pindexNew->pprev == chainActive.Tip()); + // Read block from disk. + int64_t nTime1 = GetTimeMicros(); + if (!pblock) { + std::shared_ptr pblockNew = std::make_shared(); + connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew); + if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus())) + return AbortNode(state, "Failed to read block"); + } else { + connectTrace.blocksConnected.emplace_back(pindexNew, pblock); + } + const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second; + // Apply the block atomically to the chain state. + int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; + int64_t nTime3; + LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); + { + CCoinsViewCache view(pcoinsTip); + bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams); + GetMainSignals().BlockChecked(blockConnecting, state); + if (!rv) { + if (state.IsInvalid()) + InvalidBlockFound(pindexNew, state); + return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); + } + nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; + LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); + bool flushed = view.Flush(); + assert(flushed); + } + int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; + LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); + // Write the chain state to disk, if necessary. + if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) + return false; + //**************************************************************************** + CVerifyDB().FlushICMToDisk(); + //end*************************************************************************** + int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; + LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); + // Remove conflicting transactions from the mempool.; + mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); + // Update chainActive & related variables. + UpdateTip(pindexNew, chainparams); + + int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; + LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); + LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); + return true; +} + +//Returns the trusted node height on the current link +static int GetTrustNodeHeightOnChain() +{ + //Get a trusted list + std::vector vecTrustList; + CConsensusAccountPool::Instance().getTrustList(vecTrustList); + + CBlockIndex *pindexTest = chainActive.Tip(); + if (NULL == pindexTest) + { + LogPrintf("[GetTrustNodeHeightOnChain] end by false NULL == pindexTest\n"); + return -1; + } + + while (NULL != pindexTest->pprev) + { + CKeyID pubicKey160hash; + if (!CConsensusAccountPool::Instance().getSignPkByBlockIndex(pindexTest, pubicKey160hash)) + { + LogPrintf("[GetTrustNodeHeightOnChain] end by false getSignPkByBlockIndex return false\n"); + return -1; + } + + for (std::vector::iterator trustit = vecTrustList.begin(); + trustit != vecTrustList.end(); ++trustit) + { + uint160 uIN; + uIN.SetHex(*trustit); + + if (pubicKey160hash == uIN) + { + return pindexTest->nHeight; + } + } + + pindexTest = pindexTest->pprev; + } + + LogPrintf("[GetTrustNodeHeightOnChain] end by NoTrustNode \n"); + return -1; + +} + +static bool IsCreditBlock(const CBlockIndex *pindexTest, std::vector &vecTrustList) +{ + CKeyID pubicKey160hash; + if (!CConsensusAccountPool::Instance().getSignPkByBlockIndex(pindexTest, pubicKey160hash)) + { + LogPrintf("[IsCreditBlock] end by false getSignPkByBlockIndex return false\n"); + return false; + } + + for (std::vector::iterator trustit = vecTrustList.begin(); + trustit != vecTrustList.end(); ++trustit) + { + uint160 uIN; + uIN.SetHex(*trustit); + + if (pubicKey160hash == uIN) + { + LogPrintf("[IsCreditBlock] end by true\n"); + return true; + } + } + + LogPrintf("[IsCreditBlock] end by false\n"); + return false; +} + +/** + * Return the tip of the chain with the most work in it, that isn't + * known to be invalid (it's however far from certain to be valid). + */ +static CBlockIndex* FindMostWorkChain(CBlockIndex* pblockIndex, int nChainCreditHeight, std::vector &vecTrustList) { + + bool bErase = false; + + do { + bool bFindCreditFlag = false; + + CBlockIndex *pindexNew = NULL; + std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); + // Find the best candidate header. + { + if (it == setBlockIndexCandidates.rend()) + return NULL; + + if (bErase) + { + bErase = false; + pindexNew = *it; + } + else + { + while (it != setBlockIndexCandidates.rend()) { + + if((*it)->phashBlock->ToString() == pblockIndex->phashBlock->ToString()) + { + pindexNew = *it; + break; + } + ++it; + } + + if(it == setBlockIndexCandidates.rend()) + return NULL; + } + } + + // Check whether all blocks on the path between the currently active chain and the candidate are valid. + // Just going until the active chain is an optimization, as we know all blocks in it are valid already. + CBlockIndex *pindexTest = pindexNew; + bool fInvalidAncestor = false; + int nNum = 0; + while (pindexTest && !chainActive.Contains(pindexTest)) { + LogPrintf("[FindMostWorkChain] pindexTest %d,%d\n", pindexTest->nHeight, pindexTest->nTimePeriod); + assert(pindexTest->nChainTx || pindexTest->nHeight == 0); + + // Pruned nodes may have entries in setBlockIndexCandidates for + // which block files have been deleted. Remove those as candidates + // for the most work chain if we come across them; we can't switch + // to a chain unless we have all the non-active-chain parent blocks. + bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; + bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); + if (fFailedChain || fMissingData) { + // Candidate chain is not usable (either invalid or missing data) + if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) + pindexBestInvalid = pindexNew; + CBlockIndex *pindexFailed = pindexNew; + // Remove the entire chain from the set. + while (pindexTest != pindexFailed) { + if (fFailedChain) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + } else if (fMissingData) { + // If we're missing data, then add back to mapBlocksUnlinked, + // so that if the block arrives in the future we can try adding + // to setBlockIndexCandidates again. + mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed)); + } + + setBlockIndexCandidates.erase(pindexFailed); + + pindexFailed = pindexFailed->pprev; + + bErase = true; + } + setBlockIndexCandidates.erase(pindexTest); + bErase = true; + fInvalidAncestor = true; + break; + } + + if (!bFindCreditFlag) + { + LogPrintf("[FindMostWorkChain] +++++++ %d,%d\n", pindexTest->nHeight, pindexTest->nTimePeriod); + //This block is a trusted node + if(IsCreditBlock(pindexTest, vecTrustList)) + { + LogPrintf("[FindMostWorkChain] IsCreditBlock %d,%d\n",pindexTest->nHeight, nChainCreditHeight); + //The height of the trusted node is greater than the trusted node height on the chain + if (pindexTest->nHeight > nChainCreditHeight) + { + LogPrintf("[FindMostWorkChain] bFindCreditFlag = true\n"); + bFindCreditFlag = true; + } + } + } + + pindexTest = pindexTest->pprev; + + ++nNum; + }//while + + /*if (!fInvalidAncestor) + return pindexNew;*/ + + if (!fInvalidAncestor) + { + ++it; + LogPrintf("[FindMostWorkChain] nNum = %d \n", nNum); + //Find the trusted node and return the longest chain in the set; + //The trusted node is not found, but set goes on to the highest point of chainActive, returning the longest chain in the set + //Otherwise returns the highest point of the current chainActive + if (0 == pblockIndex->nHeight) + { + LogPrintf("[FindMostWorkChain] return GenesisBlock\n"); + return pindexNew; + } + else if (bFindCreditFlag) + { + LogPrintf("[FindMostWorkChain] return pindexNew %d\n", bFindCreditFlag); + return pindexNew; + } + else if (((chainActive.Tip()->nHeight + 1) == pindexNew->nHeight)&&( nNum == 1)) + { + LogPrintf("[FindMostWorkChain] return pindexNew nNum == 1\n"); + return pindexNew; + } + else if (nNum > 100 && !bFindCreditFlag) + { + LogPrintf("[FindMostWorkChain] return nNum > 100 && !bFindCreditFlag %d,%d\n", pindexNew->nHeight,pindexNew->nTimePeriod); + + pindexNew->nStatus |= BLOCK_FAILED_MASK; + CBlockIndex *pindexInvild = pindexNew; + if (pindexInvild != NULL) + { + setBlockIndexCandidates.erase(pindexInvild); + pindexInvild = pindexInvild->pprev; + } + + while (pindexInvild && !chainActive.Contains(pindexInvild)) { + + pindexInvild->nStatus |= BLOCK_FAILED_CHILD; + setBlockIndexCandidates.erase(pindexInvild); + pindexInvild = pindexInvild->pprev; + } + + std::set::reverse_iterator itFind = setBlockIndexCandidates.rbegin(); + if (itFind == setBlockIndexCandidates.rend()) + return NULL; + + return FindMostWorkChain(*itFind, nChainCreditHeight, vecTrustList); + } + else if (it == setBlockIndexCandidates.rend()) + { + LogPrintf("[FindMostWorkChain] return NULL it == setBlockIndexCandidates.rend()\n"); + return NULL; + } + else + { + LogPrintf("[FindMostWorkChain] return next \n"); + + return FindMostWorkChain(*it, nChainCreditHeight, vecTrustList); + } + } + } while(true); +} + +/** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */ +static void PruneBlockIndexCandidates() { + // Note that we can't delete the current block itself, as we may need to return to it later in case a + // reorganization to a better block fails. + std::set::iterator it = setBlockIndexCandidates.begin(); + while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { + setBlockIndexCandidates.erase(it++); + } + // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates. + assert(!setBlockIndexCandidates.empty()); +} +static bool CheckCredibleExist(const CBlockIndex *pindexFork, CBlockIndex* pindexMostWork, const CChainParams& chainparams) +{ + std::vector vpindexToConnect; + int nHeight = pindexFork ? pindexFork->nHeight : -1; + + int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); + vpindexToConnect.clear(); + vpindexToConnect.reserve(nTargetHeight - nHeight); + CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + while (pindexIter && pindexIter->nHeight != nHeight) { + vpindexToConnect.push_back(pindexIter); + pindexIter = pindexIter->pprev; + } + nHeight = nTargetHeight; + LogPrintf("[CheckCredibleExist]nHeight=%d, pindexMostWork->nHeight=%d,nTargetHeight=%d,vpindexToConnect=%d\n", nHeight, pindexMostWork->nHeight, nTargetHeight, vpindexToConnect.size()); + + //Get a trusted list + std::vector vecTrustList; + CConsensusAccountPool::Instance().getTrustList(vecTrustList); + + BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) + { + std::shared_ptr pblockNew = std::make_shared(); + if (!ReadBlockFromDisk(*pblockNew, pindexConnect, chainparams.GetConsensus())) + { + LogPrintf("[CheckCredibleExist] end by false ReadBlockFromDisk return false\n"); + return false; + } + + //Gets the public key and the signature string in the block + CPubKey recvPublickey; + std::vector recvSign; + if (!CConsensusAccountPool::Instance().getPublicKeyFromBlock(pblockNew.get(), recvPublickey, recvSign)) + { + LogPrintf("[CheckCredibleExist] end by false getPublicKeyFromBlock return false\n"); + return false; + } + CKeyID pubicKey160hash = recvPublickey.GetID(); + + for (std::vector::iterator trustit = vecTrustList.begin(); + trustit != vecTrustList.end(); trustit++) + { + uint160 uIN; + uIN.SetHex(*trustit); + + if (pubicKey160hash == uIN) + { + LogPrintf("[CheckCredibleExist] end by true has TrustNode \n"); + return true; + } + } + } + + LogPrintf("[CheckCredibleExist] end by false NoTrustNode \n"); + return false; +} + +/** + * Try to make some progress towards making pindexMostWork the active block. + * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. + */ +static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) +{ + //std::cout << "ActivateBestChainStep(): start!" << std::endl; + LogPrintf("[ActivateBestChainStep] start\n"); + AssertLockHeld(cs_main); + const CBlockIndex *pindexOldTip = chainActive.Tip(); + const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); + + // Disconnect active blocks which are no longer in the best chain. + bool fBlocksDisconnected = false; + while (chainActive.Tip() && chainActive.Tip() != pindexFork) + { + if (!DisconnectTip(state, chainparams)) + return false; + fBlocksDisconnected = true; + } + + // Build list of new blocks to connect. + std::vector vpindexToConnect; + bool fContinue = true; + int nHeight = pindexFork ? pindexFork->nHeight : -1; + while (fContinue && nHeight != pindexMostWork->nHeight) { + // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need + // a few blocks along the way. + int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); + vpindexToConnect.clear(); + vpindexToConnect.reserve(nTargetHeight - nHeight); + CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + while (pindexIter && pindexIter->nHeight != nHeight) { + vpindexToConnect.push_back(pindexIter); + pindexIter = pindexIter->pprev; + } + + nHeight = nTargetHeight; + LogPrintf("[ActivateBestChainStep]nHeight=%d, pindexMostWork->nHeight=%d,nTargetHeight=%d,vpindexToConnect=%d\n",nHeight, pindexMostWork->nHeight, nTargetHeight, vpindexToConnect.size()); + // Connect new blocks. + //std::cout << "ActivateBestChainStep(): Connect new blocks" << std::endl; + BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { + LogPrintf("[ActivateBestChainStep]BOOST_REVERSE_FOREACH\n"); + if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr(), connectTrace)) + { + if (state.IsInvalid()) { + // The block violates a consensus rule. + if (!state.CorruptionPossible()) + InvalidChainFound(vpindexToConnect.back()); + state = CValidationState(); + fInvalidFound = true; + fContinue = false; + // If we didn't actually connect the block, don't notify listeners about it + connectTrace.blocksConnected.pop_back(); + break; + } + else + { + // A system error occurred (disk space, database error, ...). + return false; + } + + } + else + { + PruneBlockIndexCandidates(); + if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { + // We're in a better position than we were. Return temporarily to release the lock. + fContinue = false; + break; + } + } + } + } + + if (fBlocksDisconnected) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + + //Restart the meeting + LogPrintf("[ActivateBestChainStep] Restart Meeting begin\n"); + if (!fInvalidFound) + { + CDpocMining::Instance().reStart(); + } + LogPrintf("[ActivateBestChainStep] Restart Meeting end\n"); + } + mempool.check(pcoinsTip); + + // Callbacks/notifications for a new best chain. + if (fInvalidFound) + CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); + else + CheckForkWarningConditions(); + + return true; +} + +static void NotifyHeaderTip() { + bool fNotify = false; + bool fInitialBlockDownload = false; + static CBlockIndex* pindexHeaderOld = NULL; + CBlockIndex* pindexHeader = NULL; + { + LOCK(cs_main); + pindexHeader = pindexBestHeader; + + if (pindexHeader != pindexHeaderOld) { + fNotify = true; + fInitialBlockDownload = IsInitialBlockDownload(); + pindexHeaderOld = pindexHeader; + } + } + // Send block tip changed notifications without cs_main + if (fNotify) { + uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader); + } +} + +/** + * Make the best chain active, in multiple steps. The result is either failure + * or an activated best chain. pblock is either NULL or a pointer to a block + * that is already loaded (to avoid loading it again from disk). + */ +bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr pblock) { + // Note that while we're often called here from ProcessNewBlock, this is + // far from a guarantee. Things in the P2P/RPC will often end up calling + // us in the middle of ProcessNewBlock - do not assume pblock is set + // sanely for performance or correctness! + //std::cout << "ActivateBestChain(): start!" << std::endl; + LogPrintf("[ActivateBestChain] start\n"); + + //Get a trusted list + std::vector vecTrustList; + CConsensusAccountPool::Instance().getTrustList(vecTrustList); + + CBlockIndex *pindexMostWork = NULL; + CBlockIndex *pindexNewTip = NULL; + do { + boost::this_thread::interruption_point(); + if (ShutdownRequested()) + break; + + const CBlockIndex *pindexFork; + ConnectTrace connectTrace; + bool fInitialDownload; + { + LOCK(cs_main); + { // TODO: Tempoarily ensure that mempool removals are notified before + // connected transactions. This shouldn't matter, but the abandoned + // state of transactions in our wallet is currently cleared when we + // receive another notification and there is a race condition where + // notification of a connected conflict might cause an outside process + // to abandon a transaction and then have it inadvertantly cleared by + // the notification that the conflicted transaction was evicted. + MemPoolConflictRemovalTracker mrt(mempool); + CBlockIndex *pindexOldTip = chainActive.Tip(); + if (pindexMostWork == NULL) { + std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); + if (it == setBlockIndexCandidates.rend()) + { + pindexMostWork = NULL; + } + else + { + //Gets the highest height of the trusted node on the chain + int nChainCreditHeight = GetTrustNodeHeightOnChain(); + pindexMostWork = FindMostWorkChain(*it, nChainCreditHeight, vecTrustList); + } + } + + // Whether we have anything to do at all. + if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) + { + LogPrintf("[ActivateBestChain] pindexMostWork == NULL || pindexMostWork == chainActive.Tip() \n"); + return true; + } + + bool fInvalidFound = false; + std::shared_ptr nullBlockPtr; + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) + return false; + + if (fInvalidFound) { + // Wipe cache, we may need another branch now. + pindexMostWork = NULL; + } + pindexNewTip = chainActive.Tip(); + pindexFork = chainActive.FindFork(pindexOldTip); + fInitialDownload = IsInitialBlockDownload(); + + // throw all transactions though the signal-interface + + } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified + + // Transactions in the connnected block are notified + for (const auto& pair : connectTrace.blocksConnected) { + assert(pair.second); + const CBlock& block = *(pair.second); + for (unsigned int i = 0; i < block.vtx.size(); i++) + GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i); + } + } + // When we reach this point, we switched to a new tip (stored in pindexNewTip). + + // Notifications/callbacks that can run without cs_main + + // Notify external listeners about the new tip. + GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); + + // Always notify the UI if a new block tip was connected + if (pindexFork != pindexNewTip) { + uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + } + } while (pindexNewTip != pindexMostWork); + CheckBlockIndex(chainparams.GetConsensus()); + + // Write changes periodically to disk, after relay. + if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { + return false; + } + + return true; +} + + +bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) +{ + { + LOCK(cs_main); + if (pindex->nChainWork < chainActive.Tip()->nChainWork) { + // Nothing to do, this block is not at the tip. + return true; + } + if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) { + // The chain has been extended since the last call, reset the counter. + nBlockReverseSequenceId = -1; + } + nLastPreciousChainwork = chainActive.Tip()->nChainWork; + setBlockIndexCandidates.erase(pindex); + pindex->nSequenceId = nBlockReverseSequenceId; + if (nBlockReverseSequenceId > std::numeric_limits::min()) { + // We can't keep reducing the counter if somebody really wants to + // call preciousblock 2**31-1 times on the same set of tips... + nBlockReverseSequenceId--; + } + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) { + setBlockIndexCandidates.insert(pindex); + PruneBlockIndexCandidates(); + } + } + + return ActivateBestChain(state, params); +} + +bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) +{ + AssertLockHeld(cs_main); + + // Mark the block itself as invalid. + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + setBlockIndexCandidates.erase(pindex); + + while (chainActive.Contains(pindex)) { + CBlockIndex *pindexWalk = chainActive.Tip(); + pindexWalk->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(pindexWalk); + setBlockIndexCandidates.erase(pindexWalk); + // ActivateBestChain considers blocks already in chainActive + // unconditionally valid already, so force disconnect away from it. + if (!DisconnectTip(state, chainparams)) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + return false; + } + } + + LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + + // The resulting new best tip may not be in setBlockIndexCandidates anymore, so + // add it again. + BlockMap::iterator it = mapBlockIndex.begin(); + while (it != mapBlockIndex.end()) { + if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) { + setBlockIndexCandidates.insert(it->second); + } + it++; + } + + InvalidChainFound(pindex); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev); + return true; +} + +bool ResetBlockFailureFlags(CBlockIndex *pindex) { + AssertLockHeld(cs_main); + + int nHeight = pindex->nHeight; + + // Remove the invalidity flag from this block and all its descendants. + BlockMap::iterator it = mapBlockIndex.begin(); + while (it != mapBlockIndex.end()) { + if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) { + it->second->nStatus &= ~BLOCK_FAILED_MASK; + setDirtyBlockIndex.insert(it->second); + if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) { + setBlockIndexCandidates.insert(it->second); + } + if (it->second == pindexBestInvalid) { + // Reset invalid block marker if it was pointing to one of those. + pindexBestInvalid = NULL; + } + } + it++; + } + + // Remove the invalidity flag from all ancestors too. + while (pindex != NULL) { + if (pindex->nStatus & BLOCK_FAILED_MASK) { + pindex->nStatus &= ~BLOCK_FAILED_MASK; + setDirtyBlockIndex.insert(pindex); + } + pindex = pindex->pprev; + } + return true; +} + +CBlockIndex* AddToBlockIndex(const CBlockHeader& block) +{ + // Check for duplicate + uint256 hash = block.GetHash(); + BlockMap::iterator it = mapBlockIndex.find(hash); + if (it != mapBlockIndex.end()) + return it->second; + + // Construct new block index object + CBlockIndex* pindexNew = new CBlockIndex(block); + assert(pindexNew); + // We assign the sequence id to blocks only when the full data is available, + // to avoid miners withholding blocks but broadcasting headers, to get a + // competitive advantage. + pindexNew->nSequenceId = 0; + BlockMap::iterator mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); + if (miPrev != mapBlockIndex.end()) + { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + pindexNew->BuildSkip(); + } + pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime); + pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); + pindexNew->RaiseValidity(BLOCK_VALID_TREE); + if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) + pindexBestHeader = pindexNew; + + setDirtyBlockIndex.insert(pindexNew); + + return pindexNew; +} + +/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */ +bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos) +{ + pindexNew->nTx = block.vtx.size(); + pindexNew->nChainTx = 0; + pindexNew->nFile = pos.nFile; + pindexNew->nDataPos = pos.nPos; + pindexNew->nUndoPos = 0; + pindexNew->nStatus |= BLOCK_HAVE_DATA; + if (IsWitnessEnabled(pindexNew->pprev, Params().GetConsensus())) { + pindexNew->nStatus |= BLOCK_OPT_WITNESS; + } + pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); + setDirtyBlockIndex.insert(pindexNew); + + if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { + // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. + std::deque queue; + queue.push_back(pindexNew); + + // Recursively process any descendant blocks that now may be eligible to be connected. + while (!queue.empty()) { + CBlockIndex *pindex = queue.front(); + queue.pop_front(); + pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + { + LOCK(cs_nBlockSequenceId); + pindex->nSequenceId = nBlockSequenceId++; + } + + if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { + setBlockIndexCandidates.insert(pindex); + LogPrintf("[ReceivedBlockTransactions] setBlockIndexCandidates.insert=%d, %d\n", pindex->nHeight, pindex->nTimePeriod); + } + + + std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex); + while (range.first != range.second) { + std::multimap::iterator it = range.first; + queue.push_back(it->second); + range.first++; + mapBlocksUnlinked.erase(it); + } + } + } else { + if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) { + + mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); + } + } + + return true; +} + +bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) +{ + LOCK(cs_LastBlockFile); + + unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; + if (vinfoBlockFile.size() <= nFile) { + vinfoBlockFile.resize(nFile + 1); + } + + if (!fKnown) { + while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + nFile++; + if (vinfoBlockFile.size() <= nFile) { + vinfoBlockFile.resize(nFile + 1); + } + } + pos.nFile = nFile; + pos.nPos = vinfoBlockFile[nFile].nSize; + } + + if ((int)nFile != nLastBlockFile) { + if (!fKnown) { + LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString()); + } + FlushBlockFile(!fKnown); + nLastBlockFile = nFile; + } + + vinfoBlockFile[nFile].AddBlock(nHeight, nTime); + if (fKnown) + vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); + else + vinfoBlockFile[nFile].nSize += nAddSize; + + if (!fKnown) { + unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + if (fPruneMode) + fCheckForPruning = true; + if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenBlockFile(pos); + if (file) { + LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } + } + else + return state.Error("out of disk space"); + } + } + + setDirtyFileInfo.insert(nFile); + return true; +} + +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) +{ + pos.nFile = nFile; + + LOCK(cs_LastBlockFile); + + unsigned int nNewSize; + pos.nPos = vinfoBlockFile[nFile].nUndoSize; + nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; + setDirtyFileInfo.insert(nFile); + + unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + if (fPruneMode) + fCheckForPruning = true; + if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenUndoFile(pos); + if (file) { + LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } + } + else + return state.Error("out of disk space"); + } + + return true; +} +static bool CheckBlockHeaderAdd(const CBlockHeader& block) +{ + //The number of blocks should be less than the total number of blocks + if ((block.nTimePeriod >= block.nPeriodCount)||(block.nTimePeriod < 0)) + { + LogPrintf("[CheckBlockHeaderAdd] end false by block TimePeriod failed \n"); + return false; + } + //The actual block time of this block should be greater than the amount of time calculated in the block + if (block.nTime < (block.nPeriodStartTime + (block.nTimePeriod+1)*BLOCK_GEN_TIME)/1000) + { + LogPrintf("[CheckBlockHeaderAdd] end false by block meeting failed \n"); + return false; + } + + //The purpose of the surrealistic attack is to prevent the subsequent people from hitting a block + int64_t n64LocalTime = timeService.GetCurrentTimeSeconds(); + if (n64LocalTime < (block.nTime - 2)) + { + LogPrintf("[CheckBlockHeaderAdd] end by false Surreal attack n64LocalTime=%d,block.nTime=%d\n", n64LocalTime, block.nTime); + return false; + } + + LogPrintf("[CheckBlockHeaderAdd] end true \n"); + + return true; +} + +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW) +{ + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) + return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); + + LOCK(cs_main); + if ((chainActive.Height() + 1) >= Params().CHECK_START_BLOCKCOUNT) + { + if (!CheckBlockHeaderAdd(block)) + { + return state.DoS(0, false, REJECT_INVALID, "blockheader-bad", false, "block meeting failed"); + } + } + return true; +} + +bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot) +{ + // These are checks that are independent of context. + + if (block.fChecked) + return true; + + // Check that the header is valid (particularly PoW). This is mostly + // redundant with the call in AcceptBlockHeader. + if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW)) + { + return false; + } + + // Check the merkle root. + if (fCheckMerkleRoot) { + bool mutated; + uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); + if (block.hashMerkleRoot != hashMerkleRoot2) + { + return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch"); + } + + // Check for merkle tree malleability (CVE-2012-2459): repeating sequences + // of transactions in a block without affecting the merkle root of a block, + // while still invalidating it. + if (mutated) + { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction"); + } + + } + + // All potential-corruption validation must be done before we do any + // transaction validation, as otherwise we may mark the header as invalid + // because we receive the wrong transactions for it. + // Note that witness malleability is checked in ContextualCheckBlock, so no + // checks that use witness data may be performed here. + + // Size limits + if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_BASE_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE) + { + return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); + } + + + // First transaction must be coinbase, the rest must not be + if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) + return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase"); + for (unsigned int i = 1; i < block.vtx.size(); i++) + if (block.vtx[i]->IsCoinBase()) + return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase"); + + // Check transactions + tmpIPCUniqueCheckMap.clear(); + for (const auto& tx : block.vtx) + if (!CheckTransaction(*tx, state, false)) + { + std::cout << "CheckBlock: CheckTransaction falied, " << state.GetRejectReason() << std::endl; + return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), + strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage())); + } + else + PushTxToIPCValuesToMap(*tx, &tmpIPCUniqueCheckMap); + + unsigned int nSigOps = 0; + for (const auto& tx : block.vtx) + { + nSigOps += GetLegacySigOpCount(*tx); + } + if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST) + { + std::cout <<"nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST"<< std::endl; + return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); + } + + + if (fCheckPOW && fCheckMerkleRoot) + block.fChecked = true; + + //When a block is completed, the IPC Unique value saved in the inspection process should be placed in the maintenance pool + UpdateIPCUniqueMap(); + + int nHeight = -1; + { + LOCK(cs_main); + nHeight = chainActive.Height(); + } + + if ((nHeight + 1) >= Params().CHECK_START_BLOCKCOUNT) + { + if (!CConsensusAccountPool::Instance().verifyBlockSign(&block)) + { + LogPrintf("[CheckBlock] end false state100 by block sign is bad\n"); + return state.DoS(100, false, REJECT_INVALID, "bad-sign-faile", false, "block sign is bad"); + } + + uint256 u256blockhashchain; + //Verify the signature of the block + { + LOCK(cs_main); + u256blockhashchain = chainActive.Tip()->GetBlockHash(); + } + //Defensive attack + if (block.hashPrevBlock == u256blockhashchain) + { + if (!CConsensusAccountPool::Instance().checkPackagerInCurrentList(block)) + { + LogPrintf("[CheckBlock] end false checkPackagerInCurrentList return false\n"); + return state.DoS(0, false, REJECT_INVALID, "bad-getList-faile", false, "cannot find consensusList"); + } + } + } + + return true; +} + +static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidationState& state, const CChainParams& chainparams, const uint256& hash) +{ + if (*pindexPrev->phashBlock == chainparams.GetConsensus().hashGenesisBlock) + return true; + + int nHeight = pindexPrev->nHeight+1; + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); + + return true; +} + +bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + return false; +} + +// Compute at which vout of the block's coinbase transaction the witness +// commitment occurs, or -1 if not found. +static int GetWitnessCommitmentIndex(const CBlock& block) +{ + int commitpos = -1; + if (!block.vtx.empty()) { + for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) { + if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) { + commitpos = o; + } + } + } + return commitpos; +} + +void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + int commitpos = GetWitnessCommitmentIndex(block); + static const std::vector nonce(32, 0x00); + if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) { + CMutableTransaction tx(*block.vtx[0]); + tx.vin[0].scriptWitness.stack.resize(1); + tx.vin[0].scriptWitness.stack[0] = nonce; + block.vtx[0] = MakeTransactionRef(std::move(tx)); + } +} + +std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + std::vector commitment; + int commitpos = GetWitnessCommitmentIndex(block); + std::vector ret(32, 0x00); + if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) { + if (commitpos == -1) { + uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); + CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); + CTxOut out; + out.nValue = 0; + out.scriptPubKey.resize(38); + out.scriptPubKey[0] = OP_RETURN; + out.scriptPubKey[1] = 0x24; + out.scriptPubKey[2] = 0xaa; + out.scriptPubKey[3] = 0x21; + out.scriptPubKey[4] = 0xa9; + out.scriptPubKey[5] = 0xed; + memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32); + commitment = std::vector(out.scriptPubKey.begin(), out.scriptPubKey.end()); + CMutableTransaction tx(*block.vtx[0]); + tx.vout.push_back(out); + block.vtx[0] = MakeTransactionRef(std::move(tx)); + } + } + UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams); + return commitment; +} + +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) +{ + const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + + // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: + // check for version 2, 3 and 4 upgrades + if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || + (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || + (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) + return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), + strprintf("rejected nVersion=0x%08x block", block.nVersion)); + + return true; +} + +bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) +{ + const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + + // Start enforcing BIP113 (Median Time Past) using versionbits logic. + int nLockTimeFlags = 0; + if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) { + nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST; + } + + int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) + ? pindexPrev->GetMedianTimePast() + : block.GetBlockTime(); + + // Check that all transactions are finalized + for (const auto& tx : block.vtx) { + if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) { + return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction"); + } + } + + // Enforce rule that the coinbase starts with serialized block height + if (nHeight >= consensusParams.BIP34Height) + { + CScript expect = CScript() << nHeight; + if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) { + return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase"); + } + } + + // Validation for witness commitments. + // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the + // coinbase (where 0x0000....0000 is used instead). + // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). + // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). + // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are + // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are + // multiple, the last one is used. + bool fHaveWitness = false; +// if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE) { +// int commitpos = GetWitnessCommitmentIndex(block); +// if (commitpos != -1) { +// bool malleated = false; +// uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); +// // The malleation check is ignored; as the transaction tree itself +// // already does not permit it, it is impossible to trigger in the +// // witness tree. +// if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) { +// return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__)); +// } +// CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); +// if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) { +// return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__)); +// } +// fHaveWitness = true; +// } +// } + // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam + if (!fHaveWitness) { + for (size_t i = 0; i < block.vtx.size(); i++) { + if (block.vtx[i]->HasWitness()) { + return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__)); + } + } + } + + // After the coinbase witness nonce and commitment are verified, + // we can check if the block weight passes (before we've checked the + // coinbase witness, it would be possible for the weight to be too + // large by filling up the coinbase witness, which doesn't change + // the block hash, so we couldn't mark the block as permanently + // failed). + if (GetBlockWeight(block) > MAX_BLOCK_WEIGHT) { + return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__)); + } + + return true; +} + +static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) +{ + AssertLockHeld(cs_main); + // Check for duplicate + uint256 hash = block.GetHash(); + BlockMap::iterator miSelf = mapBlockIndex.find(hash); + CBlockIndex *pindex = NULL; + if (hash != chainparams.GetConsensus().hashGenesisBlock) { + + if (miSelf != mapBlockIndex.end()) { + // Block header is already known. + pindex = miSelf->second; + if (ppindex) + *ppindex = pindex; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return state.Invalid(error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate"); + return true; + } + + if (!CheckBlockHeader(block, state, chainparams.GetConsensus())) + return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + + // Get prev block index + CBlockIndex* pindexPrev = NULL; + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + if (mi == mapBlockIndex.end()) + return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); + pindexPrev = (*mi).second; + + //Block time should be greater than equal to block time +BLOCK_GEN_TIME + if (pindexPrev != NULL) + { + if (block.nTime < (pindexPrev->nPeriodStartTime + (pindexPrev->nTimePeriod + 2)*BLOCK_GEN_TIME) / 1000) + { + LogPrintf("[CheckBlockHeaderAdd] end false by block prevblock \n"); + return false; + } + } + + if (pindexPrev->nStatus & BLOCK_FAILED_MASK) + { + //return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); + return state.DoS(0, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); + } + + assert(pindexPrev); + if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) + return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); + + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) + return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + } + if (pindex == NULL) + pindex = AddToBlockIndex(block); + + if (ppindex) + *ppindex = pindex; + + CheckBlockIndex(chainparams.GetConsensus()); + + return true; +} + +// Exposed wrapper for AcceptBlockHeader +bool ProcessNewBlockHeaders(const std::vector& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) +{ + { + LOCK(cs_main); + for (const CBlockHeader& header : headers) { + CBlockIndex *pindex = NULL; // Use a temp pindex instead of ppindex to avoid a const_cast + if (!AcceptBlockHeader(header, state, chainparams, &pindex)) { + std::cout << "ProcessNewBlockHeaders failed!" << std::endl; + return false; + } + if (ppindex) { + *ppindex = pindex; + } + } + } + NotifyHeaderTip(); + return true; +} + +/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ +static bool AcceptBlock(const std::shared_ptr& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) +{ + + const CBlock& block = *pblock; + + if (fNewBlock) *fNewBlock = false; + AssertLockHeld(cs_main); + + CBlockIndex *pindexDummy = NULL; + CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; + + if (!AcceptBlockHeader(block, state, chainparams, &pindex)) + { + std::cout << "AcceptBlock failed!" << std::endl; + return false; + } + + + // Try to process all requested blocks that we don't have, but only + // process an unrequested block if it's new and has enough work to + // advance our tip, and isn't too many blocks ahead. + bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; + bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); + // Blocks that are too out-of-order needlessly limit the effectiveness of + // pruning, because pruning will not delete block files that contain any + // blocks which are too close in height to the tip. Apply this test + // regardless of whether pruning is enabled; it should generally be safe to + // not process unrequested blocks. + bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); + + // TODO: Decouple this function from the block download logic by removing fRequested + // This requires some new chain datastructure to efficiently look up if a + // block is in a chain leading to a candidate for best tip, despite not + // being such a candidate itself. + + // TODO: deal better with return value and error conditions for duplicate + // and unrequested blocks. + //LogPrintf("[AcceptBlock] TODO== %d,%d,%d,%d,%d\n", fAlreadyHave, fRequested, pindex->nTx, fHasMoreWork, fTooFarAhead); + if (fAlreadyHave) return true; + if (!fRequested) { // If we didn't ask for it: + if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned + if (!fHasMoreWork) return true; // Don't process less-work chains + if (fTooFarAhead) return true; // Block height is too high + } + if (fNewBlock) *fNewBlock = true; + + if (!CheckBlock(block, state, chainparams.GetConsensus()) || + !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) { + if (state.IsInvalid() && !state.CorruptionPossible()) { + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + } + return error("%s: %s", __func__, FormatStateMessage(state)); + } + + // Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW + // (but if it does not build on our best tip, let the SendMessages loop relay it) + if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev) + GetMainSignals().NewPoWValidBlock(pindex, pblock); + + int nHeight = pindex->nHeight; + + // Write block to history file + try { + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(state, blockPos, nBlockSize + 8, nHeight, block.GetBlockTime(), dbp != NULL)) + { + return error("AcceptBlock(): FindBlockPos failed"); + } + + if (dbp == NULL) + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) + { + AbortNode(state, "Failed to write block"); + } + + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + { + return error("AcceptBlock(): ReceivedBlockTransactions failed"); + } + + } catch (const std::runtime_error& e) { + return AbortNode(state, std::string("System error: ") + e.what()); + } + + if (fCheckForPruning) + { + FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files + } + + + return true; +} + +bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr pblock, bool fForceProcessing, bool *fNewBlock) +{ + LogPrintf("[ProcessNewBlock] begin\n"); + { + CBlockIndex *pindex = NULL; + if (fNewBlock) *fNewBlock = false; + CValidationState state; + // Ensure that CheckBlock() passes before calling AcceptBlock, as + // belt-and-suspenders. + bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus()); + + LOCK(cs_main); + + if (ret) { + // Store to disk + ret = AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, NULL, fNewBlock); + } + CheckBlockIndex(chainparams.GetConsensus()); + if (!ret) { + GetMainSignals().BlockChecked(*pblock, state); + return error("%s: AcceptBlock FAILED", __func__); + } + } + + NotifyHeaderTip(); + + CValidationState state; // Only used to report errors, not invalidity - ignore it + if (!ActivateBestChain(state, chainparams, pblock)) + { + return error("%s: ActivateBestChain failed", __func__); + } + + LogPrintf("[ProcessNewBlock] end\n"); + return true; +} + +bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) +{ + AssertLockHeld(cs_main); + assert(pindexPrev && pindexPrev == chainActive.Tip()); + if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, block.GetHash())) + return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); + + CCoinsViewCache viewNew(pcoinsTip); + CBlockIndex indexDummy(block); + indexDummy.pprev = pindexPrev; + indexDummy.nHeight = pindexPrev->nHeight + 1; + + // NOTE: CheckBlockHeader is called by CheckBlock + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) + return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); + if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot)) + return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); + if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) + return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); + if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) + return false; + assert(state.IsValid()); + + return true; +} + +/** + * BLOCK PRUNING CODE + */ + +/* Calculate the amount of disk space the block & undo files currently use */ +uint64_t CalculateCurrentUsage() +{ + uint64_t retval = 0; + BOOST_FOREACH(const CBlockFileInfo &file, vinfoBlockFile) { + retval += file.nSize + file.nUndoSize; + } + return retval; +} + +/* Prune a block file (modify associated database entries)*/ +void PruneOneBlockFile(const int fileNumber) +{ + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { + CBlockIndex* pindex = it->second; + if (pindex->nFile == fileNumber) { + pindex->nStatus &= ~BLOCK_HAVE_DATA; + pindex->nStatus &= ~BLOCK_HAVE_UNDO; + pindex->nFile = 0; + pindex->nDataPos = 0; + pindex->nUndoPos = 0; + setDirtyBlockIndex.insert(pindex); + + // Prune from mapBlocksUnlinked -- any block we prune would have + // to be downloaded again in order to consider its chain, at which + // point it would be considered as a candidate for + // mapBlocksUnlinked or setBlockIndexCandidates. + std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev); + while (range.first != range.second) { + std::multimap::iterator _it = range.first; + range.first++; + if (_it->second == pindex) { + mapBlocksUnlinked.erase(_it); + } + } + } + } + + vinfoBlockFile[fileNumber].SetNull(); + setDirtyFileInfo.insert(fileNumber); +} + + +void UnlinkPrunedFiles(const std::set& setFilesToPrune) +{ + for (std::set::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { + CDiskBlockPos pos(*it, 0); + boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); + boost::filesystem::remove(GetBlockPosFilename(pos, "rev")); + LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it); + } +} + +/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */ +void FindFilesToPruneManual(std::set& setFilesToPrune, int nManualPruneHeight) +{ + assert(fPruneMode && nManualPruneHeight > 0); + + LOCK2(cs_main, cs_LastBlockFile); + if (chainActive.Tip() == NULL) + return; + + // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip) + unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP); + int count=0; + for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { + if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) + continue; + PruneOneBlockFile(fileNumber); + setFilesToPrune.insert(fileNumber); + count++; + } + LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count); +} + +/* This function is called from the RPC code for pruneblockchain */ +void PruneBlockFilesManual(int nManualPruneHeight) +{ + CValidationState state; + FlushStateToDisk(state, FLUSH_STATE_NONE, nManualPruneHeight); +} + +/* Calculate the block/rev files that should be deleted to remain under target*/ +void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight) +{ + LOCK2(cs_main, cs_LastBlockFile); + if (chainActive.Tip() == NULL || nPruneTarget == 0) { + return; + } + if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) { + return; + } + + unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; + uint64_t nCurrentUsage = CalculateCurrentUsage(); + // We don't check to prune until after we've allocated new space for files + // So we should leave a buffer under our target to account for another allocation + // before the next pruning. + uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; + uint64_t nBytesToPrune; + int count=0; + + if (nCurrentUsage + nBuffer >= nPruneTarget) { + for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { + nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; + + if (vinfoBlockFile[fileNumber].nSize == 0) + continue; + + if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? + break; + + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning + if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) + continue; + + PruneOneBlockFile(fileNumber); + // Queue up the files for removal + setFilesToPrune.insert(fileNumber); + nCurrentUsage -= nBytesToPrune; + count++; + } + } + + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", + nPruneTarget/1024/1024, nCurrentUsage/1024/1024, + ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, + nLastBlockWeCanPrune, count); +} + +bool CheckDiskSpace(uint64_t nAdditionalBytes) +{ + uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; + + // Check for nMinDiskSpace bytes (currently 50MB) + if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) + return AbortNode("Disk space is low!", _("Error: Disk space is low!")); + + return true; +} + +FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) +{ + if (pos.IsNull()) + return NULL; + boost::filesystem::path path = GetBlockPosFilename(pos, prefix); + boost::filesystem::create_directories(path.parent_path()); + FILE* file = fopen(path.string().c_str(), "rb+"); + if (!file && !fReadOnly) + file = fopen(path.string().c_str(), "wb+"); + if (!file) { + LogPrintf("Unable to open file %s\n", path.string()); + return NULL; + } + if (pos.nPos) { + if (fseek(file, pos.nPos, SEEK_SET)) { + LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); + fclose(file); + return NULL; + } + } + return file; +} + +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "blk", fReadOnly); +} + +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "rev", fReadOnly); +} + +boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) +{ + return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); +} + +CBlockIndex * InsertBlockIndex(uint256 hash) +{ + if (hash.IsNull()) + return NULL; + + // Return existing + BlockMap::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + return (*mi).second; + + // Create new + CBlockIndex* pindexNew = new CBlockIndex(); + if (!pindexNew) + throw std::runtime_error(std::string(__func__) + ": new CBlockIndex failed"); + mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + + return pindexNew; +} + +bool static LoadBlockIndexDB(const CChainParams& chainparams) +{ + if (!pblocktree->LoadBlockIndexGuts(InsertBlockIndex)) + return false; + + boost::this_thread::interruption_point(); + + // Calculate nChainWork + std::vector > vSortedByHeight; + vSortedByHeight.reserve(mapBlockIndex.size()); + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex)); + } + sort(vSortedByHeight.begin(), vSortedByHeight.end()); + BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + { + CBlockIndex* pindex = item.second; + pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); + pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime); + // We can link the chain of blocks for which we've received transactions at some point. + // Pruned nodes may have deleted the block. + if (pindex->nTx > 0) { + if (pindex->pprev) { + if (pindex->pprev->nChainTx) { + pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; + } else { + pindex->nChainTx = 0; + mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); + } + } else { + pindex->nChainTx = pindex->nTx; + } + } + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) + setBlockIndexCandidates.insert(pindex); + if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) + pindexBestInvalid = pindex; + if (pindex->pprev) + pindex->BuildSkip(); + if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) + pindexBestHeader = pindex; + } + + // Load block file info + pblocktree->ReadLastBlockFile(nLastBlockFile); + vinfoBlockFile.resize(nLastBlockFile + 1); + LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); + for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { + pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); + } + LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); + for (int nFile = nLastBlockFile + 1; true; nFile++) { + CBlockFileInfo info; + if (pblocktree->ReadBlockFileInfo(nFile, info)) { + vinfoBlockFile.push_back(info); + } else { + break; + } + } + + // Check presence of blk files + LogPrintf("Checking all blk files are present...\n"); + std::set setBlkDataFiles; + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + if (pindex->nStatus & BLOCK_HAVE_DATA) { + setBlkDataFiles.insert(pindex->nFile); + } + } + for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) + { + CDiskBlockPos pos(*it, 0); + if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) { + return false; + } + } + + // Check whether we have ever pruned block & undo files + pblocktree->ReadFlag("prunedblockfiles", fHavePruned); + if (fHavePruned) + LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); + + // Check whether we need to continue reindexing + bool fReindexing = false; + pblocktree->ReadReindexing(fReindexing); + fReindex |= fReindexing; + + // Check whether we have a transaction index + pblocktree->ReadFlag("txindex", fTxIndex); + LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + + // Load pointer to end of best chain + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + if (it == mapBlockIndex.end()) + return true; + chainActive.SetTip(it->second); + + PruneBlockIndexCandidates(); + + LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); + + return true; +} + +CVerifyDB::CVerifyDB() +{ + // uiInterface.ShowProgress(_("Verifying blocks..."), 0); +} + +CVerifyDB::~CVerifyDB() +{ + uiInterface.ShowProgress("", 100); +} + +bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) +{ + LOCK(cs_main); + if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) + return true; + + // Verify blocks in the best chain + if (nCheckDepth <= 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > chainActive.Height()) + nCheckDepth = chainActive.Height(); + nCheckLevel = std::max(0, std::min(4, nCheckLevel)); + LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + CCoinsViewCache coins(coinsview); + CBlockIndex* pindexState = chainActive.Tip(); + CBlockIndex* pindexFailure = NULL; + int nGoodTransactions = 0; + CValidationState state; + int reportDone = 0; + LogPrintf("[0%%]..."); + for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) + { + boost::this_thread::interruption_point(); + int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); + if (reportDone < percentageDone/10) { + // report every 10% step + LogPrintf("[%d%%]...", percentageDone); + reportDone = percentageDone/10; + } + uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone); + if (pindex->nHeight < chainActive.Height()-nCheckDepth) + break; + if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, only go back as far as we have data. + LogPrintf("VerifyDB(): block verification stopping at height %d (pruning, no data)\n", pindex->nHeight); + break; + } + CBlock block; + // check level 0: read from disk + if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) + return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + // check level 1: verify block validity + if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus())) + return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, + pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); + // check level 2: verify undo validity + if (nCheckLevel >= 2 && pindex) { + CBlockUndo undo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (!pos.IsNull()) { + if (!UndoReadFromDisk(undo, pos, pindex->pprev->GetBlockHash())) + return error("VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + } + } + // check level 3: check for inconsistencies during memory-only disconnect of tip blocks + if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { + bool fClean = true; + if (!DisconnectBlock(block, state, pindex, coins, &fClean)) + return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + pindexState = pindex->pprev; + if (!fClean) { + nGoodTransactions = 0; + pindexFailure = pindex; + } else + nGoodTransactions += block.vtx.size(); + } + if (ShutdownRequested()) + return true; + } + if (pindexFailure) + return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); + + // check level 4: try reconnecting blocks + if (nCheckLevel >= 4) { + CBlockIndex *pindex = pindexState; + while (pindex != chainActive.Tip()) { + boost::this_thread::interruption_point(); + uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); + pindex = chainActive.Next(pindex); + CBlock block; + if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) + return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + if (!ConnectBlock(block, state, pindex, coins, chainparams)) + return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + } + } + + LogPrintf("[DONE].\n"); + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); + + return true; +} + + +bool RewindBlockIndex(const CChainParams& params) +{ + LOCK(cs_main); + + int nHeight = 1; + while (nHeight <= chainActive.Height()) { + if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { + break; + } + nHeight++; + } + + // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 + CValidationState state; + CBlockIndex* pindex = chainActive.Tip(); + while (chainActive.Height() >= nHeight) { + if (fPruneMode && !(chainActive.Tip()->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, don't try rewinding past the HAVE_DATA point; + // since older blocks can't be served anyway, there's + // no need to walk further, and trying to DisconnectTip() + // will fail (and require a needless reindex/redownload + // of the blockchain). + break; + } + if (!DisconnectTip(state, params, true)) { + return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + } + // Occasionally flush state to disk. + if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) + return false; + } + + // Reduce validity flag and have-data flags. + // We do this after actual disconnecting, otherwise we'll end up writing the lack of data + // to disk before writing the chainstate, resulting in a failure to continue if interrupted. + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + CBlockIndex* pindexIter = it->second; + + // Note: If we encounter an insufficiently validated block that + // is on chainActive, it must be because we are a pruning node, and + // this block or some successor doesn't HAVE_DATA, so we were unable to + // rewind all the way. Blocks remaining on chainActive at this point + // must not have their validity reduced. + if (IsWitnessEnabled(pindexIter->pprev, params.GetConsensus()) && !(pindexIter->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(pindexIter)) { + // Reduce validity + pindexIter->nStatus = std::min(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK); + // Remove have-data flags. + pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO); + // Remove storage location. + pindexIter->nFile = 0; + pindexIter->nDataPos = 0; + pindexIter->nUndoPos = 0; + // Remove various other things + pindexIter->nTx = 0; + pindexIter->nChainTx = 0; + pindexIter->nSequenceId = 0; + // Make sure it gets written. + setDirtyBlockIndex.insert(pindexIter); + // Update indexes + setBlockIndexCandidates.erase(pindexIter); + std::pair::iterator, std::multimap::iterator> ret = mapBlocksUnlinked.equal_range(pindexIter->pprev); + while (ret.first != ret.second) { + if (ret.first->second == pindexIter) { + mapBlocksUnlinked.erase(ret.first++); + } else { + ++ret.first; + } + } + } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) { + setBlockIndexCandidates.insert(pindexIter); + } + } + + PruneBlockIndexCandidates(); + + CheckBlockIndex(params.GetConsensus()); + + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { + return false; + } + + return true; +} + +// May NOT be used after any connections are up as much +// of the peer-processing logic assumes a consistent +// block index state +void UnloadBlockIndex() +{ + LOCK(cs_main); + setBlockIndexCandidates.clear(); + chainActive.SetTip(NULL); + pindexBestInvalid = NULL; + pindexBestHeader = NULL; + mempool.clear(); + mapBlocksUnlinked.clear(); + vinfoBlockFile.clear(); + nLastBlockFile = 0; + nBlockSequenceId = 1; + setDirtyBlockIndex.clear(); + setDirtyFileInfo.clear(); + versionbitscache.Clear(); + for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { + warningcache[b].clear(); + } + + BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { + delete entry.second; + } + mapBlockIndex.clear(); + fHavePruned = false; +} + +bool LoadBlockIndex(const CChainParams& chainparams) +{ + // Load block index from databases + if (!fReindex && !LoadBlockIndexDB(chainparams)) + return false; + return true; +} + +bool InitBlockIndex(const CChainParams& chainparams) +{ + LOCK(cs_main); + + // Check whether we're already initialized + if (chainActive.Genesis() != NULL) + return true; + + // Use the provided setting for -txindex in the new database + fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX); + pblocktree->WriteFlag("txindex", fTxIndex); + LogPrintf("Initializing databases...\n"); + + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) + if (!fReindex) { + try { + CBlock &block = const_cast(chainparams.GenesisBlock()); + // Start new block file + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + CValidationState state; + if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) + return error("LoadBlockIndex(): FindBlockPos failed"); + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) + return error("LoadBlockIndex(): writing genesis block to disk failed"); + CBlockIndex *pindex = AddToBlockIndex(block); + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + return error("LoadBlockIndex(): genesis block not accepted"); + // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data + return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + } catch (const std::runtime_error& e) { + return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); + } + } + + return true; +} + +bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp) +{ + // Map of disk positions for blocks with unknown parent (only used for reindex) + static std::multimap mapBlocksUnknownParent; + int64_t nStart = GetTimeMillis(); + + int nLoaded = 0; + try { + // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE+8, SER_DISK, CLIENT_VERSION); + uint64_t nRewind = blkdat.GetPos(); + while (!blkdat.eof()) { + boost::this_thread::interruption_point(); + + blkdat.SetPos(nRewind); + nRewind++; // start one byte further next time, in case of failure + blkdat.SetLimit(); // remove former limit + unsigned int nSize = 0; + try { + // locate a header + unsigned char buf[CMessageHeader::MESSAGE_START_SIZE]; + blkdat.FindByte(chainparams.MessageStart()[0]); + nRewind = blkdat.GetPos()+1; + blkdat >> FLATDATA(buf); + if (memcmp(buf, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) + continue; + // read size + blkdat >> nSize; + if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE) + continue; + } catch (const std::exception&) { + // no valid block header found; don't complain + break; + } + try { + // read block + uint64_t nBlockPos = blkdat.GetPos(); + if (dbp) + dbp->nPos = nBlockPos; + blkdat.SetLimit(nBlockPos + nSize); + blkdat.SetPos(nBlockPos); + std::shared_ptr pblock = std::make_shared(); + CBlock& block = *pblock; + blkdat >> block; + nRewind = blkdat.GetPos(); + + // detect out of order blocks, and store them for later + uint256 hash = block.GetHash(); + if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { + LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), + block.hashPrevBlock.ToString()); + if (dbp) + mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); + continue; + } + + // process in case the block isn't known yet + if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { + LOCK(cs_main); + CValidationState state; + if (AcceptBlock(pblock, state, chainparams, NULL, true, dbp, NULL)) + nLoaded++; + if (state.IsError()) + { + break; + } + + } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { + LogPrint("reindex", "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); + } + + // Activate the genesis block so normal node progress can continue + if (hash == chainparams.GetConsensus().hashGenesisBlock) { + CValidationState state; + if (!ActivateBestChain(state, chainparams)) { + break; + } + } + + NotifyHeaderTip(); + + // Recursively process earlier encountered successors of this block + std::deque queue; + queue.push_back(hash); + while (!queue.empty()) { + uint256 head = queue.front(); + queue.pop_front(); + std::pair::iterator, std::multimap::iterator> range = mapBlocksUnknownParent.equal_range(head); + while (range.first != range.second) { + std::multimap::iterator it = range.first; + std::shared_ptr pblockrecursive = std::make_shared(); + if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus())) + { + LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(), + head.ToString()); + LOCK(cs_main); + CValidationState dummy; + if (AcceptBlock(pblockrecursive, dummy, chainparams, NULL, true, &it->second, NULL)) + { + nLoaded++; + queue.push_back(pblockrecursive->GetHash()); + } + } + range.first++; + mapBlocksUnknownParent.erase(it); + NotifyHeaderTip(); + } + } + } catch (const std::exception& e) { + LogPrintf("%s: Deserialize or I/O error - %s\n", __func__, e.what()); + } + } + } catch (const std::runtime_error& e) { + AbortNode(std::string("System error: ") + e.what()); + } + if (nLoaded > 0) + LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); + return nLoaded > 0; +} + +void static CheckBlockIndex(const Consensus::Params& consensusParams) +{ + if (!fCheckBlockIndex) { + return; + } + + LOCK(cs_main); + + // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, + // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when + // iterating the block tree require that chainActive has been initialized.) + if (chainActive.Height() < 0) { + assert(mapBlockIndex.size() <= 1); + return; + } + + // Build forward-pointing map of the entire block tree. + std::multimap forward; + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + forward.insert(std::make_pair(it->second->pprev, it->second)); + } + + assert(forward.size() == mapBlockIndex.size()); + + std::pair::iterator,std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); + CBlockIndex *pindex = rangeGenesis.first->second; + rangeGenesis.first++; + assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. + + // Iterate over the entire block tree, using depth-first search. + // Along the way, remember whether there are blocks on the path from genesis + // block being explored which are the first to have certain properties. + size_t nNodes = 0; + int nHeight = 0; + CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. + CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. + CBlockIndex* pindexFirstNeverProcessed = NULL; // Oldest ancestor of pindex for which nTx == 0. + CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). + CBlockIndex* pindexFirstNotTransactionsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS (regardless of being valid or not). + CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). + CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). + while (pindex != NULL) { + nNodes++; + if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; + if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; + if (pindexFirstNeverProcessed == NULL && pindex->nTx == 0) pindexFirstNeverProcessed = pindex; + if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; + if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; + + // Begin: actual consistency checks. + if (pindex->pprev == NULL) { + // Genesis block checks. + assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. + assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. + } + if (pindex->nChainTx == 0) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock) + // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred). + // HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred. + if (!fHavePruned) { + // If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0 + assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); + assert(pindexFirstMissing == pindexFirstNeverProcessed); + } else { + // If we have pruned, then we can only say that HAVE_DATA implies nTx > 0 + if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0); + } + if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA); + assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent. + // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. + assert((pindexFirstNeverProcessed != NULL) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned). + assert((pindexFirstNotTransactionsValid != NULL) == (pindex->nChainTx == 0)); + assert(pindex->nHeight == nHeight); // nHeight must be consistent. + assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. + assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. + assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid + if (pindexFirstInvalid == NULL) { + // Checks for not-invalid blocks. + assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. + } + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == NULL) { + if (pindexFirstInvalid == NULL) { + // If this block sorts at least as good as the current tip and + // is valid and we have all data for its parents, it must be in + // setBlockIndexCandidates. chainActive.Tip() must also be there + // even if some data has been pruned. + if (pindexFirstMissing == NULL || pindex == chainActive.Tip()) { + assert(setBlockIndexCandidates.count(pindex)); + } + // If some parent is missing, then it could be that this block was in + // setBlockIndexCandidates but had to be removed because of the missing data. + // In this case it must be in mapBlocksUnlinked -- see test below. + } + } else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates. + assert(setBlockIndexCandidates.count(pindex) == 0); + } + // Check whether this block is in mapBlocksUnlinked. + std::pair::iterator,std::multimap::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); + bool foundInUnlinked = false; + while (rangeUnlinked.first != rangeUnlinked.second) { + assert(rangeUnlinked.first->first == pindex->pprev); + if (rangeUnlinked.first->second == pindex) { + foundInUnlinked = true; + break; + } + rangeUnlinked.first++; + } + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != NULL && pindexFirstInvalid == NULL) { + // If this block has block data available, some parent was never received, and has no invalid parents, it must be in mapBlocksUnlinked. + assert(foundInUnlinked); + } + if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in mapBlocksUnlinked if we don't HAVE_DATA + if (pindexFirstMissing == NULL) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked. + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == NULL && pindexFirstMissing != NULL) { + // We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent. + assert(fHavePruned); // We must have pruned. + // This block may have entered mapBlocksUnlinked if: + // - it has a descendant that at some point had more work than the + // tip, and + // - we tried switching to that descendant but were missing + // data for some intermediate block between chainActive and the + // tip. + // So if this block is itself better than chainActive.Tip() and it wasn't in + // setBlockIndexCandidates, then it must be in mapBlocksUnlinked. + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) { + if (pindexFirstInvalid == NULL) { + assert(foundInUnlinked); + } + } + } + // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow + // End: actual consistency checks. + + // Try descending into the first subnode. + std::pair::iterator,std::multimap::iterator> range = forward.equal_range(pindex); + if (range.first != range.second) { + // A subnode was found. + pindex = range.first->second; + nHeight++; + continue; + } + // This is a leaf node. + // Move upwards until we reach a node of which we have not yet visited the last child. + while (pindex) { + // We are going to either move to a parent or a sibling of pindex. + // If pindex was the first with a certain property, unset the corresponding variable. + if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; + if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; + if (pindex == pindexFirstNeverProcessed) pindexFirstNeverProcessed = NULL; + if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; + if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = NULL; + if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; + if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL; + // Find our parent. + CBlockIndex* pindexPar = pindex->pprev; + // Find which child we just visited. + std::pair::iterator,std::multimap::iterator> rangePar = forward.equal_range(pindexPar); + while (rangePar.first->second != pindex) { + assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child. + rangePar.first++; + } + // Proceed to the next one. + rangePar.first++; + if (rangePar.first != rangePar.second) { + // Move to the sibling. + pindex = rangePar.first->second; + break; + } else { + // Move up further. + pindex = pindexPar; + nHeight--; + continue; + } + } + } + + // Check that we actually traversed the entire map. + assert(nNodes == forward.size()); +} + +std::string CBlockFileInfo::ToString() const +{ + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); +} + +CBlockFileInfo* GetBlockFileInfo(size_t n) +{ + return &vinfoBlockFile.at(n); +} + +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache); +} + +int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache); +} + +static const uint64_t MEMPOOL_DUMP_VERSION = 1; + +bool LoadMempool(void) +{ + int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; + FILE* filestr = fopen((GetDataDir() / "mempool.dat").string().c_str(), "rb"); + CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); + if (file.IsNull()) { + LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n"); + return false; + } + + int64_t count = 0; + int64_t skipped = 0; + int64_t failed = 0; + int64_t nNow = GetTime(); + + try { + uint64_t version; + file >> version; + if (version != MEMPOOL_DUMP_VERSION) { + return false; + } + uint64_t num; + file >> num; + double prioritydummy = 0; + while (num--) { + CTransactionRef tx; + int64_t nTime; + int64_t nFeeDelta; + file >> tx; + file >> nTime; + file >> nFeeDelta; + + CAmount amountdelta = nFeeDelta; + if (amountdelta) { + mempool.PrioritiseTransaction(tx->GetHash(), tx->GetHash().ToString(), prioritydummy, amountdelta); + } + CValidationState state; + if (nTime + nExpiryTimeout > nNow) { + LOCK(cs_main); + AcceptToMemoryPoolWithTime(mempool, state, tx, true, NULL, nTime); + if (state.IsValid()) { + ++count; + } else { + ++failed; + } + } else { + ++skipped; + } + if (ShutdownRequested()) + return false; + } + std::map mapDeltas; + file >> mapDeltas; + + for (const auto& i : mapDeltas) { + mempool.PrioritiseTransaction(i.first, i.first.ToString(), prioritydummy, i.second); + } + } catch (const std::exception& e) { + LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what()); + return false; + } + + LogPrintf("Imported mempool transactions from disk: %i successes, %i failed, %i expired\n", count, failed, skipped); + return true; +} + +void DumpMempool(void) +{ + int64_t start = GetTimeMicros(); + + std::map mapDeltas; + std::vector vinfo; + + { + LOCK(mempool.cs); + for (const auto &i : mempool.mapDeltas) { + mapDeltas[i.first] = i.second.second; + } + vinfo = mempool.infoAll(); + } + + int64_t mid = GetTimeMicros(); + + try { + FILE* filestr = fopen((GetDataDir() / "mempool.dat.new").string().c_str(), "wb"); + if (!filestr) { + return; + } + + CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); + + uint64_t version = MEMPOOL_DUMP_VERSION; + file << version; + + file << (uint64_t)vinfo.size(); + for (const auto& i : vinfo) { + file << *(i.tx); + file << (int64_t)i.nTime; + file << (int64_t)i.nFeeDelta; + mapDeltas.erase(i.tx->GetHash()); + } + + file << mapDeltas; + FileCommit(file.Get()); + file.fclose(); + RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); + int64_t last = GetTimeMicros(); + LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*0.000001, (last-mid)*0.000001); + } catch (const std::exception& e) { + LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what()); + } +} + +//! Guess how far we are in the verification process at the given block index +double GuessVerificationProgress(const ChainTxData& data, CBlockIndex *pindex) { + if (pindex == NULL) + return 0.0; + + int64_t nNow = time(NULL); + + double fTxTotal; + + if (pindex->nChainTx <= data.nTxCount) { + fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate; + } else { + fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate; + } + + return pindex->nChainTx / fTxTotal; +} + +class CMainCleanup +{ +public: + CMainCleanup() {} + ~CMainCleanup() { + // block headers + BlockMap::iterator it1 = mapBlockIndex.begin(); + for (; it1 != mapBlockIndex.end(); it1++) + delete (*it1).second; + mapBlockIndex.clear(); + } +} instance_of_cmaincleanup; + +CFeeRate txminfee(50000);//DEFAULT_TRANSACTION_FEE +bool VerifyFee(const CTransaction& tx, CAmount nneedFee) //Check rates +{ + CAmount nFee = nneedFee; + if (tx.GetTxType() == TXOUT_TOKENREG) + nFee -= 100 * COIN; + unsigned int nBytes= ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + CFeeRate curtxfee(nFee, nBytes); + if (g_bStdCout) + { + std::cout << "txhash---fee-- : " << tx.GetHash().ToString() << " " << nneedFee << " " << nBytes << std::endl; + std::cout << "tx---fee : " << curtxfee.ToString() << std::endl; + std::cout << "txminfee : " << txminfee.ToString() << std::endl; + } + + if (curtxfee < txminfee) + return false; + return true; +} diff --git a/.svn/pristine/1e/1efc524bf11e79990dd9a5c3cf8b0a2bf113dc8f.svn-base b/.svn/pristine/1e/1efc524bf11e79990dd9a5c3cf8b0a2bf113dc8f.svn-base new file mode 100644 index 0000000..e046585 --- /dev/null +++ b/.svn/pristine/1e/1efc524bf11e79990dd9a5c3cf8b0a2bf113dc8f.svn-base @@ -0,0 +1,1396 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "chain.h" +#include "coins.h" +#include "consensus/validation.h" +#include "core_io.h" +#include "init.h" +#include "keystore.h" +#include "validation.h" +#include "merkleblock.h" +#include "net.h" +#include "policy/policy.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" +#include "txmempool.h" +#include "uint256.h" +#include "utilstrencodings.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif + +#include + +#include +#include + +#include + + +using namespace std; + +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) +{ + txnouttype type; + vector addresses; + int nRequired; + + out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey))); + if (fIncludeHex) + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + + if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + out.push_back(Pair("type", GetTxnOutputType(type))); + return; + } + + out.push_back(Pair("reqSigs", nRequired)); + out.push_back(Pair("type", GetTxnOutputType(type))); + + UniValue a(UniValue::VARR); + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + out.push_back(Pair("addresses", a)); +} + +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) +{ + entry.push_back(Pair("txid", tx.GetHash().GetHex())); + entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex())); + entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); + entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx))); + entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + CTransactionRef prevTx; + uint256 hashblock; + std::vector txoutdestes; + std::vector addresses; + addresses.clear(); + txnouttype type; + int nRequired; + UniValue vin(UniValue::VARR); + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const CTxIn& txin = tx.vin[i]; + UniValue in(UniValue::VOBJ); + if (tx.IsCoinBase()) + in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + else { + in.push_back(Pair("txid", txin.prevout.hash.GetHex())); + in.push_back(Pair("vout", (int64_t)txin.prevout.n)); + if (fTxIndex) + { + UniValue add(UniValue::VARR); + if (!GetTransaction(txin.prevout.hash, prevTx, Params().GetConsensus(), hashblock, true)) + { + if (!GetCachedChainTransaction(tx.vin[i].prevout.hash, prevTx)) + throw JSONRPCError(RPC_VERIFY_ERROR, "bad-input,Wrongful."); + } + const CTxOut& prev = prevTx->vout[txin.prevout.n]; + if (!ExtractDestinations(prev.scriptPubKey, type, txoutdestes, nRequired)) + throw JSONRPCError(RPC_VERIFY_ERROR, "txin-address-unextracted."); + + BOOST_FOREACH(CTxDestination &dest, txoutdestes){ + add.push_back(CBitcoinAddress(dest).ToString()); + } + + + in.push_back(Pair("addresses",add)); + in.push_back(Pair("type",(int64_t)prev.txType)); + switch ((int64_t)prev.txType) + { + case 4: + in.push_back(Pair("tokensymbol", prev.tokenRegLabel.getTokenSymbol())); + in.push_back(Pair("tokenvalue", ValueFromTCoins(prev.tokenRegLabel.totalCount, (int)tokenDataMap[prev.tokenRegLabel.getTokenSymbol()].accuracy))); + in.push_back(Pair("accuracy", prev.tokenRegLabel.accuracy)); + break; + case 5: + in.push_back(Pair("tokensymbol", prev.tokenLabel.getTokenSymbol())); + in.push_back(Pair("tokenvalue", ValueFromTCoins(prev.tokenLabel.value, (int)tokenDataMap[prev.tokenLabel.getTokenSymbol()].accuracy))); + in.push_back(Pair("accuracy", prev.tokenLabel.accuracy)); + break; + default: + break; + } + in.push_back(Pair("nvalue", ValueFromAmount(prev.nValue))); + } + //end + UniValue o(UniValue::VOBJ); + o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true))); + o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + in.push_back(Pair("scriptSig", o)); + } + if (tx.HasWitness()) { + UniValue txinwitness(UniValue::VARR); + for (unsigned int j = 0; j < tx.vin[i].scriptWitness.stack.size(); j++) { + std::vector item = tx.vin[i].scriptWitness.stack[j]; + txinwitness.push_back(HexStr(item.begin(), item.end())); + } + in.push_back(Pair("txinwitness", txinwitness)); + } + in.push_back(Pair("sequence", (int64_t)txin.nSequence)); + vin.push_back(in); + } + entry.push_back(Pair("vin", vin)); + UniValue vout(UniValue::VARR); + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut& txout = tx.vout[i]; + UniValue out(UniValue::VOBJ); + out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + out.push_back(Pair("n", (int64_t)i)); + out.push_back(Pair("type", (int64_t)txout.txType)); + + switch (txout.txType) + { + case 1: + out.push_back(Pair("devotetype", txout.devoteLabel.ExtendType)); + out.push_back(Pair("devotepubkeyhash160", txout.devoteLabel.hash.GetHex())); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel", txout.txLabel)); + break; + + case 4: + out.push_back(Pair("TokenSymbol", txout.tokenRegLabel.getTokenSymbol())); + out.push_back(Pair("TokenValue", txout.tokenRegLabel.value)); + out.push_back(Pair("TokenHash", txout.tokenRegLabel.hash.GetHex())); + out.push_back(Pair("TokenLabel", txout.tokenRegLabel.getTokenLabel())); + out.push_back(Pair("TokenIssue", txout.tokenRegLabel.issueDate)); + out.push_back(Pair("TokenTotalCount", ValueFromTCoins(txout.tokenRegLabel.totalCount, (int)tokenDataMap[txout.tokenRegLabel.getTokenSymbol()].accuracy))); + out.push_back(Pair("accuracy", txout.tokenRegLabel.accuracy)); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel", txout.txLabel)); + break; + + case 5: + out.push_back(Pair("TokenSymbol", txout.tokenLabel.getTokenSymbol())); + out.push_back(Pair("TokenValue", ValueFromTCoins(txout.tokenLabel.value, (int)tokenDataMap[txout.tokenLabel.getTokenSymbol()].accuracy))); + out.push_back(Pair("accuracy", txout.tokenLabel.accuracy)); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel", txout.txLabel)); + break; + + case 2: + case 3: + out.push_back(Pair("extype", txout.ipcLabel.ExtendType)); + out.push_back(Pair("starttime", txout.ipcLabel.startTime)); + out.push_back(Pair("stoptime", txout.ipcLabel.stopTime)); + out.push_back(Pair("reauthorize", txout.ipcLabel.reAuthorize)); + out.push_back(Pair("uniqueauthorize", txout.ipcLabel.uniqueAuthorize)); + out.push_back(Pair("hashLen", txout.ipcLabel.hashLen)); + out.push_back(Pair("IPChash", txout.ipcLabel.hash.GetHex())); + out.push_back(Pair("IPCTitle", txout.ipcLabel.labelTitle)); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel",txout.txLabel)); + break; + + default: + case 0: + break; + } + + UniValue o(UniValue::VOBJ); + ScriptPubKeyToJSON(txout.scriptPubKey, o, true); + out.push_back(Pair("scriptPubKey", o)); + vout.push_back(out); + } + entry.push_back(Pair("vout", vout)); + + if (!hashBlock.IsNull()) { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) { + entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); + entry.push_back(Pair("time", pindex->GetBlockTime())); + entry.push_back(Pair("blocktime", pindex->GetBlockTime())); + } + else + entry.push_back(Pair("confirmations", 0)); + } + } +} + +UniValue getrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "getrawtransaction \"txid\" ( verbose )\n" + + "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" + "enabled, it also works for blockchain transactions.\n" + "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" + + "\nReturn the raw transaction data.\n" + "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" + "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n" + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n" + + "\nResult (if verbose is not set or set to false):\n" + "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + + "\nResult (if verbose is set to true):\n" + "{\n" + " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" + " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" + " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" + " \"size\" : n, (numeric) The serialized transaction size\n" + " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) \n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"sequence\": n (numeric) The script sequence number\n" + " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + " \"blockhash\" : \"hash\", (string) the block hash\n" + " \"confirmations\" : n, (numeric) The confirmations\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getrawtransaction", "\"mytxid\"") + + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + + HelpExampleRpc("getrawtransaction", "\"mytxid\", true") + ); + + LOCK(cs_main); + + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + + // Accept either a bool (true) or a num (>=1) to indicate verbose output. + bool fVerbose = false; + if (request.params.size() > 1) { + if (request.params[1].isNum()) { + if (request.params[1].get_int() != 0) { + fVerbose = true; + } + } + else if(request.params[1].isBool()) { + if(request.params[1].isTrue()) { + fVerbose = true; + } + } + else { + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid type provided. Verbose parameter must be a boolean."); + } + } + + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + + string strHex = EncodeHexTx(*tx, RPCSerializationFlags()); + + if (!fVerbose) + return strHex; + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", strHex)); + TxToJSON(*tx, hashBlock, result); + + return result; +} + +UniValue getfeeoftxid(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + throw runtime_error( + "getfeeoftxid \"txid\" \n" + + "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" + "enabled, it also works for blockchain transactions.\n" + "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" + + "\nReturn the raw transaction fee.\n" + "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + + "\nResult (if verbose is not set or set to false):\n" + "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + + "\nResult :\n" + "{\n" + " \"fee\" : \"amount\", (string) The transaction fee \n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getfeeoftxid", "\"mytxid\"") + ); + + LOCK(cs_main); + + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + CAmount nValue = 0; + nValue = Getfeebytxid(*tx); + UniValue result(UniValue::VOBJ); + result.push_back(Pair("fee", ValueFromAmount(nValue))); + + return result; +} + +UniValue gettxoutproof(const JSONRPCRequest& request) +{ + if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) + throw runtime_error( + "gettxoutproof [\"txid\",...] ( blockhash )\n" + "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" + "\nNOTE: By default this function only works sometimes. This is when there is an\n" + "unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option or\n" + "specify the block in which the transaction is included manually (by blockhash).\n" + "\nArguments:\n" + "1. \"txids\" (string) A json array of txids to filter\n" + " [\n" + " \"txid\" (string) A transaction hash\n" + " ,...\n" + " ]\n" + "2. \"blockhash\" (string, optional) If specified, looks for txid in the block with this hash\n" + "\nResult:\n" + "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" + ); + + set setTxids; + uint256 oneTxid; + UniValue txids = request.params[0].get_array(); + for (unsigned int idx = 0; idx < txids.size(); idx++) { + const UniValue& txid = txids[idx]; + if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); + uint256 hash(uint256S(txid.get_str())); + if (setTxids.count(hash)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); + setTxids.insert(hash); + oneTxid = hash; + } + + LOCK(cs_main); + + CBlockIndex* pblockindex = NULL; + + uint256 hashBlock; + if (request.params.size() > 1) + { + hashBlock = uint256S(request.params[1].get_str()); + if (!mapBlockIndex.count(hashBlock)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + pblockindex = mapBlockIndex[hashBlock]; + } else { + CCoins coins; + if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) + pblockindex = chainActive[coins.nHeight]; + } + + if (pblockindex == NULL) + { + CTransactionRef tx; + if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); + if (!mapBlockIndex.count(hashBlock)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); + pblockindex = mapBlockIndex[hashBlock]; + } + + CBlock block; + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + unsigned int ntxFound = 0; + for (const auto& tx : block.vtx) + if (setTxids.count(tx->GetHash())) + ntxFound++; + if (ntxFound != setTxids.size()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); + + CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); + CMerkleBlock mb(block, setTxids); + ssMB << mb; + std::string strHex = HexStr(ssMB.begin(), ssMB.end()); + return strHex; +} + +UniValue verifytxoutproof(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "verifytxoutproof \"proof\"\n" + "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" + "and throwing an RPC error if the block is not in our best chain\n" + "\nArguments:\n" + "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" + "\nResult:\n" + "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" + ); + + CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); + CMerkleBlock merkleBlock; + ssMB >> merkleBlock; + + UniValue res(UniValue::VARR); + + vector vMatch; + vector vIndex; + if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) + return res; + + LOCK(cs_main); + + if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); + + BOOST_FOREACH(const uint256& hash, vMatch) + res.push_back(hash.GetHex()); + return res; +} + +UniValue createrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) + throw runtime_error( + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" + "\nCreate a transaction spending the given inputs and creating new outputs.\n" + "Outputs can be addresses or data.\n" + "Returns hex-encoded raw transaction.\n" + "Note that the transaction's inputs are not signed, and\n" + "it is not stored in the wallet or transmitted to the network.\n" + + "\nArguments:\n" + "1. \"inputs\" (array, required) A json array of json objects\n" + " [\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"sequence\":n (numeric, optional) The sequence number\n" + " } \n" + " ,...\n" + " ]\n" + "2. \"outputs\" (object, required) a json object with outputs\n" + " {\n" + " \"address\": x.xxx, (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" + " \"data\": \"hex\" (string, required) The key is \"data\", the value is hex encoded data\n" + " ,...\n" + " }\n" + "3. txtype (numeric, optional, default=0) tx type \n" + "\nResult:\n" + "\"transaction\" (string) hex string of the transaction\n" + + "\nExamples:\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VARR)(UniValue::VARR)(UniValue::VNUM), true); + if (request.params[0].isNull() || request.params[1].isNull()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); + + UniValue inputs = request.params[0].get_array(); + UniValue outputs = request.params[1].get_array(); + + CMutableTransaction rawTx; + int TxType = 0; + if (request.params.size() > 2 && !request.params[2].isNull()) { + TxType = request.params[2].get_int(); + } + + multimap mapInput; + multimap::iterator m; + CAmount nVinValue = 0; + CScript strScriptnewout; + + set > setCoins; + setCoins.clear(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + const UniValue& o = input.get_obj(); + + uint256 txid = ParseHashO(o, "txid"); + + const UniValue& vout_v = find_value(o, "vout"); + if (!vout_v.isNum()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + const UniValue& VinValue = find_value(o, "value"); + const UniValue& Vinaddress = find_value(o, "address"); + + if (!VinValue.isNull() || !Vinaddress.isNull()) + { + if (!VinValue.isNum() || !Vinaddress.isStr()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vin value must be num"); + nVinValue = AmountFromValue(VinValue); + const string& addressname = Vinaddress.get_str(); + CBitcoinAddress address(addressname); + strScriptnewout = GetScriptForDestination(address.Get()); + } + + + mapInput.insert(make_pair(txid.GetHex(), nOutput)); + + uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); + + // set the sequence number if passed in the parameters object + const UniValue& sequenceObj = find_value(o, "sequence"); + if (sequenceObj.isNum()) { + int64_t seqNr64 = sequenceObj.get_int64(); + if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); + else + nSequence = (uint32_t)seqNr64; + } + + CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); + + rawTx.vin.push_back(in); + if (pwalletMain->mapWallet.count(txid) == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txid can't found"); + CWalletTx& prewtx = pwalletMain->mapWallet[txid]; + const CWalletTx* pprewtx = &prewtx; + setCoins.insert(make_pair(pprewtx, nOutput)); + } + CAmount totalvalue = 0; + set setNormalAddress; + multimap setIPCAddress; + multimap setTokenAddress; + for (unsigned int idx = 0; idx < outputs.size(); idx++) { + const UniValue& output = outputs[idx]; + const UniValue& o = output.get_obj(); + + const UniValue& txDataObj = find_value(o, "data"); + if (txDataObj.isStr() || txDataObj.isNum()) + { + std::vector data = ParseHexV(txDataObj.getValStr(), "Data"); + CTxOut out(0, CScript() << OP_RETURN << data); + rawTx.vout.push_back(out); + } + else + { + const UniValue& txAddressObj = find_value(o, "address"); + const UniValue& txAmountObj = find_value(o, "value"); + if (!txAddressObj.isStr() || !txAmountObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, ("Invalid parameter, none of address or amount")); + } + + const string& name_ = txAddressObj.get_str(); + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + name_); + + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(txAmountObj); + CTxOut out; + + //Screening transaction type + const UniValue& txTypeObj = find_value(o, "txtype"); + if (!txTypeObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txType is not a Number"); + } + uint8_t txType = txTypeObj.get_int(); + + + + const UniValue& exTypeObj = find_value(o, "extype"); + const UniValue& txLabelObj = find_value(o, "txLabel"); + + //IPC transaction value + const UniValue& startTimeObj = find_value(o, "starttime"); + const UniValue& stopTimeObj = find_value(o, "stoptime"); + const UniValue& reAuthObj = find_value(o, "reAu"); + const UniValue& uniAuthObj = find_value(o, "uniAu"); + const UniValue& ipcHashObj = find_value(o, "IPChash"); + const UniValue& ipcTitleObj = find_value(o, "IPCtitle"); + + //Value of token trade + const UniValue& TokenSymbolObj = find_value(o, "tokensymbol"); + const UniValue& TokenHashObj = find_value(o, "tokenhash"); + const UniValue& TokenLabelObj = find_value(o, "tokenlabel"); + const UniValue& TokenIssueObj = find_value(o, "tokenissue"); + const UniValue& TokenTotalCountObj = find_value(o, "totalcount"); + const UniValue& TokenValueObj = find_value(o, "tokenvalue"); + const UniValue& TokenAccuracyObj = find_value(o, "accuracy"); + + //Campaign - related values + const UniValue& devotetypeObj = find_value(o, "devotetype"); + const UniValue& devotepubkeyhash160Obj = find_value(o, "devotepubkeyhash160"); + + IPCLabel ipcLabel; + DevoteLabel devoteLabel; + TokenRegLabel regLabel; + TokenLabel tokenlabel; + + std::string checkStr; + std::string devoterpubkeyhashstring; + uint8_t devoteType; + CKeyID gottenKeyID; + std::string devotecontentstring; + std::string strtxLabel = ""; + strtxLabel=txLabelObj.get_str(); + + switch (txType) + { + case 1://For trading + devoterpubkeyhashstring = devotepubkeyhash160Obj.get_str(); + devoteLabel.hash.SetHex(devoterpubkeyhashstring); + devoteType = devotetypeObj.get_int(); + devoteLabel.ExtendType = devoteType; + out = CTxOut(nAmount, devoteLabel); + break; + + case 2://Ownership transaction + case 3://Licensing deals + if (!exTypeObj.isNum() || + !startTimeObj.isStr() || + !stopTimeObj.isStr() || + !reAuthObj.isNum() || + !uniAuthObj.isNum() || + !ipcHashObj.isStr() || + !ipcTitleObj.isStr()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, IPC Params is invalid"); + } + if (!ParseUInt32(startTimeObj.get_str(), &ipcLabel.startTime)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, IPC startTime is invalid"); + if (!ParseUInt32(stopTimeObj.get_str(), &ipcLabel.stopTime)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, IPC stopTime is invalid"); + ipcLabel.hash.SetHex(ipcHashObj.get_str()); + + setIPCAddress.insert(make_pair(address, ipcLabel.hash)); + + + ipcLabel.ExtendType = exTypeObj.get_int(); + ipcLabel.reAuthorize = reAuthObj.get_int(); + ipcLabel.uniqueAuthorize = uniAuthObj.get_int(); + ipcLabel.labelTitle = ipcTitleObj.get_str(); + out = CTxOut(nAmount, scriptPubKey, (TransactionOutType_t)txType, ipcLabel, strtxLabel); + break; + + case 4: //Trade in token currency + if (!TokenSymbolObj.isStr() || + !TokenHashObj.isStr() || + !TokenLabelObj.isStr() || + !TokenIssueObj.isNum() || + !TokenTotalCountObj.isNum()|| + !TokenAccuracyObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Token Params is invalid"); + } + //There are no various forms of ipc in Symbol and label + checkStr = TokenSymbolObj.get_str(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != string::npos) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol cannot contain IPC"); + } + if (checkStr.size() > 8) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol MaxLen = 8"); + + checkStr = TokenLabelObj.get_str(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != string::npos) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenLabel cannot contain IPC"); + } + if (checkStr.size() > 16) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenLabel MaxLen = 16"); + + //TokenHash + checkStr = TokenHashObj.get_str(); + if (checkStr.size() > 32) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenHash MaxLen = 32(16Byte)"); + + + memcpy(regLabel.TokenSymbol, TokenSymbolObj.get_str().c_str(), TokenSymbolObj.get_str().size()>8 ? 8 : TokenSymbolObj.get_str().size()); + + setTokenAddress.insert(make_pair(address, tokenlabel.getTokenSymbol())); + + regLabel.hash.SetHex(TokenHashObj.get_str()); + memcpy(regLabel.label, TokenLabelObj.get_str().c_str(), TokenLabelObj.get_str().size() > 16 ? 16 : TokenLabelObj.get_str().size()); + regLabel.issueDate = TokenIssueObj.get_int(); + regLabel.totalCount = TokenTotalCountObj.get_int64(); + regLabel.value = TokenTotalCountObj.get_int64(); + regLabel.accuracy = TokenAccuracyObj.get_int(); + out = CTxOut(nAmount, scriptPubKey, regLabel,strtxLabel); + break; + + case 5: //Tokens trading + if (!TokenSymbolObj.isStr() || + !TokenValueObj.isNum() || + !TokenAccuracyObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Token Params is invalid"); + } + checkStr = TokenSymbolObj.get_str(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != string::npos) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol cannot contain IPC"); + } + if (checkStr.size() > 8) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol MaxLen = 8"); + + memcpy(tokenlabel.TokenSymbol, TokenSymbolObj.get_str().c_str(), TokenSymbolObj.get_str().size() > 8 ? 8 : TokenSymbolObj.get_str().size()); + + setTokenAddress.insert(make_pair(address, tokenlabel.getTokenSymbol())); + + tokenlabel.value = TokenValueObj.get_int64(); + tokenlabel.accuracy = TokenAccuracyObj.get_int(); + out = CTxOut(nAmount, scriptPubKey, tokenlabel,strtxLabel); + break; + + + case 0://General trading + setNormalAddress.insert(address); + + out = CTxOut(nAmount, scriptPubKey); + break; + default: + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txType out of range"); + + } + + rawTx.vout.push_back(out); + totalvalue += out.nValue; + + + } + + } + CAmount nvalue1(0); + CTxOut newout(nvalue1, strScriptnewout); + rawTx.vout.push_back(newout); + + + if (!(pwalletMain->DummySignTx(rawTx, setCoins))) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "DummySignTx erro!"); + } + unsigned int nBytes = GetVirtualTransactionSize(rawTx); + CAmount fee = pwalletMain->GetMinimumFee(nBytes, 8, mempool) * 2; + //Add a change + if (TxType == 4) + { + CAmount newoutvalue = nVinValue - totalvalue - AmountFromValue(10) - fee; + rawTx.vout[rawTx.vout.size() - 1].nValue = newoutvalue; + } + else + { + CAmount newoutvalue = nVinValue - totalvalue - fee; + rawTx.vout[rawTx.vout.size() - 1].nValue = newoutvalue; + } + + return EncodeHexTx(rawTx); +} + +UniValue decoderawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "decoderawtransaction \"hexstring\"\n" + "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + + "\nResult:\n" + "{\n" + " \"txid\" : \"id\", (string) The transaction id\n" + " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" + " \"size\" : n, (numeric) The transaction size\n" + " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) The output number\n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" + " \"sequence\": n (numeric) The script sequence number\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") + ); + + LOCK(cs_main); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + + CMutableTransaction mtx; + + if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + UniValue result(UniValue::VOBJ); + TxToJSON(CTransaction(std::move(mtx)), uint256(), result); + + return result; +} + +UniValue decodescript(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "decodescript \"hexstring\"\n" + "\nDecode a hex-encoded script.\n" + "\nArguments:\n" + "1. \"hexstring\" (string) the hex encoded script\n" + "\nResult:\n" + "{\n" + " \"asm\":\"asm\", (string) Script public key\n" + " \"hex\":\"hex\", (string) hex encoded public key\n" + " \"type\":\"type\", (string) The output type\n" + " \"reqSigs\": n, (numeric) The required signatures\n" + " \"addresses\": [ (json array of string)\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ],\n" + " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("decodescript", "\"hexstring\"") + + HelpExampleRpc("decodescript", "\"hexstring\"") + ); + + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + + UniValue r(UniValue::VOBJ); + CScript script; + if (request.params[0].get_str().size() > 0){ + vector scriptData(ParseHexV(request.params[0], "argument")); + script = CScript(scriptData.begin(), scriptData.end()); + } else { + // Empty scripts are valid + } + ScriptPubKeyToJSON(script, r, false); + + UniValue type; + type = find_value(r, "type"); + + if (type.isStr() && type.get_str() != "scripthash") { + // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, + // don't return the address for a P2SH of the P2SH. + r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + } + + return r; +} + +/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ +static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) +{ + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", txin.prevout.hash.ToString())); + entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); + entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); + entry.push_back(Pair("error", strMessage)); + vErrorsRet.push_back(entry); +} + +UniValue signrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) + throw runtime_error( + "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" + "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "The second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + "The third optional argument (may be null) is an array of base58-encoded private\n" + "keys that, if given, will be the only keys used to sign the transaction.\n" +#ifdef ENABLE_WALLET + + HelpRequiringPassphrase() + "\n" +#endif + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + " [ (json array of json objects, or 'null' if none provided)\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"scriptPubKey\": \"hex\", (string, required) script key\n" + " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" + " \"amount\": value (numeric, required) The amount spent\n" + " }\n" + " ,...\n" + " ]\n" + "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n" + " [ (json array of strings, or 'null' if none provided)\n" + " \"privatekey\" (string) private key in base58-encoding\n" + " ,...\n" + " ]\n" + "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + + "\nResult:\n" + "{\n" + " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" + " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" + " {\n" + " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" + " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" + " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" + " \"sequence\" : n, (numeric) Script sequence number\n" + " \"error\" : \"text\" (string) Verification or signing error related to the input\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + HelpExampleRpc("signrawtransaction", "\"myhex\"") + ); + +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); + + vector txData(ParseHexV(request.params[0], "argument 1")); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + vector txVariants; + while (!ssData.empty()) { + try { + CMutableTransaction tx; + ssData >> tx; + txVariants.push_back(tx); + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + } + + if (txVariants.empty()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); + + // mergedTx will end up with all the signatures; it + // starts as a clone of the rawtx: + CMutableTransaction mergedTx(txVariants[0]); + + // Fetch previous transactions (inputs): + CCoinsView viewDummy; + CCoinsViewCache view(&viewDummy); + { + LOCK(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(&viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { + const uint256& prevHash = txin.prevout.hash; + CCoins coins; + view.AccessCoins(prevHash); // this is certainly allowed to fail + } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (request.params.size() > 2 && !request.params[2].isNull()) { + fGivenKeys = true; + UniValue keys = request.params[2].get_array(); + for (unsigned int idx = 0; idx < keys.size(); idx++) { + UniValue k = keys[idx]; + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key = vchSecret.GetKey(); + if (!key.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + tempKeystore.AddKey(key); + } + } +#ifdef ENABLE_WALLET + else if (pwalletMain) + EnsureWalletIsUnlocked(); +#endif + + // Add previous txouts given in the RPC call: + if (request.params.size() > 1 && !request.params[1].isNull()) { + UniValue prevTxs = request.params[1].get_array(); + for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { + const UniValue& p = prevTxs[idx]; + if (!p.isObject()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); + + UniValue prevOut = p.get_obj(); + + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + }); + + uint256 txid = ParseHashO(prevOut, "txid"); + + int nOut = find_value(prevOut, "vout").get_int(); + if (nOut < 0) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); + + vector pkData(ParseHexO(prevOut, "scriptPubKey")); + CScript scriptPubKey(pkData.begin(), pkData.end()); + + { + CCoinsModifier coins = view.ModifyCoins(txid); + if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { + string err("Previous output scriptPubKey mismatch:\n"); + err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ + ScriptToAsmStr(scriptPubKey); + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); + } + if ((unsigned int)nOut >= coins->vout.size()) + coins->vout.resize(nOut+1); + coins->vout[nOut].scriptPubKey = scriptPubKey; + coins->vout[nOut].nValue = 0; + if (prevOut.exists("amount")) { + coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount")); + } + } + + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + {"redeemScript", UniValueType(UniValue::VSTR)}, + }); + UniValue v = find_value(prevOut, "redeemScript"); + if (!v.isNull()) { + vector rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } + } + } + } + +#ifdef ENABLE_WALLET + const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); +#else + const CKeyStore& keystore = tempKeystore; +#endif + + int nHashType = SIGHASH_ALL; + if (request.params.size() > 3 && !request.params[3].isNull()) { + static map mapSigHashValues = + boost::assign::map_list_of + (string("ALL"), int(SIGHASH_ALL)) + (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) + (string("NONE"), int(SIGHASH_NONE)) + (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) + (string("SINGLE"), int(SIGHASH_SINGLE)) + (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) + ; + string strHashType = request.params[3].get_str(); + if (mapSigHashValues.count(strHashType)) + nHashType = mapSigHashValues[strHashType]; + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); + } + + bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + + // Script verification errors + UniValue vErrors(UniValue::VARR); + + // Use CTransaction for the constant parts of the + // transaction to avoid rehashing. + const CTransaction txConst(mergedTx); + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { + CTxIn& txin = mergedTx.vin[i]; + const CCoins* coins = view.AccessCoins(txin.prevout.hash); + if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { + TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); + continue; + } + const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + const CAmount& amount = coins->vout[txin.prevout.n].nValue; + + SignatureData sigdata; + // Only sign SIGHASH_SINGLE if there's a corresponding output: + if (!fHashSingle || (i < mergedTx.vout.size())) + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); + + // ... and merge in other signatures: + BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { + if (txv.vin.size() > i) { + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); + } + } + + UpdateTransaction(mergedTx, i, sigdata); + + ScriptError serror = SCRIPT_ERR_OK; + if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } + } + bool fComplete = vErrors.empty(); + unsigned int nBytes = GetVirtualTransactionSize(mergedTx); + std::cout << " tx.size :" << nBytes << std::endl; + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(mergedTx))); + result.push_back(Pair("complete", fComplete)); + if (!vErrors.empty()) { + result.push_back(Pair("errors", vErrors)); + } + + return result; +} + +UniValue sendrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "sendrawtransaction \"hexstring\" ( allowhighfees )\n" + "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" + "\nAlso see createrawtransaction and signrawtransaction calls.\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" + "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" + "\nResult:\n" + "\"hex\" (string) The transaction hash in hex\n" + "\nExamples:\n" + "\nCreate a transaction\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + + "Sign the transaction, and get back the hex\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + "\nSend the transaction (signed hex)\n" + + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") + ); + + LOCK(cs_main); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); + + // parse hex string from parameter + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, request.params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + CTransactionRef tx(MakeTransactionRef(std::move(mtx))); + const uint256& hashTx = tx->GetHash(); +// + bool fLimitFree = false; + CAmount nMaxRawTxFee = maxTxFee; + if (request.params.size() > 1 && request.params[1].get_bool()) + nMaxRawTxFee = 0; + + CCoinsViewCache &view = *pcoinsTip; + const CCoins* existingCoins = view.AccessCoins(hashTx); + bool fHaveMempool = mempool.exists(hashTx); + bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) { + if (state.IsInvalid()) { + throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); + } else { + if (fMissingInputs) { + throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); + } + throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); + } + } + } else if (fHaveChain) { + throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); + } + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + CInv inv(MSG_TX, hashTx); + g_connman->ForEachNode([&inv](CNode* pnode) + { + pnode->PushInventory(inv); + }); + + return hashTx.GetHex(); +} + + +UniValue base58decodetohexstring(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "base58decodetohexstring \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + std::vector vch; + if (!DecodeBase58(request.params[0].get_str().c_str(), vch)) + throw JSONRPCError(RPC_INVALID_PARAMS, "Error: invalid Base58 Input String"); + std::string result = HexStr(vch.begin(), vch.end()); + return result; +} + +UniValue base58encodefromhexstring(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "base58encodefromhexstring \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + std::vector vch = ParseHex(request.params[0].get_str()); + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +UniValue gensystemaddress(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1) + throw runtime_error( + "gensystemaddress \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + //Public key plus prefix + std::vectorvch = ParseHex("035762" + request.params[0].get_str()); + //Do two sha256 + uint256 hash; + CSHA256().Write(&vch[0], vch.size()).Finalize(hash.begin()); + CSHA256().Write(hash.begin(), 32).Finalize(hash.begin()); + //Get the first four bytes attached + std::vector result = ParseHex("035762" + request.params[0].get_str() + "00000000"); + memcpy(&result[0] + 23, hash.begin(), 4); + std::string tmp = EncodeBase58(&result[0], &result[0] + result.size()); + return HexStr(result.begin(), result.end()) + "\n" + EncodeBase58(&result[0], &result[0] + result.size()); +} + +UniValue gensystemprivkey(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1) + throw runtime_error( + "base58encodehexstring \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + //Public key plus prefix + std::vectorvch = ParseHex("ef" + request.params[0].get_str()); + //Do two sha256 + uint256 hash; + CSHA256().Write(&vch[0], vch.size()).Finalize(hash.begin()); + CSHA256().Write(hash.begin(), 32).Finalize(hash.begin()); + //Get the first four bytes attached + std::vector result = ParseHex("ef" + request.params[0].get_str() + "00000000"); + memcpy(&result[0] + 34, hash.begin(), 4); + std::string tmp = EncodeBase58(&result[0], &result[0] + result.size()); + return HexStr(result.begin(), result.end()) + "\n" + EncodeBase58(&result[0], &result[0] + result.size()); +} + + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "getrawtransaction", &getrawtransaction, true, {"txid","verbose"} }, + { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","txtype"} }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} }, + { "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ + { "rawtransactions", "getfeeoftxid", &getfeeoftxid, true, { "txid" } }, + + { "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} }, + { "blockchain", "verifytxoutproof", &verifytxoutproof, true, {"proof"} }, + + + { "rawtransactions", "base58decodetohexstring", &base58decodetohexstring, true, {"hexstring"} }, + { "rawtransactions", "base58encodefromhexstring", &base58encodefromhexstring, true, {"hexstring"} }, + { "rawtransactions", "gensystemaddress", &gensystemaddress, true, {"hexstring"} }, + { "rawtransactions", "gensystemprivkey", &gensystemprivkey, true,{ "hexstring" } }, +}; + +void RegisterRawTransactionRPCCommands(CRPCTable &t) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + t.appendCommand(commands[vcidx].name, &commands[vcidx]); +} diff --git a/.svn/pristine/26/26d9e4bbeb411430ed6d3fd7ac90bd9654fd0a05.svn-base b/.svn/pristine/26/26d9e4bbeb411430ed6d3fd7ac90bd9654fd0a05.svn-base new file mode 100644 index 0000000..ae8ba5d --- /dev/null +++ b/.svn/pristine/26/26d9e4bbeb411430ed6d3fd7ac90bd9654fd0a05.svn-base @@ -0,0 +1,287 @@ +#include "tallyapply.h" +#include "ui_tallyapply.h" +#include "optionsmodel.h" +#include "ipchainunits.h" +#include "wallet/coincontrol.h" +#include "guiutil.h" +#include +#include "log/log.h" +#include "log/stateinfo.h" +#include "intro.h" + +TallyApply::TallyApply(QWidget *parent) : + QWidget(parent),model(NULL), + ui(new Ui::TallyApply) +{ + ui->setupUi(this); + ui->label_errmsg->setText(""); + QRegExp regx("[0-9]+$"); + QValidator *validator = new QRegExpValidator(regx, ui->coinEdit ); + ui->coinEdit->setValidator( validator ); + if(Intro::m_clienttype == "test") + { + ui->coinEdit->setText("20000"); + }else + { + ui->coinEdit->setText("20000"); + } + ui->coinEdit->setEnabled(false); +} +TallyApply::~TallyApply() +{ + delete ui; +} +void TallyApply::setModel(WalletModel *_model) +{ + this->model = _model; +} +void TallyApply::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn) +{ + QPalette pa; + pa.setColor(QPalette::WindowText,Qt::black); + switch(sendCoinsReturn.status) + { + case WalletModel::InvalidAddress: + LOG_WRITE(LOG_INFO,"tallyapply","InvalidAddress"); + ui->label_errmsg->setText(tr("InvalidAddress")); + break; + case WalletModel::InvalidAmount: + LOG_WRITE(LOG_INFO,"tallyapply","InvalidAmount"); + ui->label_errmsg->setText(tr("InvalidAmount")); + break; + case WalletModel::AmountExceedsBalance: + LOG_WRITE(LOG_INFO,"tallyapply","AmountExceedsBalance"); + ui->label_errmsg->setText(tr("AmountExceedsBalance")); + break; + case WalletModel::AmountWithFeeExceedsBalance: + LOG_WRITE(LOG_INFO,"tallyapply","AmountWithFeeExceedsBalance"); + ui->label_errmsg->setText(tr("AmountWithFeeExceedsBalance")); + break; + case WalletModel::DuplicateAddress: + LOG_WRITE(LOG_INFO,"tallyapply","DuplicateAddress"); + ui->label_errmsg->setText(tr("DuplicateAddress")); + break; + case WalletModel::TransactionCreationFailed: + LOG_WRITE(LOG_INFO,"tallyapply","TransactionCreationFailed"); + ui->label_errmsg->setText(tr("TransactionCreationFailed")+tr(" ")+ m_error.c_str()); + break; + case WalletModel::TransactionCommitFailed: + + LOG_WRITE(LOG_INFO,"tallyapply","TransactionCommitFailed",m_error.c_str()); + if(m_error == "txn-campaign-JOIN_PUBKEY_IS_TIMEOUT_PUNISHED") + { + ui->label_errmsg->setText(tr("TransactionCommitFailed")+tr(" ")+tr("txn-campaign-JOIN_PUBKEY_IS_TIMEOUT_PUNISHED")); + }else if(m_error == "txn-campaign-EXIT_PUBKEY_IS_DEPOSING") + { + ui->label_errmsg->setText(tr("TransactionCommitFailed")+tr(" ")+tr("txn-campaign-EXIT_PUBKEY_IS_DEPOSING")); + }else + ui->label_errmsg->setText(tr("TransactionCommitFailed")+tr(" ")+ m_error.c_str()); + break; + case WalletModel::AbsurdFee: + LOG_WRITE(LOG_INFO,"tallyapply","AbsurdFee"); + ui->label_errmsg->setText(tr("AbsurdFee")); + break; + case WalletModel::PaymentRequestExpired: + LOG_WRITE(LOG_INFO,"tallyapply","PaymentRequestExpired"); + ui->label_errmsg->setText(tr("PaymentRequestExpired")); + break; + case WalletModel::OK: + + STATE_CLEAR(); + STATE_WRITE("STATE = ON"); + + LOG_WRITE(LOG_INFO,"tallyapply","OK"); + ui->label_errmsg->setText(tr("")); + default: + LOG_WRITE(LOG_INFO,"tallyapply",\ + QString::number(sendCoinsReturn.status).toStdString().c_str()); + ui->label_errmsg->setText(tr("other error")); + return; + } +} +void TallyApply::on_pushButton_apply_pressed() +{ + + + m_error = ""; + if(!model || !model->getOptionsModel()) + { + return; + } + if(ui->coinEdit->text().isEmpty()) + { + ui->label_errmsg->setText(tr("money error")); + LOG_WRITE(LOG_INFO,"tallyapply","money error"); + return; + } + if(ui->pushButton_address->text().size()<20) + { + ui->label_errmsg->setText(tr("address error")); + LOG_WRITE(LOG_INFO,"tallyapply","address error"); + return; + } + + if(!model->m_bFinishedLoading){ + ui->label_errmsg->setText(tr("It's in sync. Please wait a moment.")); + return; + } + QList recipients; + SendCoinsRecipient recipient; + bool valid = true; + + if (recipient.paymentRequest.IsInitialized()){ + LOG_WRITE(LOG_INFO,"tallyapply","recipient.paymentRequest.IsInitialized()"); + return ; + } + QString t = ui->coinEdit->text(); + int64_t y = t.toInt(); + QString remainder_str; + QString quotient_str = QString::number(y); + remainder_str = QString::number(y)+"00000000"; + + recipient.address = ui->pushButton_address->text(); + recipient.label = "";//biaoqian + recipient.amount = remainder_str.toDouble(); + recipient.message = ""; + recipient.fSubtractFeeFromAmount = true; + recipients.append(recipient); + + QString add1 = ui->pushButton_address->text(); + CAmount amount1 = remainder_str.toDouble(); ; + bool was_locked = model->getEncryptionStatus() == WalletModel::Locked; + if(!model->CheckPassword()) + { + LOG_WRITE(LOG_INFO,"tallyapply","psd error"); + ui->label_errmsg->setText(tr("password error")); + return ; + } + + // If wallet is still locked, unlock was failed or cancelled, mark context as invalid + valid = model->getEncryptionStatus() != WalletModel::Locked; + //ctx(this, valid, was_locked); + WalletModel::UnlockContext ctx(model, valid, was_locked); + if(!ctx.isValid()) + { + // Unlock wallet was cancelled + ui->label_errmsg->setText(tr("Unlock wallet was cancelled")); + LOG_WRITE(LOG_INFO,"tallyapply","Unlock wallet was cancelled"); + return; + } + + // prepare transaction for getting txFee earlier + WalletModelTransaction currentTransaction(recipients); + WalletModel::SendCoinsReturn prepareStatus; + // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled + CCoinControl ctrl; + if (model->getOptionsModel()->getCoinControlFeatures()) + { + //ctrl = *CoinControlDialog::coinControl; + } + else + { + ctrl.nConfirmTarget = 1; + } + LOG_WRITE(LOG_INFO,"tallyapply prepareBookkeepingTransaction",\ + ui->pushButton_address->text().toStdString().c_str(),\ + remainder_str.toStdString().c_str() + ); + prepareStatus = model->prepareBookkeepingTransaction(add1,amount1,m_error,currentTransaction, &ctrl); + if(prepareStatus.status != WalletModel::OK) { + processSendCoinsReturn(prepareStatus); + return; + } + CAmount txFee = currentTransaction.getTransactionFee(); + // Format confirmation message + QStringList formatted; + Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients()) + { + QString amount = "" + BitcoinUnits::formatHtmlWithUnit(0, rcp.amount); + amount.append(""); + // generate monospace address string + QString address = "" + rcp.address; + address.append(""); + QString recipientElement; + QString q = QString("%1").arg(rcp.amount); + if (!rcp.paymentRequest.IsInitialized()) // normal payment + { + if(rcp.label.length() > 0) // label with address + { + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); + recipientElement.append(QString(" (%1)").arg(address)); + } + else // just address + { + recipientElement = tr("%1 to %2").arg(amount, address); + } + } + else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request + { + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); + } + else // unauthenticated payment request + { + recipientElement = tr("%1 to %2").arg(amount, address); + } + + formatted.append(recipientElement); + } + + QString questionString = tr("Are you sure you want to send?"); + questionString.append("

%1"); + + if(txFee > 0) + { + // append fee string if a fee is required + questionString.append("
"); + questionString.append(BitcoinUnits::formatHtmlWithUnit(0, txFee)); + questionString.append(" "); + questionString.append(tr("added as transaction fee")); + questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)"); + } + + // add total amount in all subdivision units + questionString.append("
"); + CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; + QStringList alternativeUnits; + Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) + { + alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(0, totalAmount)); + } + + questionString.append(tr("Total Amount %1") + .arg(BitcoinUnits::formatHtmlWithUnit(0, totalAmount))); + + + questionString.append(QString("
(=%2)
") + .arg(alternativeUnits.join(" " + tr("or") + "
"))); + + + WalletModel::SendCoinsReturn sendStatus =model->sendBookkeeping(add1,currentTransaction); + if (sendStatus.status == WalletModel::OK) + { + Q_EMIT next(add1,amount1); + } + else{ + m_error = model->m_sendcoinerror; + } + processSendCoinsReturn(sendStatus); + +} +void TallyApply::setAddress(QString address) +{ + ui->pushButton_address->setText(address); + QString strDeposit = model->getDeposit(address); + ui->coinEdit->setText(strDeposit); +} + +void TallyApply::on_pushButton_address_pressed() +{ + Q_EMIT selectaddress(); +} +void TallyApply::resetinfo() +{ + ui->coinEdit->setText("20000"); + ui->label_errmsg->setText(""); + ui->pushButton_address->setText(tr("select address")); +} + diff --git a/.svn/pristine/2b/2b485e6b602d535c70e2d941ef2de74a8f77e468.svn-base b/.svn/pristine/2b/2b485e6b602d535c70e2d941ef2de74a8f77e468.svn-base new file mode 100644 index 0000000..85b92d3 --- /dev/null +++ b/.svn/pristine/2b/2b485e6b602d535c70e2d941ef2de74a8f77e468.svn-base @@ -0,0 +1,170 @@ +#include "tallyoutaccount.h" +#include "forms/ui_tallyoutaccount.h" +#include "optionsmodel.h" +#include "ipchainunits.h" +#include "wallet/coincontrol.h" +#include "guiutil.h" +#include "log/log.h" +#include "log/stateinfo.h" +TallyOutAccount::TallyOutAccount(QWidget *parent) : + QWidget(parent), + ui(new Ui::TallyOutAccount) +{ + ui->setupUi(this); + ui->label_errmsg->setText(""); + +} + +TallyOutAccount::~TallyOutAccount() +{ + delete ui; +} +void TallyOutAccount::setModel(WalletModel * model) +{ + walletmodel = model; +} +void TallyOutAccount::on_pushButton_Cancle_pressed() +{ + Q_EMIT next(false); +} +void TallyOutAccount::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) +{ + QPalette pa; + pa.setColor(QPalette::WindowText,Qt::black); + + switch(sendCoinsReturn.status) + { + case WalletModel::InvalidAddress: + LOG_WRITE(LOG_INFO,"TallyOutAccount","InvalidAddress"); + ui->label_errmsg->setText(tr("InvalidAddress")); + break; + case WalletModel::InvalidAmount: + LOG_WRITE(LOG_INFO,"TallyOutAccount","InvalidAmount"); + ui->label_errmsg->setText(tr("InvalidAmount")); + break; + case WalletModel::AmountExceedsBalance: + LOG_WRITE(LOG_INFO,"TallyOutAccount","AmountExceedsBalance"); + ui->label_errmsg->setText(tr("AmountExceedsBalance")); + break; + case WalletModel::AmountWithFeeExceedsBalance: + LOG_WRITE(LOG_INFO,"TallyOutAccount","AmountWithFeeExceedsBalance"); + ui->label_errmsg->setText(tr("AmountWithFeeExceedsBalance")); + break; + case WalletModel::DuplicateAddress: + LOG_WRITE(LOG_INFO,"TallyOutAccount","DuplicateAddress"); + ui->label_errmsg->setText(tr("DuplicateAddress")); + break; + case WalletModel::TransactionCreationFailed: + LOG_WRITE(LOG_INFO,"TallyOutAccount","TransactionCreationFailed",m_error.c_str()); + if(!setlabel_errmsg()) + ui->label_errmsg->setText(tr("TransactionCreationFailed") + tr(" ") + m_error.c_str()); + break; + case WalletModel::TransactionCommitFailed: + LOG_WRITE(LOG_INFO,"TallyOutAccount","TransactionCommitFailed",m_error.c_str()); + if(!setlabel_errmsg()) + ui->label_errmsg->setText(tr("TransactionCommitFailed") + tr(" ") + m_error.c_str()); + break; + case WalletModel::AbsurdFee: + LOG_WRITE(LOG_INFO,"TallyOutAccount","AbsurdFee"); + ui->label_errmsg->setText(tr("AbsurdFee")); + break; + case WalletModel::PaymentRequestExpired: + LOG_WRITE(LOG_INFO,"TallyOutAccount","PaymentRequestExpired"); + + ui->label_errmsg->setText(tr("PaymentRequestExpired")); + break; + case WalletModel::OK: + LOG_WRITE(LOG_INFO,"TallyOutAccount","OK"); + ui->label_errmsg->setText(tr("")); + default: + LOG_WRITE(LOG_INFO,"TallyOutAccount",\ + QString::number(sendCoinsReturn.status).toStdString().c_str()); + ui->label_errmsg->setText(tr("other error")); + return; + } +} +void TallyOutAccount::setinfo( WalletModel::keepupaccountInfo info) +{ + info_.Add_=info.Add_; + info_.Coin_=info.Coin_; + clearinfo(); +} +WalletModel::keepupaccountInfo TallyOutAccount::getinfo() +{ + return info_; +} +void TallyOutAccount::on_pushButton_OK_pressed() +{ + m_error = ""; + if(!walletmodel || !walletmodel->getOptionsModel()) + return; + if(!walletmodel->CheckPassword()) + { + ui->label_errmsg->setText(tr("password error")); + return; + } + WalletModel::UnlockContext ctx(walletmodel, true, true); + WalletModel::SendCoinsReturn prepareStatus; + CCoinControl ctrl; + if (walletmodel->getOptionsModel()->getCoinControlFeatures()) + { + // ctrl = *CoinControlDialog::coinControl; + } + else + { + ctrl.nConfirmTarget = 1; + } + prepareStatus = walletmodel->prepareExitBookkeeping( &ctrl,m_error); + processSendCoinsReturn(prepareStatus); + if(prepareStatus.status != WalletModel::OK) { + fNewRecipientAllowed = true; + return; + } + else + { + + STATE_CLEAR(); + STATE_WRITE("STATE = OFF"); + fNewRecipientAllowed = true; + Q_EMIT next(true); + } +} +void TallyOutAccount::clearinfo() +{ + ui->label_errmsg->setText(""); +} +bool TallyOutAccount::setlabel_errmsg() +{ + if(m_error == "Not join the campaign yet"){ + ui->label_errmsg->setText(tr("Not join the campaign yet")); + }else if(m_error == "Transaction amounts must not be negative"){ + ui->label_errmsg->setText(tr("Transaction amounts must not be negative")); + }else if(m_error == "Transaction must have at least one recipient"){ + ui->label_errmsg->setText(tr("Transaction must have at least one recipient")); + }else if(m_error == "ExitCampaign The Address which you want to ExitCampaign must have some money!"){ + ui->label_errmsg->setText(tr("ExitCampaign The Address which you want to ExitCampaign must have some money!")); + }else if(m_error == "The Address which you want to ExitCampaign must have some money!"){ + ui->label_errmsg->setText(tr("The Address which you want to ExitCampaign must have some money!")); + }else if(m_error == "Transaction amount too small"){ + ui->label_errmsg->setText(tr("Transaction amount too small")); + }else if(m_error == "Insufficient funds"){ + ui->label_errmsg->setText(tr("Insufficient funds")); + }else if(m_error == "Keypool ran out, please call keypoolrefill first"){ + ui->label_errmsg->setText(tr("Keypool ran out, please call keypoolrefill first")); + }else if(m_error == "The transaction amount is too small to send after the fee has been deducted"){ + ui->label_errmsg->setText(tr("The transaction amount is too small to send after the fee has been deducted")); + }else if(m_error == "Signing transaction failed"){ + ui->label_errmsg->setText(tr("Signing transaction failed")); + } else if(m_error == "Transaction too large for fee policy"){ + ui->label_errmsg->setText(tr("Transaction too large for fee policy")); + } else if(m_error == "Transaction too large"){ + ui->label_errmsg->setText(tr("Transaction too large")); + } else if(m_error == "Transaction has too long of a mempool chai"){ + ui->label_errmsg->setText(tr("Transaction has too long of a mempool chai")); + } else if(m_error == "txn-campaign-EXIT_PUBKEY_NOT_EXIST_IN_LIST"){ + ui->label_errmsg->setText(tr("txn-campaign-EXIT_PUBKEY_NOT_EXIST_IN_LIST")); + }else{ + return false; + } + return true; +} diff --git a/.svn/pristine/2f/2f250d8b444b841d3b6600e17b2de9ac5a1c173c.svn-base b/.svn/pristine/2f/2f250d8b444b841d3b6600e17b2de9ac5a1c173c.svn-base new file mode 100644 index 0000000..3bea0fc --- /dev/null +++ b/.svn/pristine/2f/2f250d8b444b841d3b6600e17b2de9ac5a1c173c.svn-base @@ -0,0 +1,385 @@ +// Copyright (c) 2011-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "walletframe.h" + +#include "ipchaingui.h" +#include "walletview.h" +#include "settingwidget.h" +#include "sendcoinsaffrimwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include "sendresultwidget.h" +#include "addresstablemodel.h" +#include "infowidget.h" +#include "sendhistory.h" +#include "recvhistory.h" +#include "sendipchistory.h" +#include "recvipchistory.h" +#include "sendipchistory.h" +#include "recvtokenhistory.h" +#include "sendtokenhistory.h" +#include "walletpagebuttons.h" +#include "log/log.h" +#include "transactiontablemodel.h" +#include "log/log.h" +WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui) : + QFrame(_gui), + gui(_gui), + platformStyle(_platformStyle) +{ + // Leave VBox hook for adding a list view later + QVBoxLayout *walletFrameLayout = new QVBoxLayout(this); + setContentsMargins(0,0,0,0); + walletStack = new QStackedWidget(this); + walletStack->setContentsMargins(0,0,0,0); + // walletStack->setStyleSheet("background-color:rgb(255,255,255)"); + QPalette pal(walletStack->palette()); + pal.setColor(QPalette::Background, Qt::white); + walletStack->setAutoFillBackground(true); + //walletStack->setPalette(pal); + walletFrameLayout->setContentsMargins(0,0,0,0); + walletpagebuttonswidget = new walletpagebuttons(this); + connect(walletpagebuttonswidget,SIGNAL(walletpressed()),this,SLOT(gotoOverviewPage())); + connect(walletpagebuttonswidget,SIGNAL(sendpressed()),this,SLOT(gotoSendCoinsPage())); + connect(walletpagebuttonswidget,SIGNAL(recivepressed()),this,SLOT(gotoReceiveCoinsPage())); + connect(walletpagebuttonswidget,SIGNAL(ipcpressed()),this,SLOT(gotoIpcPage())); + connect(walletpagebuttonswidget,SIGNAL(setpressed()),this,SLOT(gotoSetPage())); + connect(walletpagebuttonswidget,SIGNAL(tallypressed()),this,SLOT(gotoTallyPage())); + connect(walletpagebuttonswidget,SIGNAL(eipcpressed()),this,SLOT(gotoeCoinPage())); + + QVBoxLayout *walletLayout = new QVBoxLayout(this); + walletLayout->setContentsMargins(0,0,0,0); + walletLayout->addWidget(walletpagebuttonswidget); + + pwalletStackwidget = new QWidget(this); + pwalletStackwidget->setLayout(walletLayout); + walletStackBranchPage = new QStackedWidget(this); + walletStackBranchPage->addWidget(pwalletStackwidget); + walletLayout->addWidget(walletStack); + walletStackBranchPage->setCurrentWidget(pwalletStackwidget); + + walletFrameLayout->addWidget(walletStackBranchPage); + QLabel *noWallet = new QLabel(tr("No wallet has been loaded.")); + noWallet->setAlignment(Qt::AlignCenter); + walletStack->addWidget(noWallet); + +} + +WalletFrame::~WalletFrame() +{ + delete sendpage; + + delete recvpage ; + + delete sendipcpage; + + delete recvipcpage; + + delete sendtokenpage ; + delete recvtokenpage; + if(pollTimer) + { + sel =QModelIndex(); + selid = -1; + pollTimer->stop(); + delete pollTimer; + } + +} +void WalletFrame::headshowchaininfo(int concount,int count,bool header) +{ + if (!header) + { + walletpagebuttonswidget->fresh(concount,count); + } +} + + +QStackedWidget* WalletFrame::getwaletstack() +{ + return walletStackBranchPage; +} + +void WalletFrame::setClientModel(ClientModel *_clientModel) +{ + this->clientModel = _clientModel; +} + +bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) +{ + if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0) + return false; + + WalletView *walletView = new WalletView(platformStyle, this); + walletView->setBitcoinGUI(gui); + walletView->setClientModel(clientModel); + + this->walletmd = walletModel; + this->clientmd = clientModel; + walletView->gotoOverviewPage(); + walletStack->addWidget(walletView); + walletStack->setCurrentWidget(walletView); + mapWalletViews[name] = walletView; + walletView->setWalletModel(walletModel); + // Ensure a walletView is able to show the main window + connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); + + connect(walletView, SIGNAL(outOfSyncWarningClicked()), this, SLOT(outOfSyncWarningClicked())); + + return true; +} + +bool WalletFrame::setCurrentWallet(const QString& name) +{ + if (mapWalletViews.count(name) == 0) + return false; + + WalletView *walletView = mapWalletViews.value(name); + walletStack->setCurrentWidget(walletView); + walletView->updateEncryptionStatus(); + return true; +} + +bool WalletFrame::removeWallet(const QString &name) +{ + if (mapWalletViews.count(name) == 0) + return false; + + WalletView *walletView = mapWalletViews.take(name); + walletStack->removeWidget(walletView); + return true; +} + +void WalletFrame::removeAllWallets() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + walletStack->removeWidget(i.value()); + mapWalletViews.clear(); +} + +void WalletFrame::gotoOverviewPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoOverviewPage(); + +} + +void WalletFrame::gotoIpcPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoIpcPage(); +} + +void WalletFrame::gotoSetPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoSetPage(); +} +void WalletFrame::gotoTallyPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoTallyPage(); +} +void WalletFrame::gotoeCoinPage(){ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoeCoinPage(); + +} +void WalletFrame::GoToChainInfoPage() +{ + Q_EMIT requestoverwidget(); + +} + +void WalletFrame::StatusInfoUpdate() +{ + if(!clientmd) return; + if(!walletmd) return; + + if(sel.isValid())// && sel.data().isValid() && !sel.data().isNull()) + { + if(TAB_sendipc == selid) + { + if(sendipcpage) + { + QString m_status = sel.data(TransactionTableModel::InfoStatus).toString(); + + sendipcpage->updateInfo(m_status); + } + } + + else if(TAB_recvipc == selid)//recvipc + { + if(recvipcpage) + { + QString m_status = sel.data(TransactionTableModel::InfoStatus).toString(); + + recvipcpage->updateInfo(m_status); + } + } + + else if(TAB_sendtoken == selid) + { + if(sendtokenpage) + { + QString m_status = sel.data(TransactionTableModel::InfoStatus).toString(); + + sendtokenpage->updateInfo(m_status); + } + } + else if(TAB_recvtoken == selid) + { + if(recvtokenpage) + { + QString m_status = sel.data(TransactionTableModel::InfoStatus).toString(); + + recvtokenpage->updateInfo(m_status); + } + } + else if(TAB_send ==selid) + { + if(sendpage) + { + QString m_status = sel.data(TransactionTableModel::InfoStatus).toString(); + + sendpage->updateInfo(m_status); + } + } + else if(TAB_recv ==selid) + { + if(recvpage) + { + QString m_status = sel.data(TransactionTableModel::InfoStatus).toString(); + + recvpage->updateInfo(m_status); + } + } + else if(-1 == selid) + { + + + return; + } + + + + } +} +void WalletFrame::showwwDetails(QModelIndex index,quint8 t) +{ + + if(pollTimer) + { + sel =QModelIndex(); + selid = -1; + pollTimer->stop(); + delete pollTimer; + + } + WalletView *walletView = currentWalletView(); + sel = index; + selid = t; + if(TAB_sendipc == t)//sendipc + { + QModelIndex selection = index; + sendipcpage = new sendipchistory(selection); + walletView->opensendipcpage(sendipcpage); + + } + + else if(TAB_recvipc == t)//recvipc + { + QModelIndex selection = index; + recvipcpage = new recvipchistory(selection); + walletView->openrecvipcpage(recvipcpage); + } + + else if(TAB_sendtoken == t) + { + QModelIndex selection = index; + sendtokenpage = new SendTokenHistory(selection); + walletView->opensendtokenpage(sendtokenpage); + } + else if(TAB_recvtoken== t) + { + QModelIndex selection = index; + recvtokenpage = new RecvTokenHistory(selection); + walletView->openrecvtokenpage(recvtokenpage); + } + else if(TAB_send ==t) + { + QModelIndex selection = index; + sendpage = new sendhistory(selection); + walletView->opensendpage(sendpage); + } + else if(TAB_recv ==t) + { + QModelIndex selection = index; + recvpage = new RecvHistory(selection); + walletView->openrecvpage(recvpage); + } + + pollTimer = new QTimer(this); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(StatusInfoUpdate())); + pollTimer->start(10000); +} +void WalletFrame::gotoReceiveCoinsPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoReceiveCoinsPage(); +} + +void WalletFrame::gotoSendCoinsPage(QString addr) +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoSendCoinsPage(addr); +} + + + +void WalletFrame::encryptWallet(bool status) +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->encryptWallet(status); +} + +void WalletFrame::backupWallet() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->backupWallet(); +} + +void WalletFrame::unlockWallet() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->unlockWallet(); +} +WalletView *WalletFrame::currentWalletView() +{ + return qobject_cast(walletStack->currentWidget()); +} + +void WalletFrame::outOfSyncWarningClicked() +{ + Q_EMIT requestedSyncWarningInfo(); +} + + diff --git a/.svn/pristine/33/33424596c61aa2dafcc8b401bdd48133efcaa946.svn-base b/.svn/pristine/33/33424596c61aa2dafcc8b401bdd48133efcaa946.svn-base new file mode 100644 index 0000000..a7862c1 --- /dev/null +++ b/.svn/pristine/33/33424596c61aa2dafcc8b401bdd48133efcaa946.svn-base @@ -0,0 +1,1805 @@ +#include "transactiontablemodel.h" +#include "addresstablemodel.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "platformstyle.h" +#include "transactionrecord.h" +#include "walletmodel.h" +#include "core_io.h" +#include "validation.h" +#include "sync.h" +#include "uint256.h" +#include "util.h" +#include "wallet/wallet.h" +#include "log/log.h" +#include +#include +#include +#include +#include +#include + + +#include +#include "util.h" +#include "intro.h" +#include +#include +#include + + +extern bool isfullloaded; +//extern bool m_IsRecover; +// Amount column is right-aligned it contains numbers +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, /* status */ + Qt::AlignLeft|Qt::AlignVCenter, /* watchonly */ + Qt::AlignLeft|Qt::AlignVCenter, /* date */ + Qt::AlignLeft|Qt::AlignVCenter, /* type */ + Qt::AlignLeft|Qt::AlignVCenter, /* address */ + Qt::AlignRight|Qt::AlignVCenter /* amount */ +}; +typedef pair PAIR; +// Comparison operator for sort/binary search of model tx list +struct TxLessThan +{ + bool operator()(const TransactionRecord &a, const TransactionRecord &b) const + { + return a.hash < b.hash; + } + bool operator()(const TransactionRecord &a, const uint256 &b) const + { + return a.hash < b; + } + bool operator()(const uint256 &a, const TransactionRecord &b) const + { + return a < b.hash; + } +}; + +class TransactionTablePriv +{ +public: + TransactionTablePriv(CWallet *_wallet, TransactionTableModel *_parent) : + wallet(_wallet), + parent(_parent) + { + } + + CWallet *wallet; + TransactionTableModel *parent; + + /* Local cache of wallet. + * As it is in the same order as the CWallet, by definition + * this is sorted by sha256. + */ + QList cachedWallet; + + + QList hashidWalletvec; + + QList sdlist; + QList rdlist; + + QList recoversdlist; + QList recoverinsertsdlist; + QList recoverinsertrdlist; + + QList lastsdlist; + QList lastrdlist; + QList mergelist; + QList insertsdlist; + QList insertrdlist; + + + multimap mulMapsdlist; + multimap mulMaprdlist; + multimap mulMaplastsdlist; + multimap mulMaplastrdlist; + + + multimap mulMaplastdellist; + + int insert_idx; + int insert_idy; + int merge_idy; + int insert_nidx; + int insert_nidy; + + void resizeList(QList & list, int newSize) { + + int diff = newSize - list.size(); + TransactionRecord t; + if (diff > 0) { + } + else if(0 == diff) + { + + } + else if (diff < 0) + { + list.erase(list.begin() , list.begin()-diff); + } + } + + + static bool cmp_by_value(const PAIR& lhs, const PAIR& rhs) { + if(lhs.first==rhs.first) + return lhs.second.timerhs.first; + } + + static bool compareBarData(const TransactionRecord &barAmount1, const TransactionRecord &barAmount2) + { + if (barAmount1.time < barAmount2.time) + { + return true; + } + return false; + } + void mulMaprelistit() + { + qSort(mergelist.begin(), mergelist.end(), compareBarData); + + for(int i = 0;i(a.address,a)); + } + if(TransactionRecord::Type::Senddeposit== mergelist.at(i).type) + { + TransactionRecord b = mergelist.at(i); + LOG_WRITE(LOG_INFO,"mergelists",b.address.c_str(),b.strtime.toStdString().c_str(),QString::number(b.credit).toStdString().c_str()); + + mulMapsdlist.insert(pair(b.address,b)); + } + + + } + vector mul_rdlist_vec(mulMaprdlist.begin(), mulMaprdlist.end()); + sort(mul_rdlist_vec.begin(), mul_rdlist_vec.end(), cmp_by_value); + vector mul_sdlist_vec(mulMapsdlist.begin(), mulMapsdlist.end()); + sort(mul_sdlist_vec.begin(), mul_sdlist_vec.end(), cmp_by_value); + mulMaprdlist.clear(); + mulMapsdlist.clear(); + for(int i = 0;i(mul_rdlist_vec[i].first, mul_rdlist_vec[i].second)); + } + for(int i = 0;i(mul_sdlist_vec[i].first, mul_sdlist_vec[i].second)); + + } + + multimap::iterator ir,irend; + irend=mulMaprdlist.end(); + + multimap::iterator is,isend; + isend=mulMapsdlist.end(); + is=mulMapsdlist.begin(); + for(ir=mulMaprdlist.begin();ir!=irend;ir++) + { + + if(ir== mulMaprdlist.end() || is== mulMapsdlist.end()) + { + LOG_WRITE(LOG_INFO,"no happen"); + break; + } + + if((*ir).first==(*is).first) + { + (*ir).second.credit=-(*is).second.credit; + is++; + } + else + { + //mulMaprecoversdlist.insert(is.first,is.second); + recoversdlist.append((*is).second); + is++; + ir--; + + } + + } + if(mulMaprdlist.size()::iterator ir=mulMaprdlist.begin();ir!=mulMaprdlist.end();ir++) + { + + rdlist.append((*ir).second); + + } + + for(int i=0;i(lastrdlist.at(i).address,b)); + } + + for(int i = 0;i(lastsdlist.at(i).address,lastsdlist.at(i))); + } + vector mul_lastrdlist_vec(mulMaplastrdlist.begin(), mulMaplastrdlist.end()); + sort(mul_lastrdlist_vec.begin(), mul_lastrdlist_vec.end(), cmp_by_value); + + + vector mul_lastsdlist_vec(mulMaplastsdlist.begin(), mulMaplastsdlist.end()); + sort(mul_lastsdlist_vec.begin(), mul_lastsdlist_vec.end(), cmp_by_value); + mulMaplastsdlist.clear(); + mulMaplastrdlist.clear(); + for(int i = 0;i(mul_lastsdlist_vec[i].first, mul_lastsdlist_vec[i].second)); + // LOG_WRITE(LOG_INFO,"mul_lastSdlist_vecs",mul_lastsdlist_vec[i].first.c_str(),mul_lastsdlist_vec[i].second.strtime.toStdString().c_str()); + } + LOG_WRITE(LOG_INFO,"mulMaplastrsdlistsize",QString::number(mulMaplastsdlist.size()).toStdString().c_str(),QString::number(mulMaplastrdlist.size()).toStdString().c_str()); + mul_lastsdlist_vec.clear(); + mul_lastrdlist_vec.clear(); + + multimap::iterator iir,iirend; + iirend=mulMaplastrdlist.end(); + iir=mulMaplastrdlist.begin(); + + multimap::iterator iis,iisend; + iisend=mulMaplastsdlist.end(); + iis=mulMaplastsdlist.begin(); + + + for(iir;iir!=iirend;iir) + { + if(iir== mulMaplastrdlist.end() || iis== mulMaplastsdlist.end()) + { + LOG_WRITE(LOG_INFO,"no -happen"); + break; + } + if((*iis).first==(*iir).first) + { + (*iir).second.credit=0; + (*iir).second.credit=-(*iis).second.credit; + + mulMaplastdellist.insert(pair((*iis).first,(*iis).second)); + iis++; + iir++; + } + else + { + + iis++; + // iir--; + } + + } + LOG_WRITE(LOG_INFO,"G-mulMaplastdellist",QString::number(mulMaplastdellist.size()).toStdString().c_str()); + for(multimap::iterator irt =mulMaplastdellist.begin();irt!=mulMaplastdellist.end();irt++) + { + + + for(int i=0;i::iterator irt=mulMaplastrdlist.begin();irt!=mulMaplastrdlist.end();irt++)//mul_lastrdlist_vec + { + + lastrdlist.append((*irt).second); + + } + + mulMaplastrdlist.clear(); + mulMaplastsdlist.clear(); + + + + for(int i=0;i0) + { + recoversdlist.removeAt(0); + } + if(recoversdlist.size()==0&&recoverinsertsdlist.size()!=0) + { + + recoverinsertsdlist.removeAt(0); + + } + } + } + + } + + void handlenit() + { + qSort(insertsdlist.begin(), insertsdlist.end(), compareBarData); + qSort(insertrdlist.begin(), insertrdlist.end(), compareBarData); + + + QMutableListIterator i(insertrdlist); + QMutableListIterator j(insertsdlist); + + int min1 = std::min(insertrdlist.size(),insertsdlist.size()) ; + for(int i = 0;i< min1;i++) + { + + if(insertsdlist.size() != 0 && insertrdlist.size() != 0) + { + TransactionRecord a = insertsdlist.at(i); + TransactionRecord b = insertrdlist.at(i); + + TransactionRecord c = b; + + b.credit = -a.credit; + insertrdlist.replace(i,b); + } + } + + } + + + + void relistit() + { + + qSort(mergelist.begin(), mergelist.end(), compareBarData); + + for(int i = 0;imergelist.size()) + { + break; + } + + TransactionRecord a = mergelist.at(i); + TransactionRecord b = mergelist.at(i+1); + + if(a.time == b.time) + { + mergelist.removeAt(i+1); + i--; + } + } + + + { + + for(int i = 0;i i(rdlist); + QMutableListIterator j(sdlist); + + int min_ = std::min(rdlist.size(),sdlist.size()); + for(int i = 0;i< min_;i++) + { + if(rdlist.size() != 0 && sdlist.size() != 0 ) + { + TransactionRecord a ,b; + int t=sdlist.size()-rdlist.size(); + if( sdlist.at(0).time > rdlist.at(0).time && t>=1) + { + + a = sdlist.at(i+1); + b = rdlist.at(i); + + } + else + { + a = sdlist.at(i); + b = rdlist.at(i); + } + TransactionRecord c = b; + + b.credit = - a.credit; + + rdlist.replace(i,b); + } + } + + } + void refreshWallet() + { + qDebug() << "TransactionTablePriv::refreshWallet"; + time_t startTime = time(NULL); + cachedWallet.clear(); + hashidWalletvec.clear(); + sdlist.clear(); + rdlist.clear(); + mulMapsdlist.clear(); + mulMaprdlist.clear(); + mergelist.clear(); + { + LOCK2(cs_main, wallet->cs_wallet); + + LOG_WRITE(LOG_INFO,"mapwallet size",QString::number(wallet->mapWallet.size()).toStdString().c_str()); + + for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) + { + if(TransactionRecord::showTransaction(it->second)) + { + // if("fb4d82255f12ab67ac97b3fd37c2827af71e55e4420c31dc81f8a1c79353b03e" == it->second.GetHash().ToString().c_str()) + { + cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); + } + } + } + + Q_FOREACH(const TransactionRecord &rec, cachedWallet) + { + if(TransactionRecord::Type::Senddeposit == rec.type || TransactionRecord::Type::Recvdeposit == rec.type) + { + + mergelist.insert(merge_idy, rec); + merge_idy += 1; + } + + + } + + for(int i = 0;i::iterator lower = qLowerBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + QList::iterator upper = qUpperBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + int lowerIndex = (lower - cachedWallet.begin()); + int upperIndex = (upper - cachedWallet.begin()); + bool inModel = (lower != upper); + if(status == CT_UPDATED) + { + if(showTransaction && !inModel) + status = CT_NEW; /* Not in model, but want to show, treat as new */ + if(!showTransaction && inModel) + status = CT_DELETED; /* In model, but want to hide, treat as deleted */ + } + + qDebug() << " inModel=" + QString::number(inModel) + + " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) + + " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status); + + + switch(status) + { + case CT_NEW: + if(inModel) + { + qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is already in model"; + break; + } + + if(showTransaction) + { + + time_t startTime = time(NULL); + LOCK2(cs_main, wallet->cs_wallet); + // Find transaction in wallet + std::map::iterator mi = wallet->mapWallet.find(hash); + if(mi == wallet->mapWallet.end()) + { + qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet"; + break; + } + // Added -- insert at the right position + + QList toInsert; + toInsert.clear(); + + + toInsert =TransactionRecord::decomposeTransaction(wallet, mi->second); + + Q_FOREACH( const TransactionRecord &rec , toInsert){ + bool findFlag = false; + LOG_WRITE(LOG_INFO,"new tra",QString::number(cachedWallet.size()).toStdString().c_str(),rec.address.c_str(),QString::number(rec.credit).toStdString().c_str(),rec.hash.ToString().c_str()); + TransactionRecord y = rec; + string t = y.hash.ToString().c_str(); + BOOST_FOREACH(const TransactionRecord &cacheData, cachedWallet) + { + if(cacheData.hash == rec.hash ){ + findFlag = true; + break; + } + + } + + for(int i = 0 ;ibeginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); + int insert_idx = lowerIndex; + Q_FOREACH(const TransactionRecord &rec, toInsert) + { + TransactionRecord w =rec; + if( 0 == rec.credit && 0 == rec.debit) + { + if(rec.type == TransactionRecord::Type::RecvIPC || rec.type == TransactionRecord::Type::RecveCoin) + { + } + else + { + + + break; + } + } + int idx = 0; + bool findFlag = false; + BOOST_FOREACH(const TransactionRecord &cacheData, cachedWallet) + { + if(cacheData.hash == rec.hash ){ + findFlag = true; + break; + } + } + + for(int i = 0 ;i1) + { + LOG_WRITE(LOG_INFO,"12345678900987654321",QString::number(toInsert.size()).toStdString().c_str()); + } + if(true == findFlag && toInsert.size()<2) + { + + break; + } + ++ idx; + + if(TransactionRecord::Type::Senddeposit== rec.type || TransactionRecord::Type::Recvdeposit== rec.type ) + { + hashidWalletvec.append(rec.hash); + } + cachedWallet.append(rec); + + } + parent->endInsertRows(); + + if(cachedWallet.size()>100 ) + { + + parent->beginResetModel(); + qSort(cachedWallet.begin(), cachedWallet.end(), compareBarData); + + resizeList(cachedWallet,100); + parent->endResetModel(); + + + } + else + { + parent->beginResetModel(); + qSort(cachedWallet.begin(), cachedWallet.end(), compareBarData); + parent->endResetModel(); + } + + } + } + break; + case CT_DELETED: + if(!inModel) + { + qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_DELETED, but transaction is not in model"; + break; + } + // Removed -- remove entire transaction from table + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedWallet.erase(lower, upper); + parent->endRemoveRows(); + break; + case CT_UPDATED: + // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for + // visible transactions. + break; + } + + + } + + int size() + { + return cachedWallet.size(); + } + + TransactionRecord *index(int idx) + { + + if(idx >= 0 && idx < cachedWallet.size()) + { + TransactionRecord *rec = &cachedWallet[idx]; + + // Get required locks upfront. This avoids the GUI from getting + // stuck if the core is holding the locks for a longer time - for + // example, during a wallet rescan. + // + // If a status update is needed (blocks came in since last check), + // update the status of this transaction from the wallet. Otherwise, + // simply re-use the cached status. + TRY_LOCK(cs_main, lockMain); + if(lockMain) + { + TRY_LOCK(wallet->cs_wallet, lockWallet); + if(lockWallet && rec->statusUpdateNeeded()) + { + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + if(mi != wallet->mapWallet.end()) + { + + rec->updateStatus(mi->second); + } + } + } + return rec; + } + return 0; + } + QString getTxHex(TransactionRecord *rec) + { + LOCK2(cs_main, wallet->cs_wallet); + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + if(mi != wallet->mapWallet.end()) + { + std::string strHex = EncodeHexTx(static_cast(mi->second)); + return QString::fromStdString(strHex); + } + return QString(); + } +}; + +TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, CWallet* _wallet, WalletModel *parent): + QAbstractTableModel(parent), + wallet(_wallet), + walletModel(parent), + priv(new TransactionTablePriv(_wallet, this)), + fProcessingQueuedTransactions(false), + platformStyle(_platformStyle) +{ + columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + priv->refreshWallet(); + connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + subscribeToCoreSignals(); +} + +TransactionTableModel::~TransactionTableModel() +{ + unsubscribeFromCoreSignals(); + delete priv; +} + +/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ +void TransactionTableModel::updateAmountColumnTitle() +{ + columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + Q_EMIT headerDataChanged(Qt::Horizontal,Amount,Amount); +} + +void TransactionTableModel::updateTransaction(const QString &hash, int status, bool showTransaction) +{ + uint256 updated; + updated.SetHex(hash.toStdString()); + priv->updateWallet(updated, status, showTransaction); +} + +void TransactionTableModel::updateConfirmations() +{ + Q_EMIT dataChanged(index(0, Status), index(priv->size()-1, Status)); + Q_EMIT dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress)); +} + +int TransactionTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int TransactionTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} +bool TransactionTableModel::IsformatTxStatus(const TransactionRecord *wtx) const +{ + bool m_IsConfirmedRole; + + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + { + m_IsConfirmedRole =false; + } + break; + case TransactionStatus::OpenUntilDate: + { + m_IsConfirmedRole =false; + } + break; + case TransactionStatus::Offline: + { + + m_IsConfirmedRole =false; + } + break; + case TransactionStatus::Unconfirmed: + { + m_IsConfirmedRole =false; + + } + break; + case TransactionStatus::Abandoned: + { + m_IsConfirmedRole =false; + + } + break; + case TransactionStatus::Confirming: + { + m_IsConfirmedRole =false; + } + break; + case TransactionStatus::Confirmed: + { + + m_IsConfirmedRole =true; + + } + break; + case TransactionStatus::Conflicted: + { + m_IsConfirmedRole =false; + + } + break; + case TransactionStatus::Immature: + { + m_IsConfirmedRole =false; + } + break; + case TransactionStatus::MaturesWarning: + { + m_IsConfirmedRole =false; + } + break; + case TransactionStatus::NotAccepted: + { + + m_IsConfirmedRole =false; + } + break; + + } + return m_IsConfirmedRole; +} + +QString TransactionTableModel::FormatTxStatusRecord(const TransactionRecord *wtx) const +{ + + QString status; + if(TransactionStatus::OpenUntilBlock == wtx->status.status) + { + status = tr("Open for %n more block(s)","",wtx->status.open_for); + } + else if(TransactionStatus::OpenUntilDate ==wtx->status.status ) + { + status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for)); + + } + else if(TransactionStatus::Offline ==wtx->status.status) + { + status = tr("%1/offline").arg(wtx->status.depth); + + } + else + { + int nDepth = wtx->status.depth; + if (nDepth < 0) + { + status=QObject::tr("conflicted with a transaction with %1 confirmations").arg(-nDepth); + } + else if(0== nDepth) + { + status= tr("unconfirmed"); + } + else if (nDepth < 8) + { + status=tr("%1/unconfirmed").arg(nDepth); + } + else + { + status=tr("%1 confirmations").arg(nDepth); + } + + } + return status; +} + +QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const +{ + QString status; + + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + { + status = tr("Open for %n more block(s)","",wtx->status.open_for); + } + break; + case TransactionStatus::OpenUntilDate: + { + status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for)); + + } + break; + case TransactionStatus::Offline: + { + status = tr("Offline"); + + } + break; + case TransactionStatus::Unconfirmed: + { + status = tr("Unconfirmed"); + } + break; + case TransactionStatus::Abandoned: + { + status = tr("Abandoned"); + } + break; + case TransactionStatus::Confirming: + { + status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations); + } + break; + case TransactionStatus::Confirmed: + { + status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); + } + break; + case TransactionStatus::Conflicted: + { + status = tr("Conflicted"); + } + break; + case TransactionStatus::Immature: + { + status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in); + } + break; + case TransactionStatus::MaturesWarning: + { + status = tr("This block was not received by any other nodes and will probably not be accepted!"); + } + break; + case TransactionStatus::NotAccepted: + { + status = tr("Generated but not accepted"); + } + break; + + } + return status; +} + +QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const +{ + if(wtx->time) + { + return GUIUtil::dateTimeStr(wtx->time); + } + return QString(); +} + +/* Look up address in address book, if found return label (address) + otherwise just return (address) + */ +QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const +{ + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address)); + QString description; + if(!label.isEmpty()) + { + description += label; + } + if(label.isEmpty() || tooltip) + { + description += QString(" (") + QString::fromStdString(address) + QString(")"); + } + return description; +} + +QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const +{ + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + return tr("Received with"); + case TransactionRecord::RecvFromOther: + return tr("Received from"); + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToOther: + return tr("Sent to"); + case TransactionRecord::SendToSelf: + return tr("Payment to yourself"); + case TransactionRecord::Generated: + return tr("Mined"); + default: + return QString(); + } +} + +QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const +{ + + // bool confirmed = wtx->data(TransactionTableModel::ConfirmedRole).toBool(); + switch(wtx->type) + { + case TransactionRecord::Generated: + // return QIcon(":/icons/tx_mined"); + // return QIcon(":/icons/tx_input"); + return QIcon(":/res/png/tx_input.png"); + case TransactionRecord::RecvWithAddress: + case TransactionRecord::RecvFromOther: + case TransactionRecord::RecvIPC: + case TransactionRecord::RecvCoin: + case TransactionRecord::RecveCoin: + case TransactionRecord::Recvbookkeep: + case TransactionRecord::Recvdeposit: + return QIcon(":/res/png/tx_input.png"); + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToOther: + case TransactionRecord::SendToSelf: + case TransactionRecord::SendIPC: + case TransactionRecord::SendCoin: + case TransactionRecord::SendeCoin: + case TransactionRecord::Sendbookkeep: + case TransactionRecord::Senddeposit: + case TransactionRecord::Other: + return QIcon(":/res/png/tx_output.png"); + default: + return QIcon(":/res/png/tx_inout.png"); + } +} + +QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const +{ + QString watchAddress; + if (tooltip) { + // Mark transactions involving watch-only addresses by adding " (watch-only)" + watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : ""; + } + + switch(wtx->type) + { + case TransactionRecord::RecvFromOther: + return QString::fromStdString(wtx->address) + watchAddress; + case TransactionRecord::RecvWithAddress: + case TransactionRecord::SendToAddress: + case TransactionRecord::Generated: + return lookupAddress(wtx->address, tooltip) + watchAddress; + case TransactionRecord::SendToOther: + return QString::fromStdString(wtx->address) + watchAddress; + case TransactionRecord::SendToSelf: + case TransactionRecord::SendeCointoself: + default: + return tr("(n/a)") + watchAddress; + } +} + +QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const +{ + // Show addresses without label in a less visible color + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + case TransactionRecord::SendToAddress: + case TransactionRecord::Generated: + { + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address)); + if(label.isEmpty()) + return COLOR_BAREADDRESS; + } break; + case TransactionRecord::SendToSelf: + return COLOR_BAREADDRESS; + default: + break; + } + return QVariant(); +} + +QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) const +{ + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators); + if(showUnconfirmed) + { + if(!wtx->status.countsForBalance) + { + str = QString("[") + str + QString("]"); + } + } + return QString(str); +} + + + +QString TransactionTableModel::formatLang() const +{ + QSettings settings; + // Get desired locale (e.g. "de_DE") + // 1) System default language + QString lang_territory = QLocale::system().name(); + // 2) Language from QSettings + QString lang_territory_qsettings = settings.value("language", "").toString(); + if(!lang_territory_qsettings.isEmpty()) + lang_territory = lang_territory_qsettings; + // 3) -lang command line argument + lang_territory = QString::fromStdString(GetArg("-lang", lang_territory.toStdString())); + + + QLocale locale; + if( locale.language() == QLocale::English ) //获取系统语言环境 + { + lang_territory ="English"; + } + else if( locale.language() == QLocale::Chinese ) + { + lang_territory ="Chinese"; + } + + + return lang_territory; +} + + +QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const +{ + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + case TransactionStatus::OpenUntilDate: + return COLOR_TX_STATUS_OPENUNTILDATE; + case TransactionStatus::Offline: + return COLOR_TX_STATUS_OFFLINE; + case TransactionStatus::Unconfirmed: + return QIcon(":/icons/transaction_0"); + case TransactionStatus::Abandoned: + return QIcon(":/icons/transaction_abandoned"); + case TransactionStatus::Confirming: + switch(wtx->status.depth) + { + case 1: return QIcon(":/icons/transaction_1"); + case 2: return QIcon(":/icons/transaction_2"); + case 3: return QIcon(":/icons/transaction_3"); + case 4: return QIcon(":/icons/transaction_4"); + default: return QIcon(":/icons/transaction_5"); + }; + case TransactionStatus::Confirmed: + return QIcon(":/icons/transaction_confirmed"); + case TransactionStatus::Conflicted: + return QIcon(":/icons/transaction_conflicted"); + case TransactionStatus::Immature: { + int total = wtx->status.depth + wtx->status.matures_in; + int part = (wtx->status.depth * 4 / total) + 1; + return QIcon(QString(":/icons/transaction_%1").arg(part)); + } + case TransactionStatus::MaturesWarning: + case TransactionStatus::NotAccepted: + return QIcon(":/icons/transaction_0"); + default: + return COLOR_BLACK; + } +} + +QVariant TransactionTableModel::txWatchonlyDecoration(const TransactionRecord *wtx) const +{ + if (wtx->involvesWatchAddress) + return QIcon(":/icons/eye"); + else + return QVariant(); +} + +QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const +{ + QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec); + if( TransactionRecord::RecvFromOther == rec->type || TransactionRecord::SendToOther ==rec->type || + TransactionRecord::SendToAddress == rec->type || TransactionRecord::RecvWithAddress==rec->type || + TransactionRecord::SendIPC==rec->type || TransactionRecord::RecvIPC==rec->type ) + { + // tooltip += QString(" ") + formatTxToAddress(rec, true); + } + return tooltip; +} + + +QVariant TransactionTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + TransactionRecord *rec = static_cast(index.internalPointer()); + switch(role) + { + case RawDecorationRole: + switch(index.column()) + { + case Status: + return txStatusDecoration(rec); + case Watchonly: + return txWatchonlyDecoration(rec); + case ToAddress: + return txAddressDecoration(rec); + } + break; + case Qt::DecorationRole: + { + QIcon icon = qvariant_cast(index.data(RawDecorationRole)); + return platformStyle->TextColorIcon(icon); + } + case Qt::DisplayRole: + switch(index.column()) + { + case Date: + return formatTxDate(rec); + case Type: + return formatTxType(rec); + case ToAddress: + return formatTxToAddress(rec, false); + case Amount: + return formatTxAmount(rec, true, BitcoinUnits::separatorAlways); + } + break; + case Qt::EditRole: + // Edit role is used for sorting, so return the unformatted values + switch(index.column()) + { + case Status: + return QString::fromStdString(rec->status.sortKey); + case Date: + return rec->time; + case Type: + return formatTxType(rec); + case Watchonly: + { + return (rec->involvesWatchAddress ? 1 : 0); + } + case ToAddress: + return formatTxToAddress(rec, true); + case Amount: + return qint64(rec->credit + rec->debit); + case overAmount: + return qint64(rec->debit); + + } + break; + case Qt::ToolTipRole: + return formatTooltip(rec); + case Qt::TextAlignmentRole: + return column_alignments[index.column()]; + case Qt::ForegroundRole: + // Use the "danger" color for abandoned transactions + if(rec->status.status == TransactionStatus::Abandoned) + { + return COLOR_TX_STATUS_DANGER; + } + // Non-confirmed (but not immature) as transactions are grey + if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature) + { + return COLOR_UNCONFIRMED; + } + if(index.column() == Amount && (rec->credit+rec->debit) < 0) + { + return COLOR_NEGATIVE; + } + if(index.column() == ToAddress) + { + return addressColor(rec); + } + break; + case TypeRole: + return rec->type; + case IPCType: + return rec->ipcType; + case IPCTitle: + return QString::fromStdString(rec->ipcTitle); + + + + case eCoinType: + return QString::fromStdString(rec->ecoinType); + case eCoinNum: + return rec->ecoinNum; + case DateRole: + return (QDateTime::fromTime_t(static_cast(rec->time))).toString("yyyy-MM-dd hh:mm:ss"); + case WatchonlyRole: + return rec->involvesWatchAddress; + case WatchonlyDecorationRole: + return txWatchonlyDecoration(rec); + case AddressRole: + return QString::fromStdString(rec->address); + case LabelRole: + return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); + case LanguageRole: + return formatLang(); + + case feeAmount: + return qint64(rec->TxFee); + + + case AmountRole: + if( TransactionRecord::Type::Senddeposit== rec->type ||TransactionRecord::Type::SendToSelf== rec->type || TransactionRecord::Type::Recvdeposit== rec->type) + { + return qint64(rec->credit) ; + } + else if(TransactionRecord::Type::SendCoin== rec->type) + { + return qint64(rec->debit);//+ rec->debit); + } + else + { + return qint64(rec->credit+ rec->debit); + } + case InfoStatus: + return FormatTxStatusRecord(rec); + case InfoTime: + return rec->strtime; + case AuthTime: + return rec->authTime; + case AuthLimit: + return rec->authLimit; + case AuthType: + return rec->authType; + case overAmount: + return qint64(rec->debit); + case TxIDRole: + return rec->getTxID(); + case TxHashRole: + return QString::fromStdString(rec->hash.ToString()); + case TxHexRole: + return priv->getTxHex(rec); + case TxPlainTextRole: + { + QString details; + QDateTime date = QDateTime::fromTime_t(static_cast(rec->time)); + QString txLabel = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); + + details.append(date.toString("M/d/yy HH:mm")); + details.append(" "); + details.append(formatTxStatus(rec)); + details.append(". "); + if(!formatTxType(rec).isEmpty()) { + details.append(formatTxType(rec)); + details.append(" "); + } + if(!rec->address.empty()) { + if(txLabel.isEmpty()) + details.append(tr("(no label)") + " "); + else { + details.append("("); + details.append(txLabel); + details.append(") "); + } + details.append(QString::fromStdString(rec->address)); + details.append(" "); + } + details.append(formatTxAmount(rec, false, BitcoinUnits::separatorNever)); + return details; + } + case ConfirmedRole: + return rec->status.countsForBalance; + case IsConfirmedRole: + return IsformatTxStatus(rec); + case FormattedAmountRole: + // Used for copy/export, so don't include separators + return formatTxAmount(rec, false, BitcoinUnits::separatorNever); + case StatusRole: + return rec->status.status; + } + return QVariant(); +} + +QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + else if (role == Qt::TextAlignmentRole) + { + return column_alignments[section]; + } else if (role == Qt::ToolTipRole) + { + switch(section) + { + case Status: + return tr("Transaction status. Hover over this field to show number of confirmations."); + case Date: + return tr("Date and time that the transaction was received."); + case Type: + return tr("Type of transaction."); + case Watchonly: + return tr("Whether or not a watch-only address is involved in this transaction."); + case ToAddress: + return tr("User-defined intent/purpose of the transaction."); + case Amount: + return tr("Amount removed from or added to balance."); + } + } + } + return QVariant(); +} + +QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + TransactionRecord *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + return QModelIndex(); +} + +void TransactionTableModel::updateDisplayUnit() +{ + + // emit dataChanged to update Amount column with the current unit + updateAmountColumnTitle(); + + Q_EMIT dataChanged(index(0, Amount), index(priv->size()-1, Amount)); +} + +// queue notifications to show a non freezing progress dialog e.g. for rescan +struct TransactionNotification +{ +public: + TransactionNotification() {} + TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction): + hash(_hash), status(_status), showTransaction(_showTransaction) {} + + void invoke(QObject *ttm) + { + + QString strHash = QString::fromStdString(hash.GetHex()); + qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status); + QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection, + Q_ARG(QString, strHash), + Q_ARG(int, status), + Q_ARG(bool, showTransaction)); + } +private: + uint256 hash; + ChangeType status; + bool showTransaction; +}; + +static bool fQueueNotifications = false; +static std::vector< TransactionNotification > vQueueNotifications; + +static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status) +{ + // Find transaction in wallet + std::map::iterator mi = wallet->mapWallet.find(hash); + // Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread) + bool inWallet = mi != wallet->mapWallet.end(); + bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second)); + + TransactionNotification notification(hash, status, showTransaction); + + if (fQueueNotifications) + { + vQueueNotifications.push_back(notification); + return; + } + notification.invoke(ttm); + +} + +static void ShowProgress(TransactionTableModel *ttm, const std::string &title, int nProgress) +{ + if (nProgress == 0) + fQueueNotifications = true; + + if (nProgress == 100) + { + fQueueNotifications = false; + if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons + QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true)); + for (unsigned int i = 0; i < vQueueNotifications.size(); ++i) + { + if (vQueueNotifications.size() - i <= 10) + QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false)); + + vQueueNotifications[i].invoke(ttm); + } + std::vector().swap(vQueueNotifications); // clear + } +} + +void TransactionTableModel::subscribeToCoreSignals() +{ + // Connect signals to wallet + wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); +} + +void TransactionTableModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from wallet + wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); +} diff --git a/.svn/pristine/34/34d7be7b5c07319c1e6133c951889cc146cc6681.svn-base b/.svn/pristine/34/34d7be7b5c07319c1e6133c951889cc146cc6681.svn-base new file mode 100644 index 0000000..d4750ca --- /dev/null +++ b/.svn/pristine/34/34d7be7b5c07319c1e6133c951889cc146cc6681.svn-base @@ -0,0 +1,290 @@ +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "passwordsettingwidget.h" +#include "forms/ui_passwordsettingwidget.h" +#include "guiconstants.h" +#include "walletmodel.h" +#include "support/allocators/secure.h" +#include +#include +#include +#include "cmessagebox.h" +#include "dpoc/TimeService.h" +#include +#include "log/log.h" +PasswordSettingWidget::PasswordSettingWidget(Mode _mode,QWidget *parent) : + QWidget(parent), + ui(new Ui::PasswordSettingWidget) +{ + ui->setupUi(this); + ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint()); + ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint()); + ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint()); + + ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); + + // Setup Caps Lock detection. + ui->passEdit1->installEventFilter(this); + ui->passEdit2->installEventFilter(this); + ui->passEdit3->installEventFilter(this); + + LOG_WRITE(LOG_INFO,"passwordsettingwidget::passwordsettingwidget",QString::number(_mode).toStdString().c_str()); + switch(_mode) + { + case Encrypt: // Ask passphrase x2 + ui->passEdit3->hide(); + ui->passEdit3->setVisible(false); + ui->passEdit1->setPlaceholderText(tr("6-8 characters, suggesting English numerals are mixed")); + ui->passEdit2->setPlaceholderText(tr("Confirm password")); + break; + case Unlock: // Ask passphrase + ui->passEdit2->hide(); + ui->passEdit3->hide(); + break; + case Decrypt: // Ask passphrase + ui->passEdit2->hide(); + ui->passEdit3->hide(); + break; + case ChangePass: // Ask old passphrase + new passphrase x2 + ui->passEdit1->setPlaceholderText(tr("old password")); + ui->passEdit2->setPlaceholderText(tr("6-8 characters, suggesting English numerals are mixed")); + ui->passEdit3->setPlaceholderText(tr("Confirm password")); + break; + } + textChanged(); + connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + ui->label_error->setText(""); +} + +PasswordSettingWidget::~PasswordSettingWidget() +{ + delete ui; +} +void PasswordSettingWidget::on_pushButton_2_pressed() +{ + Q_EMIT back(); +} +void PasswordSettingWidget::setMode(Mode _mode) +{ + this->mode = _mode; +} +void PasswordSettingWidget::setModel(WalletModel *_model) +{ + this->model = _model; +} +void PasswordSettingWidget::on_pushButton_pressed() +{ + LOG_WRITE(LOG_INFO,"passwordsettingwidget::on_pushBUtton_pressed",QString::number(mode).toStdString().c_str()); + + if(mode == Encrypt){ + if(ui->passEdit1->text().isEmpty()){ + ui->label_error->setText(tr("please set password")); + return; + }else if(ui->passEdit2->text().isEmpty()){ + ui->label_error->setText(tr("please set confirm password")); + return; + } + int size = ui->passEdit1->text().size(); + std::string temp = ui->passEdit1->text().toStdString(); + if(size<6||size>8||temp.length()!=size){ + ui->label_error->setText(tr("please check password")); + return; + } + }else if(mode == ChangePass){ + if(ui->passEdit1->text().isEmpty()){ + ui->label_error->setText(tr("please set old password")); + return; + }else if(ui->passEdit2->text().isEmpty()){ + ui->label_error->setText(tr("please set new password")); + return; + }else if(ui->passEdit3->text().isEmpty()){ + ui->label_error->setText(tr("please set confirm password")); + return; + } + int size = ui->passEdit2->text().size(); + std::string temp = ui->passEdit2->text().toStdString(); + if(size<6||size>8||temp.length()!=size){ + ui->label_error->setText(tr("please check password")); + return; + } + } + + ui->label_error->setText(tr("please wait...")); + SecureString oldpass, newpass1, newpass2; + if(!model) + return; + oldpass.reserve(MAX_PASSPHRASE_SIZE); + newpass1.reserve(MAX_PASSPHRASE_SIZE); + newpass2.reserve(MAX_PASSPHRASE_SIZE); + + switch(mode) + { + case Encrypt: { + newpass1.assign(ui->passEdit1->text().toStdString().c_str()); + newpass2.assign(ui->passEdit2->text().toStdString().c_str()); + + if(newpass1.empty() || newpass2.empty()) + { + // Cannot encrypt with empty passphrase + break; + } + if(newpass1 == newpass2) + { + if(model->setWalletEncrypted(true, newpass1)) + { + LOG_WRITE(LOG_INFO,"passwordsettingwidget::Encrypted",QString::number(getTag()).toStdString().c_str()); + if(2 == getTag()) + { + Q_EMIT openSendCoinsAffrimwidget(); + } + else + { + ui->label_error->setText(tr("passwordset success")); + Q_EMIT ChangePasswordSuccess(); + } + } + else + { + ui->label_error->setText(tr("Password mismatch")); + } + } + else + { + ui->label_error->setText(tr("Wallet encryption failed")); + } + + } break; + case Unlock: + if(!model->setWalletLocked(false, oldpass)) + { + ui->label_error->setText(tr("Wallet unlock failed")); + } + break; + case Decrypt: + if(!model->setWalletEncrypted(false, oldpass)) + { + ui->label_error->setText(tr("Wallet decryption failed")); + } + break; + case ChangePass: + oldpass.assign(ui->passEdit1->text().toStdString().c_str()); + newpass1.assign(ui->passEdit2->text().toStdString().c_str()); + newpass2.assign(ui->passEdit3->text().toStdString().c_str()); + if(newpass1 == newpass2) + { + int64_t timenum = 0; + int iPasswordErrorNum = 0; + bool was_locked = (model->getEncryptionStatus() == WalletModel::Locked)?1:0; + if(was_locked) + { + QSettings settings; + iPasswordErrorNum = settings.value("PasswordErrorNum").toInt(); + LOG_WRITE(LOG_INFO,"passwordsettingwidget::iPasswordErrorNum",QString::number(iPasswordErrorNum).toStdString().c_str()); + timenum = timeService.GetCurrentTimeSeconds(); + if(iPasswordErrorNum >= 5){ + QString locktime = settings.value("locktime").toString(); + LOG_WRITE(LOG_INFO,"passwordsettingwidget::timeService::GetCurrentTimeSeconds",QString::number(timenum).toStdString().c_str(),"locktime",locktime.toStdString().c_str()); + QString strtimenum = QString::number(timenum); + if(locktime > strtimenum){ + CMessageBox msg; + msg.setGeometry(this->x()+(this->width()-msg.width())/2, + this->y()+(this->height()-msg.height())/2,msg.width(),msg.height()); + + msg.setIsClose(false); + msg.setMessage(2); + msg.exec(); + ui->label_error->setText(tr("Wallet unlock failed")); + return; + } + } + if(model->changePassphrase(oldpass, newpass1)) + { + LOG_WRITE(LOG_INFO,"passwordsettingwidget::changePassphrase",QString::number(getTag()).toStdString().c_str()); + + settings.setValue("PasswordErrorNum", 0); + ui->label_error->setText(tr("Wallet passphrase was successfully changed.")); + if(2 == getTag()) + { + Q_EMIT openSendCoinsAffrimwidget(); + } + else + { + ui->label_error->setText(tr("passwordset success")); + Q_EMIT ChangePasswordSuccess(); + } + } + else + { + timenum = timenum + (int64_t)60*60*24; + settings.setValue("PasswordErrorNum", iPasswordErrorNum +1); + settings.setValue("locktime", QString::number(timenum)); + ui->label_error->setText(tr("Wallet encryption failed,The passphrase entered for the wallet decryption was incorrect.")); + } + }else{ + LOG_WRITE(LOG_INFO,"passwordsettingwidget::no lock"); + } + } + else + { + ui->label_error->setText(tr("Wallet encryption failed")); + secureClearPassFields(); + } + break; + } +} +int PasswordSettingWidget::getTag() +{ + return m_tag; +} +void PasswordSettingWidget::setTag(int tag) +{ + m_tag = tag; +} +void PasswordSettingWidget::textChanged() +{ + // Validate input, set Ok button to enabled when acceptable + bool acceptable = false; + switch(mode) + { + case Encrypt: + acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); + break; + case Unlock: + case Decrypt: + break; + case ChangePass: + break; + } +} + +bool PasswordSettingWidget::event(QEvent *event) +{ + // Detect Caps Lock key press. + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + if (ke->key() == Qt::Key_CapsLock) { + fCapsLock = !fCapsLock; + } + } + return QWidget::event(event); +} + +static void SecureClearQLineEdit(QLineEdit* edit) +{ + // Attempt to overwrite text so that they do not linger around in memory + edit->setText(QString(" ").repeated(edit->text().size())); + edit->clear(); +} + +void PasswordSettingWidget::secureClearPassFields() +{ + SecureClearQLineEdit(ui->passEdit2); + SecureClearQLineEdit(ui->passEdit3); +} + diff --git a/.svn/pristine/37/37fd25524b815f93dc91118be087095aa060b5bf.svn-base b/.svn/pristine/37/37fd25524b815f93dc91118be087095aa060b5bf.svn-base new file mode 100644 index 0000000..5149593 --- /dev/null +++ b/.svn/pristine/37/37fd25524b815f93dc91118be087095aa060b5bf.svn-base @@ -0,0 +1,624 @@ +// Copyright (c) 2011-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "walletview.h" +#include "ipchaingui.h" +#include "clientmodel.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "overviewpage.h" +#include "platformstyle.h" +#include "receivecoinsdialog.h" +#include "sendcoinsdialog.h" +#include "transactiontablemodel.h" +#include "walletmodel.h" +#include "settingwidget.h" +#include "ui_interface.h" +#include "ipcdialog.h" +#include "setdialog.h" +#include "infowidget.h" +#include "sendhistory.h" +#include "recvhistory.h" +#include "sendipchistory.h" +#include "recvipchistory.h" +#include "sendresultwidget.h" +#include "sendtokenhistory.h" +#include "recvtokenhistory.h" + +#include "passwordsettingwidget.h" +#include "sendcoinsaffrimwidget.h" +#include "addresstablemodel.h" +#include "addbookwidget.h" +//#include "editadddialog.h" +#include "ecoindialog.h" +#include "intro.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "tallyaccount.h" +#include "tallyapply.h" +#include "tallyclause.h" +#include "tallydscribe.h" +#include "tallyoutaccount.h" +#include "ipcselectaddress.h" + +#include "dpoc/DpocInfo.h" +#include "dpoc/DpocMining.h" +#include "log/log.h" + +WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent): + QStackedWidget(parent), + clientModel(0), + walletModel(0), + tallyDialogPage(0), + platformStyle(_platformStyle), + m_IsTallying(false) +{ + overviewPage = new OverviewPage(platformStyle); + receiveCoinsPage = new ReceiveCoinsDialog(platformStyle); + sendCoinsPage = new SendCoinsDialog(platformStyle); + pTallySelectAddressPage = new ipcSelectAddress(this); + ipcdialogPage = new ipcdialog(); + setdialogPage = new setdialog(); + tallyDialogPage = new QWidget(); + tallyDialogPageStack = new QStackedWidget(); + + QVBoxLayout *walletFrameLayout = new QVBoxLayout(); + walletFrameLayout->setContentsMargins(0,0,0,0); + walletFrameLayout->setContentsMargins(0,0,0,0); + tallyDialogPage->setLayout(walletFrameLayout); + walletFrameLayout->addWidget(tallyDialogPageStack); + + pTallyAccount = new TallyAccount(this); + pTallyApply = new TallyApply(this); + pTallyClause = new TallyClause(this); + pTallyDscribe = new TallyDscribe(this); + pTallyOutAccount = new TallyOutAccount(this); + tallyDialogPageStack->addWidget(pTallyAccount); + tallyDialogPageStack->addWidget(pTallyApply); + tallyDialogPageStack->addWidget(pTallyClause); + tallyDialogPageStack->addWidget(pTallyOutAccount); + tallyDialogPageStack->addWidget(pTallyDscribe); + tallyDialogPageStack->addWidget(pTallySelectAddressPage); + tallyDialogPageStack->setCurrentWidget(pTallyDscribe); + connect(pTallyDscribe,SIGNAL(next()),this,SLOT(gotoClausePage())); + connect(pTallyClause,SIGNAL(next()),this,SLOT(gotoTallyApplyPage())); + connect(pTallyClause,SIGNAL(back()),this,SLOT(gotoDscribePage())); + connect(pTallyApply,SIGNAL(next(QString,CAmount)),this,SLOT(gotoTallyAccountPage(QString,CAmount))); + connect(pTallyApply,SIGNAL(selectaddress()),this,SLOT(gotoTallyAddressPage())); + connect(pTallyAccount,SIGNAL(next(QString,CAmount)),this,SLOT(gotoTallyOutAccountPage(QString,CAmount))); + connect(pTallyOutAccount,SIGNAL(next(bool)),this,SLOT(gotoBackTallyAccountPage(bool))); + connect(pTallySelectAddressPage,SIGNAL(back(QString)),this,SLOT(gotoTallyApplyPage(QString))); + + ecoinDialogPage = new ECoinDialog(this); + + addWidget(overviewPage); + addWidget(receiveCoinsPage); + addWidget(sendCoinsPage); + addWidget(ipcdialogPage); + addWidget(setdialogPage); + addWidget(tallyDialogPage); + addWidget(ecoinDialogPage); + settingwidgetPage= new Settingwidget(this); + + addWidget(settingwidgetPage); + + connect(settingwidgetPage,SIGNAL(openPasswordSetwidget(int)),this,SLOT(gotoPasswordSetwidgetPage(int))); + connect(settingwidgetPage,SIGNAL(openSendCoinsAffrimwidget()),this,SLOT(gotoSendCoinsAffrimPage())); + + // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), parent, SLOT(focusTransaction(QModelIndex))); + connect(overviewPage, SIGNAL(transactionnnClicked(QModelIndex,quint8)), parent, SLOT(showwwDetails(QModelIndex,quint8))); + connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo())); + connect(sendCoinsPage,SIGNAL(openSettingwidget(QString,QString,QString,int)),this,SLOT(gotoSettingPage(QString,QString,QString,int))); + connect(sendCoinsPage,SIGNAL(openSendAffrimwidget(QString,QString,QString,int)),this,SLOT(gotoSendaffrimPage(QString,QString,QString,int))); + connect(sendCoinsPage,SIGNAL(openAddBookPagewidget(AddressTableModel*,int)),this,SLOT(gotoAddBookPage(AddressTableModel*,int))); + connect(setdialogPage,SIGNAL(openAddBookPagewidget(AddressTableModel*,int)),this,SLOT(gotoAddBookPage(AddressTableModel*,int))); + connect(setdialogPage,SIGNAL(openPasswordSetwidget(int)),this,SLOT(gotoPasswordSetwidgetPage(int))); + connect(this,SIGNAL(openadd()),this,SLOT(openAddBookPage())); + connect(sendCoinsPage, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); + +} + +WalletView::~WalletView() +{ +} + +void WalletView::openAddBookPage() +{ + AddBookPage = new AddBookWidget(this); + connect(AddBookPage,SIGNAL(backSend()),this,SLOT(gotoWalletViewPagekepinfo())); + connect(AddBookPage,SIGNAL(selectaddressyes(QString,QString)),this,SLOT(gotoWalletViewPaga(QString,QString))); + + AddBookPage->settag(5); + setdialogPage->setAddpageshow(AddBookPage); + AddBookPage->setModel(walletModel->getAddressTableModel()); + +} +void WalletView::gotoAddBookPage(AddressTableModel *atm,int tag) +{ + AddBookPage = new AddBookWidget(this); + connect(AddBookPage,SIGNAL(backSend()),this,SLOT(gotoWalletViewPagekepinfo())); + connect(AddBookPage,SIGNAL(selectaddressyes(QString,QString)),this,SLOT(gotoWalletViewPaga(QString,QString))); + + if(5== tag) + { + AddBookPage->settag(5); + setdialogPage->setAddpageshow(AddBookPage); + } + else + { + AddBookPage->settag(2); + addWidget(AddBookPage); + setCurrentWidget(AddBookPage); + } + AddBookPage->setModel(atm); +} + + +void WalletView::gotoWalletViewPagekepinfo() +{ + setCurrentWidget(sendCoinsPage); +} +void WalletView::gotoWalletViewPage() +{ + sendCoinsPage->clearInfo(); + setCurrentWidget(sendCoinsPage); +} + +void WalletView::gotoWalletViewPaga(QString a,QString b) +{ + if(5 == AddBookPage->gettag()) + { + return; + } + gotoSendCoinsPage(a,b); + +} +void WalletView::gotoPasswordSetwidgetPage(int tag) +{ + bool was_locked = this->walletModel->CheckIsCrypted(); + PasswordSettingWidget::Mode status ; + if(was_locked) + { + status = PasswordSettingWidget::ChangePass; + } + else + { + status =PasswordSettingWidget::Encrypt; + } + PasswordSettingPage = new PasswordSettingWidget(status,this); + connect(PasswordSettingPage,SIGNAL(back()),this,SLOT(gotoWalletViewPage())); + connect(PasswordSettingPage,SIGNAL(openSendCoinsAffrimwidget()),this,SLOT(gotoSendCoinsAffrimPage())); + connect(PasswordSettingPage,SIGNAL(ChangePasswordSuccess()),this,SLOT(gotoPsdSetSuccessPage())); + PasswordSettingPage->setMode(status); + PasswordSettingPage->setModel(this->walletModel); + this->gotopsdsetpage(PasswordSettingPage,tag); + +} + + +void WalletView::gotoSendaffrimPage(QString a,QString b,QString label,int c) +{ + bool was_locked = this->walletModel->getEncryptionStatus() == WalletModel::Locked; + + SendCoinsAffrimPage = new SendCoinsAffrimWidget(was_locked,this); + SendCoinsAffrimPage->setModel(this->walletModel); + SendCoinsAffrimPage->setClientModel(this->clientModel); + connect(SendCoinsAffrimPage,SIGNAL(back()),this,SLOT(gotoWalletViewPage())); + connect(SendCoinsAffrimPage,SIGNAL(goresultpage()),this,SLOT(gotoResultPage()),Qt::DirectConnection); + connect(SendCoinsAffrimPage,SIGNAL(gobacktosendpage()),this,SLOT(backtoSendCoinsPage())); + + addWidget(SendCoinsAffrimPage); + setCurrentWidget(SendCoinsAffrimPage); + QString add = a; + QString coin = b; + int tag = c; + SendCoinsAffrimPage->setMessage(add,coin,label,tag); + +} +void WalletView::gotoSendCoinsAffrimPage() +{ + bool was_locked = this->walletModel->getEncryptionStatus() == WalletModel::Locked; + + SendCoinsAffrimPage = new SendCoinsAffrimWidget(was_locked,this); + SendCoinsAffrimPage->setModel(this->walletModel); + SendCoinsAffrimPage->setClientModel(this->clientModel); + connect(SendCoinsAffrimPage,SIGNAL(back()),this,SLOT(gotoWalletViewPage())); + connect(SendCoinsAffrimPage,SIGNAL(goresultpage()),this,SLOT(gotoResultPage()),Qt::DirectConnection); + connect(SendCoinsAffrimPage,SIGNAL(gobacktosendpage()),this,SLOT(backtoSendCoinsPage())); + + addWidget(SendCoinsAffrimPage); + setCurrentWidget(SendCoinsAffrimPage); + QString a = sendinfo_.Add_; + QString b = sendinfo_.Coin_; + QString label = sendinfo_.Label_; + int c = sendinfo_.Status_; + SendCoinsAffrimPage->setMessage(a,b,label,c); + +} +void WalletView::gotoPsdSetSuccessPage() +{ + setdialogPage->setPsdSetSuccess(); +} +void WalletView::gotoSettingPage(QString a,QString b,QString label,int tag) +{ + SendInfo T; + T.Add_ = a; + T.Coin_ = b, + T.Label_ = label; + T.Status_ = tag; + setvalue(T); + setCurrentWidget(settingwidgetPage); +} + +void WalletView::setvalue(SendInfo a) +{ + sendinfo_.Coin_ = a.Coin_; + sendinfo_.Add_ = a.Add_; + sendinfo_.Label_ = a.Label_; + sendinfo_.Status_ = a.Status_; +} +SendInfo WalletView::getvalue() +{ + return sendinfo_; +} + +void WalletView::setBitcoinGUI(BitcoinGUI *gui) +{ + + + if (gui) + { + connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); + connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); + connect(this, SIGNAL(hdEnabledStatusChanged(int)), gui, SLOT(setHDStatus(int))); + } +} + +void WalletView::setClientModel(ClientModel *_clientModel) +{ + this->clientModel = _clientModel; + overviewPage->setClientModel(_clientModel); + sendCoinsPage->setClientModel(_clientModel); +} + +void WalletView::setWalletModel(WalletModel *_walletModel) +{ + this->walletModel = _walletModel; + + overviewPage->setWalletModel(_walletModel); + receiveCoinsPage->setModel(_walletModel); + sendCoinsPage->setModel(_walletModel); + ipcdialogPage->setModel(_walletModel); + setdialogPage->setModel(_walletModel); + pTallySelectAddressPage->setWalletModel(_walletModel); + pTallyApply->setModel(_walletModel); + pTallyOutAccount->setModel(_walletModel); + ecoinDialogPage->setModel(_walletModel); + pTallyAccount->setModel(_walletModel); + + + if (_walletModel) + { + // Receive and pass through messages from wallet model + connect(_walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); + + // Handle changes in encryption status + connect(_walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int))); + updateEncryptionStatus(); + + // update HD status + Q_EMIT hdEnabledStatusChanged(_walletModel->hdEnabled()); + + // Balloon pop-up for new transaction + connect(_walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(processNewTransaction(QModelIndex,int,int))); + + // Ask for passphrase if needed + connect(_walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet())); + // Show progress dialog + connect(_walletModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int))); + } +} + +void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/) +{ + // Prevent balloon-spam when initial block download is in progress + if (!walletModel || !clientModel || clientModel->inInitialBlockDownload()) + return; + + TransactionTableModel *ttm = walletModel->getTransactionTableModel(); + if (!ttm || ttm->processingQueuedTransactions()) + return; + + QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); + qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); + QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); + QModelIndex index = ttm->index(start, 0, parent); + QString address = ttm->data(index, TransactionTableModel::AddressRole).toString(); + QString label = ttm->data(index, TransactionTableModel::LabelRole).toString(); + + QString ipctitle = ttm->index(start,TransactionTableModel::IPCTitle,parent).data().toString(); + QString ipctype = ttm->index(start,TransactionTableModel::IPCType,parent).data().toString(); + +} + + +void WalletView::opensendpage(sendhistory *sendpage) +{ + overviewPage->addsendpage(sendpage); +} + + +void WalletView::openrecvpage(RecvHistory *recvpage) +{ + overviewPage->addrecv(recvpage); +} +void WalletView::opensendipcpage(sendipchistory *sendipcpage) +{ + overviewPage->addsendipcpage(sendipcpage); + +} +void WalletView::openrecvipcpage(recvipchistory *recvipcpage) +{ + overviewPage->addrecvipcpage(recvipcpage); +} + + +void WalletView::opensendtokenpage(SendTokenHistory *sendTokenPage) +{ + overviewPage->addsendtokenpage(sendTokenPage); +} +void WalletView::openrecvtokenpage(RecvTokenHistory *recvTokenPage) +{ + overviewPage->addrecvtokenpage(recvTokenPage); +} + +void WalletView::gotoOverviewPage() +{ + setCurrentWidget(overviewPage); +} + + +void WalletView::gotoReceiveCoinsPage() +{ + setCurrentWidget(receiveCoinsPage); +} + +void WalletView::gotopsdsetpage(PasswordSettingWidget *PasswordSettingPage,int tag) +{ + if(5==tag) + { + PasswordSettingPage->setTag(5); + setdialogPage->setPsdshow(PasswordSettingPage); + } + else + { + PasswordSettingPage->setTag(2); + addWidget(PasswordSettingPage); + setCurrentWidget(PasswordSettingPage); + + } +} +void WalletView::gotoResultPage() +{ + sendresultpage =new SendResultWidget(this); + addWidget(sendresultpage); + connect(sendresultpage,SIGNAL(backmain()),this,SLOT(gotoWalletViewPage())); + + setCurrentWidget(sendresultpage); + +} + +void WalletView::gotoIpcPage() +{ + ipcdialogPage->updatalist(); + setCurrentWidget(ipcdialogPage); +} + +void WalletView::gotoSetPage() +{ + setCurrentWidget(setdialogPage); + + Q_EMIT openadd(); +} +void WalletView::gotoTallyPage() +{ + + try{ + if(!CDpocInfo::Instance().IsHasLocalAccount()) + { + LOG_WRITE(LOG_INFO,"CDpocInfo::Instance().IsHasLocalAccount false "); + if(tallyDialogPageStack&&pTallyDscribe) + tallyDialogPageStack->setCurrentWidget(pTallyDscribe); + } + else + { + LOG_WRITE(LOG_INFO,"CDpocInfo::Instance().IsHasLocalAccount true "); + pTallyAccount->resettime(); + if(tallyDialogPageStack&&pTallyAccount) + { + pTallyAccount->SetnewInfo(); + tallyDialogPageStack->setCurrentWidget(pTallyAccount); + + } + } + if(tallyDialogPage) + setCurrentWidget(tallyDialogPage); + } + catch(...){ + LOG_WRITE(LOG_INFO,"WalletView::gotoTallyPage() try Error!"); + } + LOG_WRITE(LOG_INFO,"WalletView::gotoTallyPage finish!"); +} +void WalletView::gotoeCoinPage(){ + ecoinDialogPage->updatalist(); + setCurrentWidget(ecoinDialogPage); + +} + +void WalletView::backtoSendCoinsPage() +{ + setCurrentWidget(sendCoinsPage); +} +void WalletView::gotoSendCoinsPage(QString addr,QString label) +{ + + setCurrentWidget(sendCoinsPage); + + if (!addr.isEmpty()) + { + sendCoinsPage->setAddress(addr,label); + } + else + { + sendCoinsPage->clearInfo(); + } +} + + +void WalletView::updateEncryptionStatus() +{ + Q_EMIT encryptionStatusChanged(walletModel->getEncryptionStatus()); +} + +void WalletView::encryptWallet(bool status) +{ + if(!walletModel) + return; + + + updateEncryptionStatus(); +} + +void WalletView::backupWallet() +{ + QString filename = GUIUtil::getSaveFileName(this, + tr("Backup Wallet"), QString(), + tr("Wallet Data (*.dat)"), NULL); + + if (filename.isEmpty()) + return; + + if (!walletModel->backupWallet(filename)) { + Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename), + CClientUIInterface::MSG_ERROR); + } + else { + Q_EMIT message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename), + CClientUIInterface::MSG_INFORMATION); + } +} + + + +void WalletView::unlockWallet() +{ + if(!walletModel) + return; + // Unlock wallet when requested by wallet model + if (walletModel->getEncryptionStatus() == WalletModel::Locked) + { + SecureString oldpass; + oldpass.assign("1"); + + if(!walletModel->setWalletLocked(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet unlock failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + // QDialog::accept(); // Success + } + } +} + + +void WalletView::showProgress(const QString &title, int nProgress) +{ + if (nProgress == 0) + { + progressDialog = new QProgressDialog(title, "", 0, 100); + progressDialog->setWindowModality(Qt::ApplicationModal); + progressDialog->setMinimumDuration(0); + progressDialog->setCancelButton(0); + progressDialog->setAutoClose(false); + progressDialog->setValue(0); + } + else if (nProgress == 100) + { + if (progressDialog) + { + progressDialog->close(); + progressDialog->deleteLater(); + } + } + else if (progressDialog) + progressDialog->setValue(nProgress); +} + +void WalletView::requestedSyncWarningInfo() +{ + Q_EMIT outOfSyncWarningClicked(); +} +void WalletView::gotoClausePage() +{ + tallyDialogPageStack->setCurrentWidget(pTallyClause); +} +void WalletView::gotoDscribePage() +{ + tallyDialogPageStack->setCurrentWidget(pTallyDscribe); +} + +void WalletView::gotoTallyApplyPage(QString address) +{ + pTallyApply->resetinfo(); + if(!address.isEmpty()){ + pTallyApply->setAddress(address); + } + tallyDialogPageStack->setCurrentWidget(pTallyApply); +} +void WalletView::gotoTallyAccountPage(QString add,CAmount num) +{ + tallyDialogPageStack->setCurrentWidget(pTallyAccount); + WalletModel::keepupaccountInfo info; + info.Add_ =add; + info.Coin_ = num; + + pTallyAccount->setinfo(info); +} +void WalletView::setIsTallying(bool IsTally) +{ + m_IsTallying= IsTally; +} +bool WalletView::getIsTallying() +{ + return m_IsTallying; +} +void WalletView::gotoTallyOutAccountPage(QString add,CAmount num) +{ + tallyDialogPageStack->setCurrentWidget(pTallyOutAccount); + WalletModel::keepupaccountInfo accinfo; + accinfo.Add_ =add; + accinfo.Coin_ = num; + pTallyOutAccount->setinfo(accinfo); +} +void WalletView::gotoBackTallyAccountPage(bool isok) +{ + if(isok) + pTallyAccount->setfinishinfo(); + tallyDialogPageStack->setCurrentWidget(pTallyAccount); +} +void WalletView::gotoTallyAddressPage() +{ + tallyDialogPageStack->setCurrentWidget(pTallySelectAddressPage); +} diff --git a/.svn/pristine/3a/3a44d6d1422c4bc34b84ddf7a36f16b70d49d9e3.svn-base b/.svn/pristine/3a/3a44d6d1422c4bc34b84ddf7a36f16b70d49d9e3.svn-base new file mode 100644 index 0000000..22f615a --- /dev/null +++ b/.svn/pristine/3a/3a44d6d1422c4bc34b84ddf7a36f16b70d49d9e3.svn-base @@ -0,0 +1,431 @@ +#include "ecoincreatedialog.h" +#include "forms/ui_ecoincreatedialog.h" +#include "optionsmodel.h" +#include "ipchainunits.h" +#include "wallet/coincontrol.h" +#include "guiutil.h" +#include +#include +#include +#include +#include +#include "md5thread.h" +#include "log/log.h" +#include +#include "CDateEdit.h" +using namespace std; +using namespace boost; +ecoincreatedialog::ecoincreatedialog(QWidget *parent) : + QWidget(parent), + ui(new Ui::ecoincreatedialog) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint); + ui->scrollArea->setFrameShape(QFrame::NoFrame); + connect( this,SIGNAL(showmd5(QString)),this,SLOT(showmd5Slot(QString))); + ui->coinlabel->setMaxLength(16); + ui->coinname->setPlaceholderText(tr("Tokens are not more than 8 characters (without IPC)")); + ui->picEdit->setPlaceholderText(tr("Token pic")); + ui->coinmd5->setPlaceholderText(tr("Token hash")); + ui->coinNum->setPlaceholderText(tr("Token num")); + ui->coinlabel->setPlaceholderText(tr("Please enter no more than 16 characters (without IPC)")); + QRegExp regx("[a-zA-Z]+$"); + QValidator *validator = new QRegExpValidator(regx, ui->coinlabel ); + ui->coinname->setValidator( validator ); + + QRegExp regxcoinNum("[0-9]+$"); + QValidator *validatorxcoinNum = new QRegExpValidator(regxcoinNum, ui->coinNum ); + ui->coinNum->setValidator( validatorxcoinNum ); + connect(ui->coinNum,SIGNAL(textChanged(const QString&)),this,SLOT(slotTextChanged(const QString&))); + ui->chooseaddBtn->hide(); + ui->comboBox->addItems(QStringList()<<"0"<<"1"<<"2"<<"3"<<"4"<<"5"<<"6"<<"7"<<"8"); +} + +ecoincreatedialog::~ecoincreatedialog() +{ + delete ui; +} + +void ecoincreatedialog::setinfoclean() +{ + ui->coinname->setText(""); + ui->picEdit->setText(""); + ui->coinmd5->setText(""); + ui->coinNum->setText(""); + ui->coinlabel->setText(""); + ui->TimeEdit->setText(""); + ui->tiplabel->setText(""); + ui->chooseaddBtn->setText(tr("chooseaddress")); + CDateEdit* pdataedit = (CDateEdit*)ui->TimeEdit; + if(model) + pdataedit->setDate(model->getSySQDate()); +} + +void ecoincreatedialog::setModel(WalletModel *_model) +{ + this->model = _model; +} +void ecoincreatedialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) +{ + LOG_WRITE(LOG_INFO,"processSendCoinsReturn",m_sendcoinerror.c_str()); + QPalette pa; + pa.setColor(QPalette::WindowText,Qt::black); + + switch(sendCoinsReturn.status) + { + case WalletModel::InvalidAddress: + ui->tiplabel->setText(tr("The recipient address is not valid. Please recheck.")); + break; + case WalletModel::InvalidAmount: + ui->tiplabel->setText(tr("The amount to pay must be larger than 0.")); + break; + case WalletModel::AmountExceedsBalance: + ui->tiplabel->setText(tr("The amount exceeds your balance.")); + break; + case WalletModel::AmountWithFeeExceedsBalance: + ui->tiplabel->setText(tr("The total exceeds your balance when the 0.001IPC/KB transaction fee is included.").arg(msgArg)); + break; + case WalletModel::DuplicateAddress: + ui->tiplabel->setText(tr("Duplicate address found: addresses should only be used once each.")); + break; + case WalletModel::TransactionCreationFailed: + if(m_sendcoinerror!="") + ui->tiplabel->setText(m_sendcoinerror.c_str()); + else + ui->tiplabel->setText(tr("Transaction creation failed!")); + break; + case WalletModel::TransactionCommitFailed: + { + if("bad-Token-tokensymbol-repeat" == sendCoinsReturn.reasonCommitFailed ) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-tokensymbol-repeat")); + + } + else if("bad-Token-tokenhash-repeat" == sendCoinsReturn.reasonCommitFailed ) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-tokenhash-repeat")); + + } + else if("bad-Token-Multi-inType" == sendCoinsReturn.reasonCommitFailed ) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-Multi-inType")); + + } + else if("bad-Token-Reg-issueDate(Regtime)" == sendCoinsReturn.reasonCommitFailed) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-Reg-issueDate(Regtime)")); + + } + else if("bad-Token-regtotoken-value-unequal" == sendCoinsReturn.reasonCommitFailed ) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-regtotoken-value-unequal")); + } + else if("bad-Token-Label-contain-errvalue"== sendCoinsReturn.reasonCommitFailed) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-Label-contain-errvalue")); + } + else if("bad-Token-Symbol-contain-errvalue"== sendCoinsReturn.reasonCommitFailed) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-Symbol-contain-errvalue")); + } + else if("bad-Token-value-unequal" == sendCoinsReturn.reasonCommitFailed ) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-value-unequal")); + } + else if("bad-Token-Multi-outType" == sendCoinsReturn.reasonCommitFailed ) + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: bad-Token-Multi-outType")); + } + else + { + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed)); + } + } + break; + case WalletModel::AbsurdFee: + ui->tiplabel->setText(tr("A fee higher is considered an absurdly high fee.")); + break; + case WalletModel::PaymentRequestExpired: + ui->tiplabel->setText(tr("Payment request expired.")); + break; + case WalletModel::OK: + default: + return; + } +} +void ecoincreatedialog::setAddress(QString address) +{ + ui->chooseaddBtn->setText(address); + m_address = address; +} + +void ecoincreatedialog::on_CreateeCoinButton_pressed() +{ + m_sendcoinerror = ""; + ui->tiplabel->setText(tr("")); + QString coinNumText = ui->coinNum->text(); + coinNumText = coinNumText.replace(",",""); + Q_EMIT GetOldAddress(); + if(!model || !model->getOptionsModel()) + return; + if("" == ui->coinname->text() || + "" == ui->coinname->text() || + "" == ui->picEdit->text() || + "" == ui->coinmd5->text() || + "" == coinNumText || + "" ==ui->chooseaddBtn->text() || + "" == ui->coinlabel->text() || + "" == ui->TimeEdit->text()) + { + ui->tiplabel->setText(tr("input info")); + return; + } + if(0 == coinNumText.toDouble()) + { + ui->tiplabel->setText(tr("num error")); + return; + } + QString stdlabel = ui->coinlabel->text(); + std::string tempstr = stdlabel.toStdString(); + if(tempstr.length()>16) + { + ui->tiplabel->setText(tr("Token label size too large.")); + return; + } + int accuracy = ui->comboBox->currentIndex(); + + QList recipients; + SendCoinsRecipient recipient; + bool valid = true; + + if (recipient.paymentRequest.IsInitialized()) + return ; + + recipient.address = ui->chooseaddBtn->text(); + recipient.label = ""; + recipient.amount = 0; + recipient.message = ""; + recipient.fSubtractFeeFromAmount = false; + recipients.append(recipient); + fNewRecipientAllowed = false; + QString add1 = ui->chooseaddBtn->text() ; + uint64_t amount1 = (uint64_t)coinNumText.toULongLong(); + + if(amount1<=0||amount1>1000000000000000000) + { + ui->tiplabel->setText(tr("coinNum (0,1000000000000000000]")); + return ; + } + int iaccuracy = accuracy; + uint64_t iamount1= amount1; + while(iaccuracy--){ + if(iamount1>1000000000000000000) + { + ui->tiplabel->setText(tr("coinNum*10^accuracy must no more than 1000000000000000000")); + return ; + } + iamount1=iamount1*10; + } + if(iamount1<=0||iamount1>1000000000000000000) + { + ui->tiplabel->setText(tr("coinNum*10^accuracy must no more than 1000000000000000000")); + return ; + } + bool was_locked = model->getEncryptionStatus() == WalletModel::Locked; + if(!model->CheckPassword()) + { + ui->tiplabel->setText(tr("password error")); + return ; + } + // If wallet is still locked, unlock was failed or cancelled, mark context as invalid + valid = model->getEncryptionStatus() != WalletModel::Locked; + WalletModel::UnlockContext ctx(model, valid, was_locked); + if(!ctx.isValid()) + { + fNewRecipientAllowed = true; + } + // prepare transaction for getting txFee earlier + WalletModelTransaction currentTransaction(recipients); + WalletModel::SendCoinsReturn prepareStatus; + // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled + CCoinControl ctrl; + if (model->getOptionsModel()->getCoinControlFeatures()) + { + // ctrl = *CoinControlDialog::coinControl; + } + else + { + ctrl.nConfirmTarget = 1; + } + QString timestr = ui->TimeEdit->text(); + int inttimr = strTimeToInt(timestr); + if(inttimr<=0) + { + ui->tiplabel->setText(tr("time error")); + return ; + } + + QStringList data; + QString coin_name = ui->coinname->text(); + QString coin_md5 =ui->coinmd5->text(); + QString coin_label = ui->coinlabel->text(); + data<<"\""+coin_name+"\""; + data<prepareeCoinsCreateTransaction(data,add1,currentTransaction, &ctrl); + m_sendcoinerror = model->m_sendcoinerror; + processSendCoinsReturn(prepareStatus, + BitcoinUnits::formatWithUnit(eTag, currentTransaction.getTransactionFee())); + + if(prepareStatus.status != WalletModel::OK) { + fNewRecipientAllowed = true; + + return; + } + CAmount txFee = currentTransaction.getTransactionFee(); + + // Format confirmation message + QStringList formatted; + Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients()) + { + + QString amount = "" + BitcoinUnits::formatHtmlWithUnit(0, rcp.amount); + amount.append(""); + QString address = "" + rcp.address; + address.append(""); + QString recipientElement; + QString q = QString("%1").arg(rcp.amount); + if (!rcp.paymentRequest.IsInitialized()) + { + if(rcp.label.length() > 0) + { + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); + recipientElement.append(QString(" (%1)").arg(address)); + } + else + { + recipientElement = tr("%1 to %2").arg(amount, address); + } + } + else if(!rcp.authenticatedMerchant.isEmpty()) + { + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); + } + else + { + recipientElement = tr("%1 to %2").arg(amount, address); + } + + formatted.append(recipientElement); + } + + QString questionString = tr("Are you sure you want to send?"); + questionString.append("

%1"); + + if(txFee > 0) + { + questionString.append("
"); + questionString.append(BitcoinUnits::formatHtmlWithUnit(0, txFee)); + questionString.append(" "); + questionString.append(tr("added as transaction fee")); + questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)"); + } + + // add total amount in all subdivision units + questionString.append("
"); + CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; + QStringList alternativeUnits; + Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) + { + alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(0, totalAmount)); + } + + questionString.append(tr("Total Amount %1") + .arg(BitcoinUnits::formatHtmlWithUnit(0, totalAmount))); + + questionString.append(QString("
(=%2)
") + .arg(alternativeUnits.join(" " + tr("or") + "
"))); + + WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); + + if (sendStatus.status == WalletModel::OK) + { + Q_EMIT CreateeCoinSuccess(); + } + else + { + m_sendcoinerror = model->m_sendcoinerror; + } + processSendCoinsReturn(sendStatus); + fNewRecipientAllowed = true; + +} + +void ecoincreatedialog::on_chooseaddBtn_pressed() +{ + Q_EMIT selectaddress(3); +} +void ecoincreatedialog::fileToMd5(std::string strmd5) +{ + Q_EMIT showmd5(QString::fromStdString(strmd5)); +} +void ecoincreatedialog::showmd5Slot(QString strmd5) +{ + ui->coinmd5->setText(strmd5); +} + +void ecoincreatedialog::on_openPicBtn_pressed() +{ + + QString file_full, file_name, file_path; + QFileInfo fi; + file_full = QFileDialog::getOpenFileName(this,"","/","image Files(*.bmp *.jpg *.png *.jpeg)"); + if(file_full == "") + { + return; + } + fi = QFileInfo(file_full); + file_name = fi.fileName(); + file_path = fi.absolutePath(); + ui->picEdit->setText(file_name); + + boost::thread m_ptr(fileToMd5Thread,file_full.toStdString(),string("eCoinCreate"),(void*)this); + m_ptr.detach(); + +} +int ecoincreatedialog::strTimeToInt(QString time) +{ + QDate date = QDate::fromString(time,"yyyy-MM-dd"); + QDateTime datetime(date); + int inttime = datetime.toTime_t(); + return inttime; +} +uint64_t ecoincreatedialog::getpow10(uint64_t oldnum,int n) +{ + while (n--) { + oldnum = oldnum*10; + } + return oldnum; +} +void ecoincreatedialog::slotTextChanged(const QString & text) +{ + QString textContent = text; + QString numtexttemp = textContent; + QString textContenttemp = numtexttemp.replace(",",""); + uint64_t maxLength = 1000000000000000000; + uint64_t amount1 = (uint64_t)textContenttemp.toULongLong(); + if(amount1 > maxLength) + { + int position = ui->coinNum->cursorPosition(); + textContent.chop(1); + ui->coinNum->setText(textContent); + ui->coinNum->setCursorPosition(position-1); + } +} diff --git a/.svn/pristine/3c/3c945115ba2895c7ab1a13e1c5d790bca81314ae.svn-base b/.svn/pristine/3c/3c945115ba2895c7ab1a13e1c5d790bca81314ae.svn-base new file mode 100644 index 0000000..538b316 --- /dev/null +++ b/.svn/pristine/3c/3c945115ba2895c7ab1a13e1c5d790bca81314ae.svn-base @@ -0,0 +1,424 @@ +#include "ipcdetails.h" +#include "ui_ipcdetails.h" +#include "walletmodel.h" +#include "guiutil.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "log/log.h" +extern bool g_bIpchainShutdown; +bool firststart = 1; +bool g_staticfirststart2 = 1; +QStringList type = QStringList()<setupUi(this); + QPalette patime; + patime.setColor(QPalette::WindowText,Qt::gray); + ui->label_t7->setPalette(patime); + ui->label_t8->setPalette(patime); + m_nTimerId = startTimer(10000); + m_pushButton_transfer_show = false; + m_pushButton_authorization_show = false; + m_bCanTransaction = false; +} + +IpcDetails::~IpcDetails() +{ + killTimer(m_nTimerId); + delete ui; +} + +void IpcDetails::on_pushButton_back_pressed() +{ + Q_EMIT back(); +} + +void IpcDetails::on_pushButton_transfer_pressed() +{ + Q_EMIT gotoIpcTransferTransactionPage(ui->label_t1->text()); +} + +void IpcDetails::on_pushButton_authorization_pressed() +{ + Q_EMIT gotoIpcAuthorizationTransactionPage(ui->label_t1->text()); +} +void IpcDetails::resetinfo(int index) +{ + + ui->pushButton_transfer->hide(); + ui->pushButton_authorization->hide(); + ui->pushButton_toimage->hide(); + m_bCanTransaction = false; + if(walletModel) + { + m_index = index; + QStringList back = walletModel->getInfoFromIpcList(index); + if(back.size()>=13) + { + QString name = back.at(0); + if(name.size()>38) + name.insert(37," "); + ui->label_t1->setText(name); + int typenum = back.at(1).toInt(); + QStringList type = QStringList()<=0&&typenum<8) + { + ui->label_t2->setText(type.at(typenum)); + } + else + { + ui->label_t2->setText(tr("patent")); + } + ui->label_t3->setText(back.at(2)); + if(back.at(3) == "forever") + { + ui->label_t4->setText(tr("forever")); + }else + ui->label_t4->setText(back.at(3)); + m_ui_label_t5 = back.at(4); + if("ownership"==back.at(4)){ + ui->label_t5->setText(tr("ownership")); + }else{ + ui->label_t5->setText(tr("Use right")); + } + m_ui_label_t6 = back.at(5); + if("can authorization"==back.at(5)){ + ui->label_t6->setText(tr("can authorization")); + }else{ + ui->label_t6->setText(tr("cannot authorization")); + } + + ui->label_t7->setText(back.at(6)); + ui->label_t8->setText(back.at(7)); + ui->label_t8->setAlignment(Qt::AlignLeft); + ui->label_t9->setText(back.at(8)); + + ui->label_t10->setText(back.at(9)); + if(back.at(10)=="1") + { + m_pushButton_transfer_show = true; + } + else + { + m_pushButton_transfer_show = false; + } + if(back.at(11)=="1") + { + m_pushButton_authorization_show = true; + } + else + { + m_pushButton_authorization_show = false; + } + + m_strExclusive = back.at(12); + QString canusetime = back.at(13); + ui->label_inuretime->setText(canusetime); + + if(back.at(8) == "") + { + ui->label_t9->hide(); + ui->label_18->hide(); + ui->line_9->hide(); + }else{ + if(firststart){ + firststart = 0; + ui->label_t9->document()->adjustSize(); + } + int row = ui->label_t9->document()->size().height(); + ui->label_t9->setMinimumHeight(row+5); + ui->label_t9->setMaximumHeight(row+5); + ui->label_t9->show(); + ui->label_18->show(); + ui->line_9->show(); + } + + if(walletModel->GetDepthInMainChain(index)>=8){ + bool bcanshow = canTransaction(); + if(m_pushButton_transfer_show&&bcanshow) + ui->pushButton_transfer->show(); + if(m_pushButton_authorization_show&&bcanshow) + ui->pushButton_authorization->show(); + ui->pushButton_toimage->show(); + }else{ + ui->pushButton_transfer->hide(); + ui->pushButton_authorization->hide(); + ui->pushButton_toimage->hide(); + } + } + } +} +void IpcDetails::on_pushButton_toimage_pressed() +{ + QLocale locale; + QString file_full, file_name, file_path; + QFileInfo fi; + file_full = QFileDialog::getSaveFileName(this,"","/","image Files(*.png)"); + if(file_full == "") + { + return; + } + if(file_full.right(4)!=".png"){ + file_full=file_full+".png"; + QFile mFile(file_full); + if(mFile.exists()) + { + QMessageBox::StandardButton reply; + if( locale.language() == QLocale::Chinese ) + reply = QMessageBox::question(this, "IPC", "该文件名已存在,是否要替换?", QMessageBox::Yes | QMessageBox::No); + else + reply = QMessageBox::question(this, "IPC", "The file name already exists. Do you want to replace it?", QMessageBox::Yes | QMessageBox::No); + if(reply != QMessageBox::Yes) + { + return; + } + } + } + + bool isChineseLanguage = false; + + QImage image ; + image = QPixmap(":res/png/ipcinfoEnglish.png").toImage(); + QPainter painter(&image); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + + QPen pen = painter.pen(); + pen.setColor(QColor(198,156,109)); + QFont font = painter.font(); + font.setFamily("SimplifiedChinese"); + + int fontsize = 18; + font.setPixelSize(fontsize); + + painter.setPen(pen); + painter.setFont(font); + QString label_t1_text = ui->label_t1->text(); + if(label_t1_text.size()<=20){ + painter.drawText(195,286+fontsize,label_t1_text); + + }else if(label_t1_text.size()<=40){ + painter.drawText(195,286-20+fontsize,label_t1_text.mid(0, 20)); + painter.drawText(195,286+fontsize,label_t1_text.mid(20, 20)); + } + else if(label_t1_text.size()<=60){ + painter.drawText(195,286-40+fontsize,label_t1_text.mid(0, 20)); + painter.drawText(195,286-20+fontsize,label_t1_text.mid(20, 20)); + painter.drawText(195,286+fontsize,label_t1_text.mid(40, 20)); + } + else{ + painter.drawText(195,286-60+10+fontsize,label_t1_text.mid(0, 20)); + painter.drawText(195,286-40+10+fontsize,label_t1_text.mid(20, 20)); + painter.drawText(195,286-20+10+fontsize,label_t1_text.mid(40, 20)); + painter.drawText(195,286+10+fontsize,label_t1_text.mid(60, 20)); + } + + QString label_t2_text = ui->label_t2->text(); + QStringList type = QStringList()<label_t5->text(); + if(isChineseLanguage){ + if(label_t5_text == tr("ownership")){ + painter.drawText(266,350+fontsize,"所有权"); + } + else{ + painter.drawText(266,350+fontsize,"使用权"); + } + }else{ + painter.drawText(266,350+fontsize,label_t5_text); + } + + QString label_t3_text = ui->label_t3->text(); + label_t3_text = QChineseTimeToEnglish(label_t3_text); + painter.drawText(240,385+fontsize,label_t3_text); + + QString label_t4_text = ui->label_t4->text(); + if(isChineseLanguage){ + if(label_t4_text == "forever")label_t4_text= "永久"; + painter.drawText(202,416+fontsize,label_t4_text); + }else{ + label_t4_text = QChineseTimeToEnglish(label_t4_text); + painter.drawText(202,416+fontsize,label_t4_text); + } + + QString inuretime = ui->label_inuretime->text(); + inuretime = QChineseTimeToEnglish(inuretime); + painter.drawText(246,451+fontsize,inuretime); + + QString label_t6_text = m_ui_label_t6;//ui->label_t6->text(); + if(isChineseLanguage){ + if(label_t6_text == tr("can authorization")){ + painter.drawText(300,484+fontsize,"可再授权"); + } + else{ + painter.drawText(300,484+fontsize,"不可再授权"); + } + }else{ + painter.drawText(300,484+fontsize,label_t6_text); + } + QString hashstr = ui->label_t7->text(); + + + QString str = hashstr;; + + char* ch; + + QByteArray ba = str.toLatin1(); + + ch=ba.data(); + + QRcode *code = QRcode_encodeString(ch, 0, QR_ECLEVEL_L, QR_MODE_8, 1); + if (!code) + { + } + else + { + QImage qrImage = QImage(code->width , code->width , QImage::Format_RGB32); + unsigned char *p = code->data; + for (int y = 0; y < code->width; y++) + { + for (int x = 0; x < code->width; x++) + { + qrImage.setPixel(x , y , ((*p & 1) ? 0x363535 : 0xC69C6D)); + p++; + } + } + QRcode_free(code); + QImage qrAddrImage = QImage(140, 140, QImage::Format_RGB32); + QPainter painter1(&qrAddrImage); + painter1.drawImage(0, 0, qrImage.scaled(140, 140)); + font = GUIUtil::fixedPitchFont(); + font.setPixelSize(0);//25 + painter1.setFont(font); + QRect paddedRect = qrAddrImage.rect(); + paddedRect.setHeight(140); + painter1.end(); + painter.drawPixmap(277,569, QPixmap::fromImage(qrAddrImage)); + } + font.setPixelSize(fontsize); + font.setBold(true); + painter.setFont(font); + painter.drawText(183,517+fontsize,hashstr); + image.save(file_full,0); +} + +QString IpcDetails::QChineseTimeToEnglish(QString chtime) +{ + chtime.replace("年","/"); + chtime.replace("日",""); + chtime.replace("月","/"); + chtime.replace("永久","forever"); + return chtime; +} + +QString IpcDetails::getStartTime() +{ + return ui->label_t3->text(); +} + +void IpcDetails::timerEvent( QTimerEvent *event ) +{ + if(m_index<0) + return; + if(g_bIpchainShutdown)return; + try{ + int num = walletModel->GetDepthInMainChain(m_index); + if(num >= 0) + { + QString queren = QString::number(num); + queren.append("+"); + ui->label_t10->setText(queren); + if(num >=8) + { + if(m_pushButton_transfer_show&&canTransaction()) + ui->pushButton_transfer->show(); + if(m_pushButton_authorization_show&&canTransaction()) + ui->pushButton_authorization->show(); + ui->pushButton_toimage->show(); + + } + else if(num < 3){ + ui->label_inuretime->setText(walletModel->GetTimeOfTokenInChain(m_index)); + } + } + } + catch(...){ + LOG_WRITE(LOG_INFO,"IpcDetails::timerEvent Error"); + } +} +bool IpcDetails::canTransaction() +{ + if(!m_bCanTransaction) + m_bCanTransaction = walletModel->CanIPCTransaction(m_index); + return m_bCanTransaction; +} diff --git a/.svn/pristine/41/412b82edf2e47e8f85b67a8544a9ce80d997a3f0.svn-base b/.svn/pristine/41/412b82edf2e47e8f85b67a8544a9ce80d997a3f0.svn-base new file mode 100644 index 0000000..0999f59 --- /dev/null +++ b/.svn/pristine/41/412b82edf2e47e8f85b67a8544a9ce80d997a3f0.svn-base @@ -0,0 +1,6669 @@ + + + + + + AddressBookPage + + Right-click to edit address or label + 鼠标右击编辑地址或标签 + + + Create a new address + 创建新地址 + + + &New + 新建(&N) + + + Copy the currently selected address to the system clipboard + 复制当前选中的地址到系统剪贴板 + + + &Copy + 复制(&C) + + + C&lose + 关闭(&l) + + + Delete the currently selected address from the list + 从列表中删除选中的地址 + + + Export the data in the current tab to a file + 导出当前分页里的数据到文件 + + + &Export + 导出(&E) + + + &Delete + 删除(&D) + + + Choose the address to send coins to + 选择要付钱过去的地址 + + + Choose the address to receive coins with + 选择要收钱进来的地址 + + + C&hoose + 选择 + + + Sending addresses + 付款地址 + + + Receiving addresses + 收款地址 + + + These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins. + 这些是你要付款过去的知产币地址。在付钱之前,务必要检查金额和收款地址是否正确。 + + + These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction. + 这些是你用来收款的知产币地址。建议在每次交易时,都使用一个新的收款地址。 + + + &Copy Address + 复制地址 + + + Copy &Label + 复制标签 + + + &Edit + 编辑 + + + Export Address List + 导出地址列表 + + + Comma separated file (*.csv) + 逗号分隔文件 (*.csv) + + + Exporting Failed + 导出失败 + + + There was an error trying to save the address list to %1. Please try again. + 存储地址列表到 %1 时发生错误。请再试一次。 + + + + TxViewDelegate + + IsSended + 已发送 + + + IsRecved + 已接收 + + + + AddressTableModel + + Label + 标签 + + + My Address + 我的地址 + + + Address + 地址 + + + (no label) + (无标签) + + + + AskPassphraseDialog + + Passphrase Dialog + 密码对话框 + + + Enter passphrase + 输入密码 + + + New passphrase + 新密码 + + + Repeat new passphrase + 重复新密码 + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. + 输入钱包的新密码。<br/>密码请用<b>10 个以上的随机字符</b>,或是<b>8 个以上的字词</b>。 + + + Encrypt wallet + 加密钱包 + + + This operation needs your wallet passphrase to unlock the wallet. + 这个操作需要你的钱包密码来解锁钱包。 + + + Unlock wallet + 解锁钱包 + + + This operation needs your wallet passphrase to decrypt the wallet. + 这个操作需要你的钱包密码来把钱包解密。 + + + Decrypt wallet + 解密钱包 + + + Change passphrase + 修改密码 + + + Enter the old passphrase and new passphrase to the wallet. + 请输入钱包的旧密码和新密码。 + + + Confirm wallet encryption + 确认钱包加密 + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>! + 警告: 如果把钱包加密后又忘记密码,你就会从此<b>失去其中所有的知产币了</b>! + + + Are you sure you wish to encrypt your wallet? + 你确定要把钱包加密吗? + + + Wallet encrypted + 钱包已加密 + + + %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer. + %1 现在要关闭,以完成加密过程。请注意,加密钱包不能完全防止入侵你的电脑的恶意程序偷取钱币。 + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + 重要: 请改用新产生的有加密的钱包文件,来取代旧钱包文件的备份。为了安全性,当你开始使用新的有加密的钱包后,旧钱包文件的备份就不能再使用了。 + + + Wallet encryption failed + 钱包加密失败 + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + 因为内部错误导致钱包加密失败。你的钱包还是没加密。 + + + The supplied passphrases do not match. + 提供的密码不yi'zhi。 + + + Wallet unlock failed + 钱包解锁失败 + + + The passphrase entered for the wallet decryption was incorrect. + 输入用来解密钱包的密码不正确。 + + + Wallet decryption failed + 钱包解密失败 + + + Wallet passphrase was successfully changed. + 钱包密码修改成功。 + + + Warning: The Caps Lock key is on! + 警告: 大写字母锁定已开启! + + + + BanTableModel + + IP/Netmask + IP/网络掩码 + + + Banned Until + 在此之前禁止: + + + + BitcoinGUI + + Sign &message... + 消息签名(&M)... + + + Synchronizing with network... + 正在与网络同步... + + + &Overview + 概况(&O) + + + Node + 节点 + + + Show general overview of wallet + 显示钱包概况 + + + &Transactions + 交易记录(&T) + + + Browse transaction history + 查看交易历史 + + + E&xit + 退出(&X) + + + Quit application + 退出程序 + + + &About %1 + 关于 %1 + + + Show information about %1 + 显示 %1 相关信息 + + + About &Qt + 关于Qt(&Q) + + + Show information about Qt + 显示 Qt 相关信息 + + + &Options... + 选项(&O)... + + + Modify configuration options for %1 + 修改%1配置选项 + + + &Encrypt Wallet... + 加密钱包(&E)... + + + &Backup Wallet... + 备份钱包(&B)... + + + &Change Passphrase... + 更改密码(&C)... + + + &Sending addresses... + 正在发送地址(&S)... + + + &Receiving addresses... + 正在接收地址(&R)... + + + Open &URI... + 打开 &URI... + + + Click to disable network activity. + 点击禁用网络活动。 + + + Network activity disabled. + 网络活动已禁用。 + + + Click to enable network activity again. + 点击重新开启网络活动。 + + + Reindexing blocks on disk... + 正在为数据块重建索引... + + + Send coins to a Bitcoin address + 向一个知产币地址发送知产币 + + + Backup wallet to another location + 备份钱包到其他文件夹 + + + Change the passphrase used for wallet encryption + 更改钱包加密口令 + + + &Debug window + 调试窗口(&D) + + + Open debugging and diagnostic console + 打开调试和诊断控制台 + + + &Verify message... + 验证消息(&V)... + + + Bitcoin + 知产币 + + + IPC + 知产币 + + + Wallet + 钱包 + + + &Send + 发送(&S) + + + &Receive + 接收(&R) + + + &Show / Hide + 显示 / 隐藏(&S) + + + Show or hide the main Window + 显示或隐藏主窗口 + + + Encrypt the private keys that belong to your wallet + 对钱包中的私钥加密 + + + Sign messages with your Bitcoin addresses to prove you own them + 用知产币地址关联的私钥为消息签名,以证明您拥有这个知产币地址 + + + Verify messages to ensure they were signed with specified Bitcoin addresses + 校验消息,确保该消息是由指定的知产币地址所有者签名的 + + + &File + 文件(&F) + + + &Settings + 设置(&S) + + + &Help + 帮助(&H) + + + Tabs toolbar + 分页工具栏 + + + Request payments (generates QR codes and bitcoin: URIs) + 请求支付 (生成二维码和 bitcoin: URI) + + + Show the list of used sending addresses and labels + 显示用过的发送地址和标签的列表 + + + Show the list of used receiving addresses and labels + 显示用过的接收地址和标签的列表 + + + Open a bitcoin: URI or payment request + 打开一个 bitcoin: URI 或支付请求 + + + &Command-line options + 命令行选项(&C) + + + %n active connection(s) to Bitcoin network + + %n 个到知产链网络的活动连接 + + + + Indexing blocks on disk... + 正在为数据块建立索引... + + + Processing blocks on disk... + 正在处理数据块... + + + Processed %n block(s) of transaction history. + + 已处理 %n 个交易历史数据块。 + + + + %1 behind + 落后 %1 + + + Last received block was generated %1 ago. + 最新收到的区块产生于 %1。 + + + Transactions after this will not yet be visible. + 在此之后的交易尚未可见 + + + Error + 错误 + + + Warning + 警告 + + + Information + 信息 + + + Up to date + 已是最新 + + + Show the %1 help message to get a list with possible Bitcoin command-line options + 显示 %1 帮助信息,获取可用命令行选项列表 + + + %1 client + %1 客戶 + + + Connecting to peers... + 正在连接到节点…… + + + Catching up... + 更新中... + + + Date: %1 + 日期: %1 + + + Amount: %1 + 金额: %1 + + + Type: %1 + 类型: %1 + + + Label: %1 + 标签: %1 + + + Address: %1 + 地址: %1 + + + Sent transaction + 发送交易 + + + Incoming transaction + 流入交易 + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + 钱包已被<b>加密</b>,当前为<b>解锁</b>状态 + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + 钱包已被<b>加密</b>,当前为<b>锁定</b>状态 + + + A fatal error occurred. Bitcoin can no longer continue safely and will quit. + 发生严重错误。客户端无法安全地继续运行,即将退出。 + + + wallet.dat corrupt, salvage failed + 钱包文件错误,启动失败。 + + + + CoinControlDialog + + Coin Selection + 选择钱币 + + + Quantity: + 总量: + + + Bytes: + 字节: + + + Amount: + 金额: + + + Fee: + 费用: + + + Dust: + 小额: + + + After Fee: + 加上交易费用后: + + + Change: + 变更 : + + + (un)select all + (不)全选 + + + Tree mode + 树状模式 + + + List mode + 列表模式 + + + Amount + 金额 + + + Received with label + 按标签收款 + + + Received with address + 按地址收款 + + + Date + 日期 + + + Confirmations + 确认 + + + Confirmed + 已确认 + + + Copy address + 复制地址 + + + Copy label + 复制标签 + + + Copy amount + 复制金额 + + + Copy transaction ID + 复制交易识别码 + + + Lock unspent + 锁定未花费 + + + Unlock unspent + 解锁未花费 + + + Copy quantity + 复制数目 + + + Copy fee + 复制手续费 + + + Copy after fee + 复制计费后金额 + + + Copy bytes + 复制字节数 + + + Copy dust + 复制零散金额 + + + Copy change + 复制找零金额 + + + (%1 locked) + (锁定 %1 枚) + + + yes + + + + no + + + + This label turns red if any recipient receives an amount smaller than the current dust threshold. + 当任何一个收款金额小于目前的零散金额上限时,文字会变红色。 + + + Can vary +/- %1 satoshi(s) per input. + 每组输入可能有 +/- %1 个 satoshi 的误差。 + + + (no label) + (无标签) + + + change from %1 (%2) + 找零前是 %1 (%2) + + + (change) + (找零) + + + + EditAddressDialog + + Edit Address + 编辑地址 + + + &Label + 标签(&L) + + + The label associated with this address list entry + 与此地址相关的标签项 + + + The address associated with this address list entry. This can only be modified for sending addresses. + 该地址已与地址列表中的条目关联,只能被发送地址修改。 + + + &Address + 地址(&A) + + + New receiving address + 新建收款地址 + + + New sending address + 新建付款地址 + + + Edit receiving address + 编辑收款地址 + + + Edit sending address + 编辑付款地址 + + + The entered address "%1" is not a valid Bitcoin address. + 输入的地址 %1 并不是有效的知产币地址。 + + + The entered address "%1" is already in the address book. + 输入的地址 %1 已经存在地址簿。 + + + Could not unlock wallet. + 无法将钱包解锁。 + + + New key generation failed. + 产生新的密钥失败了。 + + + + FreespaceChecker + + A new data directory will be created. + 一个新的数据目录将被创建。 + + + name + 名称 + + + Directory already exists. Add %1 if you intend to create a new directory here. + 目录已存在。如果您打算在这里创建一个新目录,添加 %1。 + + + Path already exists, and is not a directory. + 路径已存在,并且不是一个目录。 + + + Cannot create data directory here. + 无法在此创建数据目录。 + + + + HelpMessageDialog + + version + 版本 + + + (%1-bit) + (%1 位) + + + About %1 + 關於 %1 + + + Command-line options + 命令行选项 + + + Usage: + 使用: + + + command-line options + 命令行选项 + + + UI Options: + 界面选项: + + + Choose data directory on startup (default: %u) + 在启动时选择目录(默认%u) + + + Set language, for example "de_DE" (default: system locale) + 设置语言, 例如“zh-CN”(默认:系统语言) + + + Start minimized + 启动时最小化 + + + Set SSL root certificates for payment request (default: -system-) + 设置付款请求的SSL根证书(默认:-系统-) + + + Show splash screen on startup (default: %u) + 显示启动画面(默认:%u) + + + Reset all settings changed in the GUI + 重置图形界面所有的变更设置 + + + + Intro + + Welcome + 欢迎 + + + Welcome to %1. + 歡迎來到 %1 + + + As this is the first time the program is launched, you can choose where %1 will store its data. + 由于这是第一次启动此程序,您可以选择%1的数据所存储的位置 + + + %1 will download and store a copy of the Bitcoin block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + %1 会下载并存储一份知产币区块链的副本。至少有 %2GB 的数据会存储到这个目录中,并且还会持续增长。另外钱包资料也会储存在这个目录。 + + + Use the default data directory + 使用默认的数据目录 + + + Use a custom data directory: + 使用自定义的数据目录: + + + Error: Specified data directory "%1" cannot be created. + 错误:无法创建 指定的数据目录 "%1" + + + Error + 错误 + + + %n GB of free space available + + 有 %n GB 空闲空间 + + + + (of %n GB needed) + + (需要%n GB空间) + + + + + ModalOverlay + + Form + 表单 + + + Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below. + 近期交易可能尚未显示,因此当前余额可能不准确。以上信息将在与知产链网络完全同步后更正。详情如下 + + + Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network. + 尝试使用受未可见交易影响的余额将不被网络接受。 + + + Number of blocks left + 剩余区块数量 + + + Unknown + 未知 + + + Last block time + 上一数据块时间 + + + Progress + 进度 + + + Progress increase per hour + 每小时进度增加 + + + calculating... + 正在计算 + + + Estimated time left until synced + 预计剩余同步时间 + + + Hide + 隐藏 + + + + OpenURIDialog + + Open URI + 打开 URI + + + Open payment request from URI or file + 打开来自URI或文件的付款请求 + + + URI: + URI: + + + Select payment request file + 选择付款请求文件 + + + Select payment request file to open + 选择要打开的付款请求文件 + + + + OptionsDialog + + Options + 选项 + + + &Main + 主要(&M) + + + Automatically start %1 after logging in to the system. + 在登入系统后自动启动 %1 + + + &Start %1 on system login + 系统登入时启动 %1 + + + Size of &database cache + 数据库缓存大小(&D) + + + MB + MB + + + Number of script &verification threads + 脚本验证线程数(&V) + + + Accept connections from outside + 接收外部连接 + + + Allow incoming connections + 允许流入连接 + + + IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + 代理的 IP 地址 (例如 IPv4: 127.0.0.1 / IPv6: ::1) + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + 窗口被关闭时最小化而不是退出应用程序。当此选项启用时,应用程序只会在菜单中选择退出时退出。 + + + Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + 出现在交易的选项卡的上下文菜单项的第三方网址 (例如:区块链接查询) 。 %s的URL被替换为交易哈希。多个的URL需要竖线 | 分隔。 + + + Third party transaction URLs + 第三方交易网址 + + + Active command-line options that override above options: + 有效的命令行参数覆盖上述选项: + + + Reset all client options to default. + 恢复客户端的缺省设置 + + + &Reset Options + 恢复缺省设置(&R) + + + &Network + 网络(&N) + + + (0 = auto, <0 = leave that many cores free) + (0 = 自动, <0 = 离开很多免费的核心) + + + W&allet + 钱包(&A) + + + Expert + 专家 + + + Enable coin &control features + 启动货币控制功能(&C) + + + If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed. + 如果禁用未确认的零钱,则零钱至少需要1个块确认才能使用。同时账户余额计算会受到影响。 + + + &Spend unconfirmed change + 使用未经确认的零钱(&S) + + + Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled. + 自动在路由器中打开知产币端口。只有当您的路由器开启了 UPnP 选项时此功能才有效。 + + + Map port using &UPnP + 使用 &UPnP 映射端口 + + + Connect to the Bitcoin network through a SOCKS5 proxy. + 通过 SOCKS5 代理连接知产链网络。 + + + &Connect through SOCKS5 proxy (default proxy): + 通过 SO&CKS5 代理连接(默认代理): + + + Proxy &IP: + 代理服务器 &IP: + + + &Port: + 端口(&P): + + + Port of the proxy (e.g. 9050) + 代理端口(例如 9050) + + + Used for reaching peers via: + 连接到同伴的方式: + + + Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type. + 如果默认的SOCKS5代理被用于在该网络下连接同伴,则显示 + + + IPv4 + IPv4 + + + IPv6 + IPv6 + + + Tor + Tor + + + Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services. + 在 Tor 匿名网络下通过不同的 SOCKS5 代理连接网络 + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services: + 通过Tor隐藏服务连接节点时 使用不同的SOCKS5代理 + + + &Window + 窗口(&W) + + + &Hide the icon from the system tray. + 不在通知区显示图标 + + + Hide tray icon + 不显示通知区图标 + + + Show only a tray icon after minimizing the window. + 最小化窗口后仅显示托盘图标 + + + &Minimize to the tray instead of the taskbar + 最小化到托盘(&M) + + + M&inimize on close + 单击关闭按钮最小化(&I) + + + &Display + 显示(&D) + + + User Interface &language: + 用户界面语言(&L): + + + The user interface language can be set here. This setting will take effect after restarting %1. + 可以在这里设定用户界面的语言。这个设定在重启 %1 后才会生效。 + + + &Unit to show amounts in: + 知产币金额单位(&U): + + + Choose the default subdivision unit to show in the interface and when sending coins. + 选择知产币单位。 + + + Whether to show coin control features or not. + 是否需要交易源地址控制功能。 + + + &OK + 确定(&O) + + + &Cancel + 取消(&C) + + + default + 默认 + + + none + + + + Confirm options reset + 确认恢复缺省设置 + + + Client restart required to activate changes. + 更改生效需要重启客户端。 + + + Client will be shut down. Do you want to proceed? + 客户端即将关闭,您想继续吗? + + + This change would require a client restart. + 此更改需要重启客户端。 + + + The supplied proxy address is invalid. + 提供的代理服务器地址无效。 + + + + OverviewPage + + IsSended + 已发送 + + + IsRecved + 已接收 + + + Form + 表单 + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet. + 现在显示的消息可能是过期的。在连接上知产链网络节点后,您的钱包将自动与网络同步,但是这个过程还没有完成。 + + + Watch-only: + 查看-只有: + + + Available: + 可使用的余额: + + + Your current spendable balance + 您当前可使用的余额 + + + Pending: + 等待中的余额: + + + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + 尚未确认的交易总额,未计入当前余额 + + + Immature: + 未成熟的: + + + Mined balance that has not yet matured + 尚未成熟的挖矿收入余额 + + + Balances + 余额 + + + Total: + 总额: + + + Your current total balance + 您当前的总余额 + + + Your current balance in watch-only addresses + 您当前 观察地址(watch-only address)的余额 + + + Spendable: + 可使用: + + + Recent transactions + 最近交易记录 + + + Unconfirmed transactions to watch-only addresses + 观察地址(watch-only address)的未确认交易记录 + + + Mined balance in watch-only addresses that has not yet matured + 观察地址(watch-only address)中尚未成熟(matured)的挖矿收入余额: + + + Current total balance in watch-only addresses + 观察地址(watch-only address)中的当前总余额 + + + + Settingwidget + + For your account security, please set the password. No password, anyone can use your currency. + 为了您的账户安全,请立即设置支付密码。没有支付密码,任何一个可以打开您钱包的人都能使用您的知产币。 + + + toset + 去设置 + + + nochange + 先不用 + + + + ecoindialog + + TokenNum: + 我的数字资产 + + + chooseadd + 选择接收地址 + + + CreateToken + 创建我的数字资产 + + + + sendcoinsaffrimwidget + + + eCoinSendresultDialog + + + success + 交易成功 + + + + eCoinSenddialog + + + name + 数字资产名称 + + + + Send + 发送数字资产 + + + + num + 数字资产余额 + + + + Address + 数字资产地址 + + + + available + 可用余额 + + + + time + 发行时间 + + + + SendCoinsAffrimWidget + + The passphrase entered for the wallet decryption was incorrect. + 输入用来解密钱包的密码不正确。 + + + The amount to pay must be larger than 0. + 支付金额不合理。 + + + success + 交易成功 + + + The recipient address is not valid. Please recheck. + 接收人地址无效。请重新检查。 + + + The amount exceeds your balance. + 金额超出您的账上余额。 + + + The total exceeds your balance when the 0.001IPC/KB transaction fee is included. + 计入 0.001IPC/KB 交易费后的金额超出您的账上余额。 + + + Duplicate address found: addresses should only be used once each. + 发现重复地址:每个地址应该只使用一次。 + + + Transaction creation failed! + 交易创建失败! + + + The transaction was rejected with the following reason: %1 + 交易因以下原因拒绝:%1 + + + A fee higher than %1 is considered an absurdly high fee. + 超过 %1 的交易费被认为是荒谬的高费率。 + + + Payment request expired. + 付款请求已过期。 + + + reciver + 收件人 + + + send + 点击发送 + + + address + 发送到 + + + coins + 发送金额 + + + InputPsd + 请输入密码 + + + The recipient address is not valid. Please recheck. + 地址不可用 + + + + eCoinSelectAddress + + newaddress + 添加新地址作为数字资产地址 + + + + AddBookWidget + + General contact AddressBook + 常用联系人地址簿 + + + + ecoincreatedialog + + accuracy + 数字资产精度 + + + num error + 请输入正确金额 + + + Prompt: Payment of the transaction fee and the transaction fee of 100ipc. + 提示:需要缴纳手续费及100ipc的交易费。 + + + coinNum (0,1000000000000000000] + 数字资产数量范围 (0,1000000000000000000] + + + coinNum*10^accuracy must no more than 1000000000000000000 + 数字资产数量*10^精度不能超过1000000000000000000 + + + + The recipient address is not valid. Please recheck. + 接收人地址无效。请重新检查。 + + + The amount to pay must be larger than 0. + 支付金额必须大于0。 + + + The amount exceeds your balance. + 金额超出您的账上余额。 + + + The total exceeds your balance when the 0.001IPC/KB transaction fee is included. + 计入 0.001IPC/KB 交易费后的金额超出您的账上余额。 + + + Duplicate address found: addresses should only be used once each. + 发现重复地址:每个地址应该只使用一次。 + + + Transaction creation failed! + 交易创建失败! + + + The transaction was rejected with the following reason: %1 + 交易因以下原因拒绝:%1 + + + The transaction was rejected with the following reason: bad-Token-tokensymbol-repeat + 交易因以下原因拒绝: 数字资产标识重复 + + + The transaction was rejected with the following reason: bad-Token-Label-contain-errvalue + 交易因以下原因拒绝: 数字资产标签包含错误的值 + + + The transaction was rejected with the following reason: bad-Token-Symbol-contain-errvalue + 交易因以下原因拒绝: 数字资产标识包含错误的值 + + + The transaction was rejected with the following reason: bad-Token-Reg-issueDate(Regtime) + 交易因以下原因拒绝: 发行时间错误 + + + The transaction was rejected with the following reason: bad-Token-tokenhash-repeat + 交易因以下原因拒绝: 数字资产hash重复 + + + password error + 密码错误 + + + A fee higher is considered an absurdly high fee. + 交易费被认为是荒谬的高费率。 + + + Token label size too large. + 数字资产标签太长。 + + + Payment request expired. + 付款请求已过期。 + + + ok + 确认 + + + Createtoken + 创建数字资产 + + + TokenLabel + 数字资产标识 + + + Token pic + 未选择文件 + + + input info + 请输入信息 + + + PicLabel + 图片标识 + + + openpic + 浏览... + + + hash + 数字资产hash + + + TokenNum + 数字资产数量 + + + TokenTag + 数字资产标签 + + + Address + 数字资产地址 + + + chooseaddress + 选择地址 + + + TimeLabel + 发行时间 + + + Tokens are not more than 8 characters (without IPC) + 数字资产名称,不超过8个字符(不出现IPC字样) + + + Token hash + 根据您选择的标识图片生成 + + + Token num + 发行数量(例如:3000000) + + + Please enter no more than 16 characters (without IPC) + 请输入不超过16个字符(不出现IPC字样) + + + + overviewpage + + Available: + 可用余额: + + + Detained: + 锁定余额: + + + IsSended + 已发送 + + + IsRecved + 已接受 + + + Immature: + 待确认余额: + + + Pending: + 等待中的余额: + + + Balances + 余额 + + + Total: + 总额: + + + Spendable: + 可使用: + + + + PaymentServer + + Payment request error + 要求付款时发生错误 + + + Cannot start IPC: click-to-pay handler + 无法启动 IPC 协议的“ 一键支付”处理器 + + + URI handling + URI 处理 + + + Payment request fetch URL is invalid: %1 + 取得付款请求的 URL 无效: %1 + + + Invalid payment address %1 + 无效的付款地址 %1 + + + URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters. + 无法解析 URI 地址!可能是因为知产币地址无效,或是 URI 参数格式错误。 + + + Payment request file handling + 处理付款请求文件 + + + Payment request file cannot be read! This can be caused by an invalid payment request file. + 无法读取付款请求文件!可能是文件无效造成的。 + + + Payment request rejected + 付款请求已被拒绝 + + + Payment request network doesn't match client network. + 付款请求的网络类型跟客户端不符。 + + + Payment request expired. + 付款请求已过期。 + + + Payment request is not initialized. + 支付请求未成形。 + + + Unverified payment requests to custom payment scripts are unsupported. + 不支持到自定义付款脚本的未验证付款请求。 + + + Invalid payment request. + 无效的支付请求。 + + + Requested payment amount of %1 is too small (considered dust). + 请求支付的金额 %1 太小(就像尘埃)。 + + + Refund from %1 + 退款来自 %1 + + + Payment request %1 is too large (%2 bytes, allowed %3 bytes). + 支付请求 %1 太大 (%2 字节。只允许 %3 字节)。 + + + Error communicating with %1: %2 + %1: %2 通讯出错 + + + Payment request cannot be parsed! + 无法解析 付款请求! + + + Bad response from server %1 + 来自 %1 服务器的错误响应 + + + Network request error + 网络请求出错 + + + Payment acknowledged + 付款已确认 + + + + PeerTableModel + + User Agent + 用户代理 + + + Node/Service + 节点/服务 + + + NodeId + 节点ID + + + Ping + + + + + QObject + + Amount + 金额 + + + Enter a Bitcoin address (e.g. %1) + 请输入一个知产币地址 (例如 %1) + + + %1 d + %1 天 + + + %1 h + %1 小时 + + + %1 m + %1 分钟 + + + %1 s + %1 秒 + + + None + + + + N/A + 不可用 + + + %1 ms + %1 毫秒 + + + %n second(s) + + %n 秒 + + + + %n minute(s) + + %n 分钟 + + + + %n hour(s) + + %n 小时 + + + + %n day(s) + + %n 天 + + + + %n week(s) + + %n 周 + + + + %1 and %2 + %1 和 %2 + + + %n year(s) + + %n 年 + + + + %1 didn't yet exit safely... + %1 尚未安全退出 + + + + QObject::QObject + + Error: Specified data directory "%1" does not exist. + 错误:指定的数据目录“%1”不存在。 + + + Error: Cannot parse configuration file: %1. Only use key=value syntax. + 错误:无法解析配置文件:%1。只接受 key=value语法。 + + + Error: %1 + 错误:%1 + + + + QRImageWidget + + &Save Image... + 保存图片(&S)... + + + &Copy Image + 复制图片 + + + Save QR Code + 保存二维码 + + + PNG Image (*.png) + PNG 图像(*.png) + + + + RPCConsole + + N/A + 不可用 + + + Client version + 客户端版本 + + + &Information + 信息 + + + Debug window + 调试窗口 + + + General + 常规 + + + Using BerkeleyDB version + 使用的 BerkeleyDB 版本 + + + Datadir + 数据目录 + + + Startup time + 启动时间 + + + Network + 网络 + + + Name + 姓名 + + + Number of connections + 连接数 + + + Block chain + 数据链 + + + Current number of blocks + 当前数据块数量 + + + Memory Pool + 资金池 + + + Current number of transactions + 当前交易数量 + + + Memory usage + 内存使用 + + + Received + 收到 + + + Sent + 发送 + + + &Peers + 同伴(&P) + + + Banned peers + 节点黑名单 + + + Select a peer to view detailed information. + 选择节点查看详细信息。 + + + Whitelisted + 白名单 + + + Direction + 方向 + + + Version + 版本 + + + Starting Block + 正在启动数据块 + + + Synced Headers + 同步区块头 + + + Synced Blocks + 同步区块链 + + + User Agent + 用户代理 + + + Decrease font size + 缩小文字 + + + Increase font size + 放大文字 + + + Services + 服务 + + + Ban Score + 禁止得分 + + + Connection Time + 连接时间 + + + Last Send + 最后发送 + + + Last Receive + 最后接收 + + + Ping Time + Ping 时间 + + + The duration of a currently outstanding ping. + 目前这一次 ping 已经过去的时间。 + + + Ping Wait + Ping等待 + + + Min Ping + 最小Ping值 + + + Time Offset + 时间偏移 + + + Last block time + 上一数据块时间 + + + &Open + 打开(&O) + + + &Console + 控制台(&C) + + + &Network Traffic + 网络流量(&N) + + + &Clear + 清除(&C) + + + Totals + 总数 + + + In: + 输入: + + + Out: + 输出: + + + Debug log file + 调试日志文件 + + + Clear console + 清空控制台 + + + 1 &hour + 1 小时(&H) + + + 1 &day + 1 天(&D) + + + 1 &week + 1 周(&W) + + + 1 &year + 1 年(&Y) + + + &Disconnect + (&D)断开 + + + Ban for + 禁止 + + + &Unban + 重新允许 + + + Welcome to the %1 RPC console. + 欢迎使用 %1 的 RPC 控制台。 + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + 使用上下方向键浏览历史, <b>Ctrl-L</b>清除屏幕。 + + + Type <b>help</b> for an overview of available commands. + 使用 <b>help</b> 命令显示帮助信息。 + + + WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. + 警告: 已有骗子通过要求用户在此输入指令以盗取钱包。不要在没有完全理解命令规范时使用控制台。 + + + Network activity disabled + 网络活动已禁用 + + + %1 B + %1 字节 + + + %1 KB + %1 KB + + + %1 MB + %1 MB + + + %1 GB + %1 GB + + + (node id: %1) + (节点ID: %1) + + + via %1 + 通过 %1 + + + never + 从未 + + + Inbound + 传入 + + + Outbound + 传出 + + + Yes + + + + No + + + + Unknown + 未知 + + + + ReceiveCoinsDialog + + AddisUsing + 正在使用的地址 + + + new address + 使用新地址 + + + &Amount: + 总额(&A): + + + &Label: + 标签(&L): + + + &Message: + 消息(&M): + + + Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before. + 重复使用以前用过的接收地址。重用地址有安全和隐私方面的隐患。除非是为重复生成同一项支付请求,否则请不要这样做。 + + + R&euse an existing receiving address (not recommended) + 重用现有的接收地址(不推荐) + + + An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network. + 可在付款请求上备注一条信息,在打开付款请求时可以看到。注意:该消息不是通过知产链网络传送。 + + + An optional label to associate with the new receiving address. + 可为新建的收款地址添加一个标签。 + + + Use this form to request payments. All fields are <b>optional</b>. + 使用此表单要求付款。所有字段都是<b>可选</b>。 + + + An optional amount to request. Leave this empty or zero to not request a specific amount. + 可选的请求金额。留空或填零为不要求具体金额。 + + + Clear all fields of the form. + 清除此表单的所有字段。 + + + Clear + 清除 + + + Requested payments history + 请求付款的历史 + + + &Request payment + 请求付款(&R) + + + Show the selected request (does the same as double clicking an entry) + 显示选中的请求 (双击也可以显示) + + + Show + 显示 + + + Remove the selected entries from the list + 从列表中移除选中的条目 + + + Remove + 移除 + + + Copy URI + 复制URI + + + Copy + 复制 + + + Copy label + 复制标签 + + + Copy message + 复制消息 + + + Copy amount + 复制金额 + + + + ReceiveRequestDialog + + QR Code + 二维码 + + + Copy &URI + 复制 URI(&U) + + + Copy &Address + 复制地址(&A) + + + &Save Image... + 保存图片(&S)... + + + Request payment to %1 + 请求付款到 %1 + + + Payment information + 付款信息 + + + URI + URI + + + My Address + 我的地址 + + + Address + 地址 + + + Amount + 金额 + + + Label + 标签 + + + Message + 消息 + + + Resulting URI too long, try to reduce the text for label / message. + URI 太长,请试着精简标签或消息文本。 + + + Error encoding URI into QR Code. + 把 URI 编码成二维码时发生错误。 + + + + RecentRequestsTableModel + + Date + 日期 + + + Label + 标签 + + + Message + 消息 + + + (no label) + (无标签) + + + (no message) + (无消息) + + + (no amount requested) + (无请求金额) + + + Requested + 总额 + + + + TransactionRecord + + Open for %n more block(s) + + 再打开 %n 个数据块 + + + + Open until %1 + 至 %1 个数据块时开启 + + + conflicted with a transaction with %1 confirmations + 与一个有 %1 个块确认的交易冲突 + + + %1/offline + 离线 + + + 0/unconfirmed, %1 + 0/未确认,%1 + + + unconfirmed + 未确认 + + + in memory pool + 在内存池中 + + + not in memory pool + 不在内存池中 + + + abandoned + 已抛弃 + + + %1/unconfirmed + %1/未确认 + + + %1 confirmations + %1 已确认 + + + matures in %n more block(s) + + %n 个数据块后成熟(mature) + + + + not accepted + 未被接受 + + + own address + 自己的地址 + + + patent + 专利 + + + trademark + 商标 + + + Electronic document + 电子文档 + + + Photo + 照片 + + + Journalism + 新闻 + + + video + 视频 + + + audio frequency + 音频 + + + security code + 防伪码 + + + introduction + 简介 + + + + InfoWidget + + Unknown + 未知 + + + loading + 等待加载 + + + + infowidget + + please click + 请点击 + + + look info + 查询信息 + + + feedback information + 反馈信息 + + + The recent transaction is not displayed, so the current balanceis inaccurate.Correction after information synchronization + 近期交易可能尚未显示,因此当前余额可能不准确。以上信息将在与知产链网络完全同步后更正 + + + current balanceis inaccurate.Correction after information synchronization + 当前余额可能不准确。以上信息 + + + The recent transaction is not displayed, so the + 近期交易可能尚未显示,因此 + + + information synchronization + 将在与知产链网络完全同步后更正 + + + The recent transaction is not displayed, so the current balanceis inaccurate.Correction after information synchronization + 近期交易可能尚未显示,因此当前余额可能不准确。以上信息将在与知产链网络完全同步后更正。详情如下 + + + freeblock + 剩余区块数量 + + + Unknown + 未知 + + + loading + 正在加载 + + + lastblock + 上一数据块时间 + + + precent + 进度 + + + Progress + 进度 + + + please update new version + 请更新版本 + + + + PasswordSettingWidget + + + set password + 设置密码 + + + + The password for payment cannot be recovered. You must take a pen and copy it. + 支付密码不能恢复,一定要拿笔把它抄写下来。 + + + + ok + 确认 + + + + old password + 旧密码 + + + + Input password + 新密码 + + + + Confirm password + 确认新密码 + + + + please set old password + 请写入旧密码 + + + + please set new password + 请写入新密码 + + + + please set confirm password + 请确认新密码 + + + + please set password + 请写入密码 + + + + Wallet passphrase was successfully changed. + 密码设置成功 + + + + passwordset success + 密码设置成功 + + + + Wallet encryption failed,The passphrase entered for the wallet decryption was incorrect. + 钱包加密失败,密码输入的钱包解密是不正确的。 + + + + 6-8 characters, suggesting English numerals are mixed + 6-8个字符,建议英文数字混合 + + + + please check password + 密码不符合,请重新输入 + + + + Wallet encryption failed + 钱包加密失败 + + + + Password mismatch + 密码不匹配 + + + + Wallet unlock failed + 钱包解锁失败 + + + + Wallet decryption failed + 钱包解密失败 + + + + Wallet encryption failed + 钱包加密失败 + + + + please wait... + 请稍候... + + + + SendCoinsDialog + + send + 确认 + + + input num + 输入数额 + + + input label + 为这个地址添加一个标签,以便将它加入您的地址簿 + + + input receiver + 请输入一个知产币地址 + + + input info + 请输入信息 + + + Send Coins + 发送知产币 + + + Coin Control Features + 交易源地址控制功能 + + + Inputs... + 输入... + + + automatically selected + 自动选择 + + + Insufficient funds! + 存款不足! + + + Quantity: + 总量: + + + Bytes: + 字节: + + + Amount: + 金额: + + + Fee: + 费用: + + + After Fee: + 加上交易费用后: + + + Change: + 变更 : + + + If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. + 如果激活该选项,但是零钱地址用光或者非法,将会新生成零钱地址,转入零钱。 + + + Custom change address + 自定义零钱地址 + + + Transaction Fee: + 交易费用: + + + Choose... + 选择... + + + collapse fee-settings + 收起 费用设置 + + + per kilobyte + 每kb + + + If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte. + 如果自定义交易费设置为 1000聪而交易大小只有250字节,则“每千字节" 模式只支付250聪交易费, 而"最少"模式则支付1000聪。 大于1000字节的交易按每千字节付费。 + + + Hide + 隐藏 + + + input info + 输入信息 + + + input num + 输入数额 + + + input label + 发送为这个地址添加一个标签,以便将它加入您的地址簿 + + + input receiver + 请输入一个知产币地址 + + + The recipient address is not valid. Please recheck. + 接收人地址无效。请重新检查。 + + + input info + 请输入信息 + + + Send Coins + 发送知产币 + + + Coin Control Features + 交易源地址控制功能 + + + Inputs... + 输入... + + + automatically selected + 自动选择 + + + Insufficient funds! + 存款不足! + + + Quantity: + 总量: + + + Bytes: + 字节: + + + Amount: + 金额: + + + Fee: + 费用: + + + After Fee: + 加上交易费用后: + + + Change: + 变更 : + + + If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. + 如果激活该选项,但是零钱地址用光或者非法,将会新生成零钱地址,转入零钱。 + + + Custom change address + 自定义零钱地址 + + + Transaction Fee: + 交易费用: + + + Choose... + 选择... + + + collapse fee-settings + 收起 费用设置 + + + per kilobyte + 每kb + + + If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte. + 如果自定义交易费设置为 1000聪而交易大小只有250字节,则“每千字节" 模式只支付250聪交易费, 而"最少"模式则支付1000聪。 大于1000字节的交易按每千字节付费。 + + + Hide + 隐藏 + + + input num + 输入数额 + + + input label + 发送为这个地址添加一个标签,以便将它加入您的地址簿 + + + input receiver + 请输入一个知产币地址 + + + input info + 请输入信息 + + + Send Coins + 发送知产币 + + + Coin Control Features + 交易源地址控制功能 + + + Inputs... + 输入... + + + automatically selected + 自动选择 + + + Insufficient funds! + 存款不足! + + + Quantity: + 总量: + + + Bytes: + 字节: + + + Amount: + 金额: + + + Fee: + 费用: + + + After Fee: + 加上交易费用后: + + + Change: + 变更 : + + + If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. + 如果激活该选项,但是零钱地址用光或者非法,将会新生成零钱地址,转入零钱。 + + + Custom change address + 自定义零钱地址 + + + Transaction Fee: + 交易费用: + + + Choose... + 选择... + + + collapse fee-settings + 收起 费用设置 + + + per kilobyte + 每kb + + + If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte. + 如果自定义交易费设置为 1000聪而交易大小只有250字节,则“每千字节" 模式只支付250聪交易费, 而"最少"模式则支付1000聪。 大于1000字节的交易按每千字节付费。 + + + Hide + 隐藏 + + + total at least + 最小额 + + + Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process. + 交易量小时只支付最小交易费是可以的。但是请注意,当交易量大到超出网络可处理时您的交易可能永远无法确认。 + + + (read the tooltip) + (请注意提示信息) + + + Recommended: + 推荐: + + + Custom: + 自定义: + + + (Smart fee not initialized yet. This usually takes a few blocks...) + (智能交易费用 尚未初始化。 需要再下载一些数据块...) + + + normal + 一般 + + + fast + 快速 + + + Send to multiple recipients at once + 一次发送给多个接收者 + + + Add &Recipient + 添加收款人(&R) + + + Clear all fields of the form. + 清除此表单的所有字段。 + + + Dust: + 小额: + + + Clear &All + 清除所有(&A) + + + Balance: + 余额: + + + Confirm the send action + 确认发送货币 + + + S&end + 发送(&E) + + + Copy quantity + 复制数目 + + + Copy amount + 复制金额 + + + Copy fee + 复制手续费 + + + Copy after fee + 复制计费后金额 + + + Copy bytes + 复制字节数 + + + Copy dust + 复制零散金额 + + + Copy change + 复制找零金额 + + + %1 to %2 + %1 到 %2 + + + Are you sure you want to send? + 您确定要发出吗? + + + added as transaction fee + 已添加交易费 + + + Total Amount %1 + 总金额 %1 + + + or + + + + Confirm send coins + 确认发送货币 + + + The recipient address is not valid. Please recheck. + 接收人地址无效。请重新检查。 + + + The amount to pay must be larger than 0. + 支付金额必须大于0。 + + + The amount exceeds your balance. + 金额超出您的账上余额。 + + + The total exceeds your balance when the 0.001IPC/KB transaction fee is included. + 计入 0.001IPC/KB 交易费后的金额超出您的账上余额。 + + + Duplicate address found: addresses should only be used once each. + 发现重复地址:每个地址应该只使用一次。 + + + Transaction creation failed! + 交易创建失败! + + + The transaction was rejected with the following reason: %1 + 交易因以下原因拒绝:%1 + + + A fee higher than %1 is considered an absurdly high fee. + 超过 %1 的交易费被认为是荒谬的高费率。 + + + Payment request expired. + 付款请求已过期。 + + + %n block(s) + + %n 个区块 + + + + Pay only the required fee of %1 + 只支付必要费用 %1 + + + Estimated to begin confirmation within %n block(s). + + 预计 %n 个数据块后被确认。 + + + + Warning: Invalid Bitcoin address + 警告: 知产币地址无效 + + + Warning: Unknown change address + 警告:未知的更改地址 + + + The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? + 你选择的找零地址未被包含在本钱包中,你钱包中的部分或全部金额将被发送至该地址。你确定要这样做吗? + + + (no label) + (无标签) + + + Reciver + 收件人 + + + label + 标签 + + + Num + 数额 + + + + SendCoinsEntry + + A&mount: + 金额(&M) + + + Pay &To: + 付给(&T): + + + &Label: + 标签(&L): + + + Choose previously used address + 选择以前用过的地址 + + + This is a normal payment. + 这是笔正常的支付。 + + + The Bitcoin address to send the payment to + 付款目的地址 + + + Alt+A + Alt+A + + + Paste address from clipboard + 从剪贴板粘贴地址 + + + Alt+P + Alt+P + + + Remove this entry + 移除此项 + + + The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally. + 交易费将从发送总额中扣除。接收人将收到比您在金额框中输入的更少的知产币。如果选中了多个收件人,交易费平分。 + + + S&ubtract fee from amount + 从金额中减去交易费(&U) + + + Message: + 消息: + + + This is an unauthenticated payment request. + 这是一个未经验证的支付请求。 + + + This is an authenticated payment request. + 这是一个已经验证的支付请求。 + + + Enter a label for this address to add it to the list of used addresses + 请为此地址输入一个标签以将它加入用过的地址列表 + + + A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network. + bitcoin:URI 附带的备注信息,将会和交易一起存储,备查。 注意:该消息不会通过知产链网络传输。 + + + Pay To: + 支付给: + + + Memo: + 便条: + + + Enter a label for this address to add it to your address book + 为这个地址输入一个标签,以便将它添加到您的地址簿 + + + + SendConfirmationDialog + + Yes + + + + + ShutdownWindow + + %1 is shutting down... + 正在关闭 %1 ... + + + Do not shut down the computer until this window disappears. + 在此窗口消失前不要关闭计算机。 + + + + SplashScreen + + [testnet] + [测试网络] + + + + TrafficGraphWidget + + KB/s + KB/s + + + + TransactionDesc + + Status + 状态 + + + , has not been successfully broadcast yet + ,未被成功广播 + + + , broadcast through %n node(s) + + , 通过 %n 个节点广播 + + + + Date + 日期 + + + Source + + + + Generated + 生成 + + + From + 来自 + + + unknown + 未知 + + + To + + + + IPC名称 + IPCTitle + + + IPC类型 + IPCType + + + own address + 自己的地址 + + + watch-only + 观察地址(watch-only) + + + label + 标签 + + + Credit + 收入 + + + Debit + 支出 + + + Total debit + 总收入 + + + Total credit + 总支出 + + + Transaction fee + 交易费 + + + Net amount + 净额 + + + Message + 消息 + + + Comment + 备注 + + + Transaction ID + ID + + + Transaction total size + 交易总大小 + + + Output index + 输出索引 + + + Merchant + 商家 + + + Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + 生成的知产币在可以使用前必须有 %1 个成熟的区块。当您生成了此区块后,它将被广播到网络中以加入区块链。如果它未成功进入区块链,其状态将变更为“不接受”并且不可使用。这可能偶尔会发生,如果另一个节点比你早几秒钟成功生成一个区块。 + + + Debug information + 调试信息 + + + Transaction + 交易 + + + Inputs + 输入 + + + Amount + 金额 + + + true + + + + false + + + + + TransactionDescDialog + + This pane shows a detailed description of the transaction + 当前面板显示了交易的详细信息 + + + + TransactionTableModel + + Open for %n more block(s) + + 再打开 %n 个数据块 + + + + Open until %1 + 至 %1 个数据块时开启 + + + conflicted with a transaction with %1 confirmations + 与一个有 %1 个块确认的交易冲突 + + + %1/offline + 离线 + + + unconfirmed + 未确认 + + + 0/unconfirmed, %1 + 0/未确认,%1 + + + in memory pool + 在内存池中 + + + not in memory pool + 不在内存池中 + + + abandoned + 已抛弃 + + + %1/unconfirmed + %1/未确认 + + + %1 confirmations + %1 已确认 + + + matures in %n more block(s) + + %n 个数据块后成熟(mature) + + + + not accepted + 未被接受 + + + Date + 日期 + + + Type + 种类 + + + Label + 标签 + + + Open until %1 + 至 %1 个数据块时开启 + + + Offline + 掉线 + + + Unconfirmed + 未确认的 + + + Confirming (%1 of %2 recommended confirmations) + 确认中 (推荐 %2个块确认,已经有 %1个块确认) + + + Confirmed (%1 confirmations) + 已确认 (%1 条确认信息) + + + Conflicted + 冲突的 + + + Immature (%1 confirmations, will be available after %2) + 未成熟 (%1 个块确认,将在 %2 个后可用) + + + This block was not received by any other nodes and will probably not be accepted! + 此数据块未被任何其他节点接收,可能不被接受! + + + Generated but not accepted + 已生成但未被接受 + + + Received with + 收款 + + + Received from + 收款来自 + + + Sent to + 付款 + + + Payment to yourself + 付款给自己 + + + Mined + 挖矿所得 + + + watch-only + 观察地址(watch-only) + + + (n/a) + (不可用) + + + (no label) + (无标签) + + + Transaction status. Hover over this field to show number of confirmations. + 交易状态。 鼠标移到此区域可显示确认项数量。 + + + Date and time that the transaction was received. + 接收到交易的时间 + + + Type of transaction. + 交易类别。 + + + Whether or not a watch-only address is involved in this transaction. + 该交易中是否涉及 观察地址(watch-only address)。 + + + User-defined intent/purpose of the transaction. + 用户定义的该交易的意图/目的。 + + + Amount removed from or added to balance. + 从余额添加或移除的金额。 + + + + TransactionView + + All + 全部 + + + Today + 今天 + + + This week + 这星期 + + + This month + 这个月 + + + Last month + 上个月 + + + This year + 今年 + + + Range... + 指定范围... + + + Received with + 收款 + + + Sent to + 付款 + + + To yourself + 给自己 + + + Mined + 挖矿所得 + + + Other + 其它 + + + Enter address or label to search + 输入地址或标签进行搜索 + + + Min amount + 最小金额 + + + Abandon transaction + 放弃交易 + + + Copy address + 复制地址 + + + Copy label + 复制标签 + + + Copy amount + 复制金额 + + + Copy transaction ID + 复制交易识别码 + + + Copy raw transaction + 复制原始交易 + + + Copy full transaction details + 复制所有交易详情 + + + Edit label + 编辑标签 + + + Show transaction details + 显示交易详情 + + + Export Transaction History + 导出交易历史 + + + Comma separated file (*.csv) + 逗号分隔文件 (*.csv) + + + Confirmed + 已确认 + + + Watch-only + 观察地址(Watch-only) + + + Date + 日期 + + + Type + 种类 + + + Label + 标签 + + + Address + 地址 + + + ID + ID + + + Exporting Failed + 导出失败 + + + There was an error trying to save the transaction history to %1. + 导出交易历史到 %1 时发生错误。 + + + Exporting Successful + 导出成功 + + + The transaction history was successfully saved to %1. + 交易历史已成功保存到 %1。 + + + Range: + 范围: + + + to + + + + + UnitDisplayStatusBarControl + + Unit to show amounts in. Click to select another unit. + 金额单位。单击选择别的单位。 + + + + WalletFrame + + No wallet has been loaded. + 没有载入钱包。 + + + + WalletModel + + Send Coins + 发送知产币 + + + + WalletView + + &Export + 导出(&E) + + + Export the data in the current tab to a file + 导出当前分页里的数据到文件 + + + Backup Wallet + 备份钱包 + + + Wallet Data (*.dat) + 钱包文件(*.dat) + + + Backup Failed + 备份失败 + + + There was an error trying to save the wallet data to %1. + 尝试保存钱包数据至 %1 时发生错误。 + + + Backup Successful + 备份成功 + + + The wallet data was successfully saved to %1. + 钱包数据成功保存至 %1 。 + + + + bitcoin-core + + Options: + 选项: + + + Specify data directory + 指定数据目录 + + + Connect to a node to retrieve peer addresses, and disconnect + 连接一个节点并获取对端地址,然后断开连接 + + + Specify your own public address + 指定您的公共地址 + + + Accept command line and JSON-RPC commands + 接受命令行和 JSON-RPC 命令 + + + If <category> is not supplied or if <category> = 1, output all debugging information. + 如果<category>未提供或<category> = 1,输出所有调试信息。 + + + Prune configured below the minimum of %d MiB. Please use a higher number. + 修剪值被设置为低于最小值%d MiB,请使用更大的数值。 + + + Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) + 修剪:最后的钱包同步超过了修剪的数据。你需要通过 -reindex (重新下载整个区块链以防修剪节点) + + + Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. + 无法在开启修剪的状态下重扫描,请使用 -reindex重新下载完整的区块链。 + + + Error: A fatal internal error occurred, see debug.log for details + 错误:发生了致命的内部错误,详情见 debug.log 文件 + + + Fee (in %s/kB) to add to transactions you send (default: %s) + 为付款交易添加交易费 (%s/kB) (默认: %s) + + + Pruning blockstore... + 正在修剪区块存储... + + + Run in the background as a daemon and accept commands + 在后台运行并接受命令 + + + Unable to start HTTP server. See debug log for details. + 无法启动HTTP服务,查看日志获取更多信息 + + + Bitcoin Core + Bitcoin Core + + + The %s developers + %s 开发人员 + + + A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) + 当费用估计数据(default: %s)不足时将会启用的费率 (in %s/kB) + + + Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d) + 即使在无关联交易(默认: %d)时也接受来自白名单同行的关联交易 + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + 绑定指定的IP地址开始监听。IPv6地址请使用[host]:port 格式 + + + Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup + 删除钱包的所有交易记录,且只有用 -rescan参数启动客户端才能重新取回交易记录 + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + 当最佳区块变化时执行命令 (命令行中的 %s 会被替换成区块哈希值) + + + Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) + 设置脚本验证的程序 (%u 到 %d, 0 = 自动, <0 = 保留自由的核心, 默认值: %d) + + + Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly. + 请检测本机时间,若本地时钟错误,IPChain将无法正常工作 + + + + Cannot obtain a lock on data directory %s. %s is probably already running. + 无法获取被锁数据 %s. %s 数据正在运行. + + + + + The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct + 区块数据库包含未来的交易,这可能是由本机错误的日期时间引起。若确认本机日期时间正确,请重新建立区块数据库。 + + + Use UPnP to map the listening port (default: 1 when listening and no -proxy) + 使用UPnP暴露本机监听端口(默认:1 当正在监听且不使用代理) + + + -maxmempool must be at least %d MB + -maxmempool 最小为%d MB + + + <category> can be: + <category> 可能是: + + + Append comment to the user agent string + 为用户代理字符串附加说明 + + + Block creation options: + 数据块创建选项: + + + Cannot resolve -%s address: '%s' + 无法解析 - %s 地址: '%s' + + + Connection options: + 连接选项: + + + Copyright (C) %i-%i + 版权所有 (C) %i-%i + + + Corrupted block database detected + 检测发现数据块数据库损坏。请使用 -reindex参数重启客户端。 + + + Debugging/Testing options: + 调试/测试选项: + + + Do not load the wallet and disable wallet RPC calls + 不要加载钱包和禁用钱包的 RPC 调用 + + + Do you want to rebuild the block database now? + 你想现在就重建块数据库吗? + + + Enable publish hash block in <address> + 允许在<address>广播哈希区块 + + + Enable publish hash transaction in <address> + 允许在<address>广播哈希交易 + + + Enable publish raw block in <address> + 允许在<address>广播原始区块 + + + Enable publish raw transaction in <address> + 允许在<address>广播原始交易 + + + Enable transaction replacement in the memory pool (default: %u) + 保证内存池中的交易更换(默认:%u) + + + Error initializing block database + 初始化数据块数据库出错 + + + Error initializing wallet database environment %s! + Error initializing wallet database environment %s! + + + Error loading %s + 载入 %s 时发生错误 + + + Error loading block database + 导入数据块数据库出错 + + + Error opening block database + 导入数据块数据库出错 + + + Error: Disk space is low! + 错误:磁盘剩余空间低! + + + Failed to listen on any port. Use -listen=0 if you want this. + 监听端口失败。请使用 -listen=0 参数。 + + + Importing... + 导入中... + + + Incorrect or no genesis block found. Wrong datadir for network? + 不正确或没有找到起源区块。网络错误? + + + Invalid -onion address: '%s' + 无效的 -onion 地址:“%s” + + + Invalid amount for -fallbackfee=<amount>: '%s' + -fallbackfee 的无效数额=<amount>: '%s' + + + Keep the transaction memory pool below <n> megabytes (default: %u) + 保持交易内存池大小低于<n>MB(默认:%u) + + + Location of the auth cookie (default: data dir) + 认证Cookie的位置 (默认: data目录) + + + Not enough file descriptors available. + 没有足够的文件描述符可用。 + + + Only connect to nodes in network <net> (ipv4, ipv6 or onion) + 只连接 <net>网络中的节点 (ipv4, ipv6 或 onion) + + + Print version and exit + 打印版本信息并退出 + + + Prune cannot be configured with a negative value. + 修剪不能配置一个负数。 + + + Prune mode is incompatible with -txindex. + 修剪模式与 -txindex 不兼容。 + + + Set database cache size in megabytes (%d to %d, default: %d) + 设置以MB为单位的数据库缓存大小(%d 到 %d, 默认值: %d) + + + Set maximum block size in bytes (default: %d) + 设置最大区块大小 (默认: %d,单位字节) + + + Specify wallet file (within data directory) + 指定钱包文件(数据目录内) + + + The source code is available from %s. + 源代码可以在 %s 获得。 + + + Unsupported argument -benchmark ignored, use -debug=bench. + 忽略不支持的选项 -benchmark,使用 -debug=bench + + + Unsupported argument -debugnet ignored, use -debug=net. + 忽略不支持的选项 -debugnet,使用 -debug=net。 + + + Unsupported argument -tor found, use -onion. + 忽略不支持的选项 -tor,使用 -oinon + + + Use UPnP to map the listening port (default: %u) + 使用UPnp映射监听端口 (默认: %u) + + + Use the test chain + 使用测试链 + + + User Agent comment (%s) contains unsafe characters. + 用户代理评论(%s)包含不安全的字符。 + + + Verifying blocks... + 正在验证区块... + + + Verifying wallet... + 正在验证钱包... + + + Wallet %s resides outside data directory %s + 钱包 %s 在外部的数据目录 %s + + + Wallet debugging/testing options: + 钱包调试/测试选项: + + + Wallet options: + 钱包选项: + + + Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times + 允许来自指定地址的 JSON-RPC 连接。 <ip>为单一IP (如: 1.2.3.4), 网络/掩码 (如: 1.2.3.4/255.255.255.0), 网络/CIDR (如: 1.2.3.4/24)。该选项可多次指定。 + + + Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 + 绑定到指定地址和连接的白名单节点。 IPv6使用 [主机]:端口 格式 + + + Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) + 绑定到指定地址监听 JSON-RPC连接。 IPv6使用[主机]:端口 格式。该选项可多次指定 (默认: 绑定到所有接口) + + + Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) + 创建系统默认权限的文件,而不是 umask 077 (只在关闭钱包功能时有效) + + + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) + 发现自己的 IP 地址(默认: 监听并且无 -externalip 或 -proxy 时为 1) + + + Error: Listening for incoming connections failed (listen returned error %s) + 错误:监听外部连接失败 (监听返回错误 %s) + + + Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) + 当收到相关提醒或者我们看到一个长分叉时执行命令(%s 将替换为消息) + + + Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) + 交易费(in %s/kB)比这更小的在关联、挖掘和生成交易时将被视为零费交易 (默认: %s) + + + If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) + 如果未设置交易费用,自动添加足够的交易费以确保交易在平均n个数据块内被确认 (默认: %u) + + + Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) + -maxtxfee=<amount>: '%s' 的金额无效(交易费至少为 %s,以免交易滞留过久) + + + Maximum size of data in data carrier transactions we relay and mine (default: %u) + Maximum size of data in data carrier transactions we relay and mine (default: %u) + + + Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) + 为每个代理连接随机化凭据。这将启用 Tor 流隔离 (默认: %u) + + + Set maximum size of high-priority/low-fee transactions in bytes (default: %d) + 设置 高优先级/低交易费 交易的最大字节 (缺省: %d) + + + The transaction amount is too small to send after the fee has been deducted + 在交易费被扣除后发送的交易金额太小 + + + Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway + 白名单节点不能被DoS banned ,且转发所有来自他们的交易(即便这些交易已经存在于mempool中),常用于网关 + + + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain + 您需要使用 -reindex 重新构建数据库以返回未修剪的模式。这将重新下载整个区块链 + + + (default: %u) + (默认: %u) + + + Accept public REST requests (default: %u) + 接受公共 REST 请求 (默认: %u) + + + Automatically create Tor hidden service (default: %d) + 自动建立Tor隐藏服务 (默认:%d) + + + Connect through SOCKS5 proxy + 通过 SOCKS5 代理连接 + + + Error reading from database, shutting down. + 读取数据库出错,关闭中。 + + + Imports blocks from external blk000??.dat file on startup + 启动时从其他来源的 blk000??.dat 文件导入区块 + + + Information + 信息 + + + Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) + 无效的金额 -paytxfee=<amount>: '%s' (必须至少为 %s) + + + Invalid netmask specified in -whitelist: '%s' + -whitelist: '%s' 指定的网络掩码无效 + + + Keep at most <n> unconnectable transactions in memory (default: %u) + 内存中最多保留 <n> 笔孤立的交易 (默认: %u) + + + Need to specify a port with -whitebind: '%s' + -whitebind: '%s' 需要指定一个端口 + + + Node relay options: + 节点中继选项: + + + RPC server options: + RPC 服务器选项: + + + Reducing -maxconnections from %d to %d, because of system limitations. + 因为系统的限制,将 -maxconnections 参数从 %d 降到了 %d + + + Rescan the block chain for missing wallet transactions on startup + 重新扫描区块链以查找遗漏的钱包交易 + + + Send trace/debug info to console instead of debug.log file + 跟踪/调试信息输出到控制台,不输出到 debug.log 文件 + + + Send transactions as zero-fee transactions if possible (default: %u) + 发送时尽可能 不支付交易费用 (默认: %u) + + + Show all debugging options (usage: --help -help-debug) + 显示所有调试选项 (用法: --帮助 -帮助调试) + + + Shrink debug.log file on client startup (default: 1 when no -debug) + 客户端启动时压缩debug.log文件(缺省:no-debug模式时为1) + + + Signing transaction failed + 签署交易失败 + + + The transaction amount is too small to pay the fee + 交易金额太小,不足以支付交易费 + + + This is experimental software. + 这是实验性的软件。 + + + Tor control port password (default: empty) + Tor 控制端口密码 (默认值: 空白) + + + Tor control port to use if onion listening enabled (default: %s) + 开启监听 onion 连接时的 Tor 控制端口号 (默认值: %s) + + + Transaction amount too small + 交易量太小 + + + Transaction too large for fee policy + 费用策略的交易太大 + + + Transaction too large + 交易太大 + + + Unable to bind to %s on this computer (bind returned error %s) + 无法在此计算机上绑定 %s (绑定返回错误 %s) + + + Upgrade wallet to latest format on startup + 程序启动时升级钱包到最新格式 + + + Username for JSON-RPC connections + JSON-RPC 连接用户名 + + + Warning + 警告 + + + Warning: unknown new rules activated (versionbit %i) + 警告: 不明的交易规则被启用了(versionbit %i) + + + Whether to operate in a blocks only mode (default: %u) + 是否用块方进行 (%u) + + + Zapping all transactions from wallet... + 正在消除錢包中的所有交易... + + + ZeroMQ notification options: + ZeroMQ 通知选项: + + + Password for JSON-RPC connections + JSON-RPC 连接密码 + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + 当最佳数据块变化时执行命令 (命令行中的 %s 会被替换成数据块哈希值) + + + Allow DNS lookups for -addnode, -seednode and -connect + 使用 -addnode, -seednode 和 -connect 选项时允许查询DNS + + + Loading addresses... + 正在加载地址簿... + + + (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) + (1 = 保留 tx meta data , 如 account owner 和 payment request information, 2 = 不保留 tx meta data) + + + -maxtxfee is set very high! Fees this large could be paid on a single transaction. + 参数 -maxtxfee 设定了很高的金额!这是你一次交易就有可能付出的最高手续费。 + + + Do not keep transactions in the mempool longer than <n> hours (default: %u) + 不要让交易留在内存池中超过 <n> 个小时 (默认值: %u) + + + Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) + 当产生交易时,如果每千字节 (kB) 的手续费比这个值 (单位是 %s) 低,就视为没支付手续费 (默认值: %s) + + + How thorough the block verification of -checkblocks is (0-4, default: %u) + 数据块验证 严密级别 -checkblocks (0-4, 默认: %u) + + + Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) + 维护一份完整的交易索引, 用于 getrawtransaction RPC调用 (默认: %u) + + + Number of seconds to keep misbehaving peers from reconnecting (default: %u) + 限制 非礼节点 若干秒内不能连接 (默认: %u) + + + Output debugging information (default: %u, supplying <category> is optional) + 输出调试信息 (默认: %u, 提供 <category> 是可选项) + + + Support filtering of blocks and transaction with bloom filters (default: %u) + 支持用 Bloom 过滤器来过滤区块和交易(默认值: %u) + + + Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. + 网络版本字符串的总长度 (%i) 超过最大长度 (%i) 了。请减少 uacomment 参数的数目或长度。 + + + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) + 尝试保持上传带宽低于(MiB/24h),0=无限制(默认:%d) + + + Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. + 找到不再支持的 -socks 参数。现在只支持 SOCKS5 协议的代理服务器,因此不可以指定 SOCKS 协议版本。 + + + Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. + 一个不被支持的参数 -whitelistalwaysrelay 被忽略了。请使用 -whitelistrelay 或者 -whitelistforcerelay. + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) + 通过Tor隐藏服务连接节点时 使用不同的SOCKS5代理 (默认: %s) + + + Warning: Unknown block versions being mined! It's possible unknown rules are in effect + 警告: 未知的区块版本被挖掘!未知规则可能已生效 + + + (default: %s) + (默认: %s) + + + Always query for peer addresses via DNS lookup (default: %u) + 始终通过 DNS 查询节点地址 (默认: %u) + + + How many blocks to check at startup (default: %u, 0 = all) + 启动时检测多少个数据块(默认: %u, 0=所有) + + + Include IP addresses in debug output (default: %u) + 在调试输出中包含IP地址 (默认: %u) + + + Invalid -proxy address: '%s' + 无效的代理地址:%s + + + Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) + 使用 <port>端口监听 JSON-RPC 连接 (默认: %u ; testnet: %u) + + + Listen for connections on <port> (default: %u or testnet: %u) + 使用端口 <port> 监听连接 (默认: %u ; testnet: %u) + + + Maintain at most <n> connections to peers (default: %u) + 保留最多 <n> 条节点连接 (默认: %u) + + + Make the wallet broadcast transactions + 钱包广播事务处理 + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) + 每个连接的最大接收缓存,<n>*1000 字节 (默认: %u) + + + Maximum per-connection send buffer, <n>*1000 bytes (default: %u) + 每个连接的最大发送缓存,<n>*1000 字节 (默认: %u) + + + Prepend debug output with timestamp (default: %u) + 输出调试信息时,前面加上时间戳 (默认: %u) + + + Relay and mine data carrier transactions (default: %u) + Relay and mine data carrier transactions (default: %u) + + + Relay non-P2SH multisig (default: %u) + 是否转发 非P2SH格式的多签名交易 (默认: %u) + + + Set key pool size to <n> (default: %u) + 设置私钥池大小为 <n> (默认:%u) + + + Set the number of threads to service RPC calls (default: %d) + 设置RPC服务线程数 (默认: %d) + + + Specify configuration file (default: %s) + 指定配置文件 (默认: %s) + + + Specify connection timeout in milliseconds (minimum: 1, default: %d) + 指定连接超时毫秒数 (最小: 1, 默认: %d) + + + Specify pid file (default: %s) + 指定 pid 文件 (默认: %s) + + + Spend unconfirmed change when sending transactions (default: %u) + 付款时允许使用未确认的零钱 (默认: %u) + + + Starting network threads... + 正在启动网络线程... + + + Threshold for disconnecting misbehaving peers (default: %u) + 断开 非礼节点的阀值 (默认: %u) + + + Unknown network specified in -onlynet: '%s' + -onlynet 指定的是未知网络:%s + + + Insufficient funds + 金额不足 + + + Loading block index... + 正在加载区块索引... + + + Add a node to connect to and attempt to keep the connection open + 添加节点并与其保持连接 + + + Loading wallet... + 正在加载钱包... + + + Cannot downgrade wallet + 无法降级钱包 + + + Cannot write default address + 无法写入默认地址 + + + Rescanning... + 正在重新扫描... + + + Done loading + 加载完成 + + + Error + 错误 + + + + logon + + + restore from backup + 从备份中恢复 + + + + create wallet + 创建钱包 + + + + datapath + 存储路径 + + + + change + 浏览 + + + + + walletpagebuttons + + \n\n\nwallet + \n\n\n钱包 + + + wallet + 钱包 + + + + send + 发送 + + + + receive + 接收 + + + + IP + 知产 + + + + token + 数字资产 + + + + set + 设置 + + + + markbill + 记账 + + + + setdialog + + + set + 设置 + + + + address book + 地址簿 + + + + export + 导出 + + + + recovery + 恢复 + + + + set password + 设置密码 + + + + message authentication + 消息签名 + + + + message Verification + 验证消息 + + + + about wallet + 版本号 + + + + exportdialog + + + export + 导出 + + + + export position + 导出位置 + + + + password error + 密码错误 + + + + browse + 浏览 + + + + Unselected + 未选择文件 + + + + Show other options + 显示其他选项 + + + + set password + 设置密码 + + + + confirm password + 确认密码 + + + + confirm export + 确认导出 + + + + your password + 你的密码 + + + + repeat password + 重复输入密码 + + + + export failed + 导出失败 + + + + Please select export address + 请选择导出地址 + + + + wallet path: + 钱包路径 + + + + SetRecovery + + + recovery + 恢复 + + + + Select a backup file from your computer. + 从你的计算机选择一个备份文件 + + + + select + 浏览 + + + + password + 密码 + + + + your password + 你的密码 + + + + import + 确认恢复 + + + + Unselected + 未选择文件 + + + + password error + 密码错误 + + + + wallet recovery false. + 钱包恢复失败。 + + + + SetMessageAuthentication + + + message authentication + 验证消息 + + + + authentication + 验证消息 + + + + message signature + 消息签名 + + + + signature + 消息签名 + + + + password error + 密码错误 + + + + SetMessageAuthenticationTab + + + Enter the recipient's address, message, and signature below to validate the message. + 请在下面输入接收者地址,消息和签名以验证消息。 + + + + enter an IPC address + 请输入一个知产币地址 + + + + Message authentication successful. + 消息验证成功 + + + + Message validation failed. + 消息验证失败 + + + + message authentication + 验证消息 + + + + message + 消息 + + + + The entered address is invalid.Please check the address and try again. + 输入的地址非法,请检查后重试 + + + The signature did not match the message digest. + 签明和消息摘要不匹配 + + + Please check the signature and try again. + 请检查签名并重新设置 + + + Please input info + 请输入信息 + + + + Message verification failed. + 消息验证失败 + + + + Message verified. + 消息验证成功 + + + + Wallet unlock was cancelled. + 密码错误 + + + + password error + 密码错误 + + + + SetMessageSignature + + + Form + + + + Please input info + 请输入信息 + + + Private key for the entered address is not available. + 地址的私钥不合法。 + + + + You can use your address to sign the message / protocol to prove that you can receive the message sent to the address. + 您可以用你的地址对消息/协议进行签名,以证明您可以接收发送到该地址的知产币 + + + + enter an IPC address + 请输入一个知产币地址 + + + + message + 消息 + + + + Signature generated. + 签名已生成 + + + + Message signed. + 签名已生成 + + + + The address entered is illegal. Please check and try again. + 输入的地址非法,请检查后重试 + + + + The private key corresponding to the input address cannot be found. + 找不到输入地址对应的私钥 + + + + message signature + 消息签名 + + + + Wallet unlock was cancelled. + 密码错误 + + + + password error + 密码错误 + + + + ipcdialog + + + Ip Total + 知产总额 + + + + IP register + 知产登记 + + + + Inspection tag + 检验标签 + + + + IPC + 知产 + + + + IpcRegister + + + IP register + 知产登记 + + + + IP name + 知产名 + + + + please wait + 请稍候 + + + + input ipc name (No more than 75 words) + 请输入知产名(不超过75个汉字) + + + + IP type + 知产类型 + + + + patent + 专利 + + + + trademark + 商标 + + + + Electronic document + 电子文档 + + + + Photo + 照片 + + + + Journalism + 新闻 + + + + video + 视频 + + + + audio frequency + 音频 + + + + security code + 防伪码 + + + + Electronic tag + 电子标签 + + + + browse + 浏览... + + + + None + 未选择文件 + + + + brief introduction + 简介 + + + + select IP registration address + 选择知产登记地址 + + + + registration address + 登记地址 + + + + IP registtration fee + 知产登记费用 + + + + The registration fee is mainly used to pay the miners' knowledge of the production, transfer and transaction costs. + 知产登记费用主要用于向矿工支付知产转移交易手续费用 + + + + next + 下一步 + + + + AmountExceedsBalance + 余额不足 + + + + AmountWithFeeExceedsBalance + 余额不足 + + + + InvalidAddress + 地址错误 + + + + Other + 其它错误 + + + + please set Electronic tag + 请设置电子标签 + + + + Password error. + 密码错误! + + + + please input ipcname + 请输入知产名 + + + + please input brief + 请输入简介 + + + + bad-IPC-IPChash-repeat + 电子标签已注册 + + + + Vout-txLabel-length-out-of-bounds + 简介超出范围 + + + + brief too large + 简介超出范围 + + + + IpcRegisterInformation + + + IP register + 知产登记 + + + + IP name + 知产名 + + + + period + 授权期限 + + + + permanent + 永久 + + + + Your knowledge registration is related to the original electronic file. Please be careful to keep your original file as valid evidence + 您的知产登记与原始电子文件相关,请注意安全存储您的原始文件作为有效证据. + + + + IP type + 知产类型 + + + + patent + 专利 + + + + trademark + 商标 + + + + Electronic document + 电子文档 + + + + Photo + 照片 + + + + Journalism + 新闻 + + + + video + 视频 + + + + audio frequency + 音频 + + + + security code + 防伪码 + + + + introduction + 简介 + + + + type + 授权类型 + + + + ownership + 所有权 + + + + ok + 确认交易 + + + + Electronic tag + 电子标签 + + + + commit faild + 提交失败 + + + + bad-IPC-IPChash-repeat + 电子标签已注册 + + + + IpcDetails + + + IPC details + 知产详情 + + + + name + 知产名 + + + + + type + 知产类型 + + + + time + 时间 + + + + introduction + 简介 + + + + confirm + 确认 + + + + limit + 授权限制 + + + + term + 授权期限 + + + + Electronic tag + 电子标签 + + + + transfer IP + 知产转移 + + + + authorization IP + 知产授权 + + + + patent + 专利 + + + + trademark + 商标 + + + + Electronic document + 电子文档 + + + + Photo + 照片 + + + + Journalism + 新闻 + + + + video + 视频 + + + + audio frequency + 音频 + + + + security code + 防伪码 + + + start time + 开始时间 + + + inure time + 生效时间 + + + end time + 结束时间 + + + authorize type + 授权类型 + + + ownership + 所有权 + + + Use right + 使用权 + + + can authorization + 可再授权 + + + cannot authorization + 不可再授权 + + + forever + 永久 + + + Export certificate + 导出证书 + + + + IpcTransferTransaction + + + IP Transfer transaction + 知产转移交易 + + + + address + 接收地址 + + + + IPC name + 知产名称 + + + + + Please enter a receive address + 请输入接收地址 + + + + transfer fee + 知产转移费用 + + + + The transfer fee is mainly used to pay the miners' knowledge of the production, transfer and transaction costs + 知产转移费用主要用于向矿工支付知产转移交易手续费用 + + + + The balance in your wallet is insufficient to complete the knowledge transfer transaction + 您钱包里余额不足,无法完成知产转移交易 + + + + Confirm transaction + 确认交易 + + + + AmountExceedsBalance + 余额不足 + + + + AmountWithFeeExceedsBalance + 余额不足 + + + + InvalidAddress + 地址错误 + + + + + bad-txns-inputs-spent + 输入不合理 + + + + + Other + 其它错误 + + + + Password error. + 密码错误 + + + + IPC-owner-starttime-is-up-yet + 知产的有效期还没到 + + + + SuccessfulTrade + + + successful trade + 交易成功 + + + + successful export + 导出成功 + + + + successful revovery + 恢复成功 + + + successful psdset + 密码设置成功 + + + + IpcAuthorizationTransaction + + + authorization transaction + 知产授权交易 + + + + Receive address + 接收地址 + + + + IPC name + 知产名称 + + + + Licensing restrictions + 授权限制 + + + + start time + 开始日期 + + + + end time + 结束日期 + + + + Select date + 请选择日期 + + + + Knowledge licensing Fee + 知产授权费用 + + + + The fee for knowing the property is mainly used to pay the miners' knowledge of the production, transfer and transaction fees. + 知产授权费用主要用于向矿工支付知产转移交易手续费用 + + + + Exclusive restriction + 排他性限制 + + + + Please enter a receive address + 请输入接收地址 + + + + confirm transaction + 确认交易 + + + + Allow re authorization + 允许再授权 + + + + Final authority (not allowed to re authorize) + 最终授权(不允许再授权) + + + + Exclusive license + 独家授权 + + + + General authorization + 普通授权 + + + + AmountExceedsBalance + 余额不足 + + + + AmountWithFeeExceedsBalance + 余额不足 + + + + InvalidAddress + 地址错误 + + + + Other + 其它错误 + + + + date error + 请选择正确日期 + + + + Password error. + 密码错误 + + + + bad-IPC-timecheck-error + 输出日期超过输入的日期限定 + + + + bad-IPC-uniqueAuthor-timecheck-error + 日期限定不符 + + + + IPC-Author-starttime-is-up-yet + 知产时间权限未到 + + + + you can't uniqueauthorize,you have an authorize yet + 你已经有一个普通授权,不能再独家授权 + + + + + + You can't uniqueAuthorize ,you hava an Authorize yet! + 你已经有一个普通授权,不能再独家授权 + + + + + bad-txns-inputs-spent + 输入不合理 + + + + + bad-IPC-normalAuthor-timecheck-error + 起止日期不能超过输入的日期 + + + + bad-IPC-reAuthor-timecheck-error + 起止日期不能超过输入的日期 + + + + IPC-owner-starttime-is-up-yet + 知产还未进入有效期 + + + + IpcInspectionTag + + + Inspection tag + 检验标签 + + + + Please select the file below, enter the tag, and check the tag + 请在下面选择文件,输入电子标签已检验标签。 + + + + select + 浏览 + + + + None + 未选择文件 + + + + Generating an electronic tag + 生成的电子标签 + + + + Enter an electronic tag + 输入电子标签 + + + + Please upload the electronic tag. + 请上传电子标签 + + + + inspection tag + 检验标签 + + + + Please input the electronic tag. + 请输入电子标签 + + + + Please check the electronic tag. + 请检测电子标签 + + + + same + 检验标签成功 + + + + not same + 检验标签失败 + + + + TallyAccount + + + Form + + + + + err + + + + + out account + 退出记账 + + + + net error + 网络错误 + + + + General punishment.Long term abnormal bookkeeping, please standardize bookkeeping behavior. + 普通处罚 长期非正常记账,请规范记账行为。 + + + + Serious punishment You can't continue accounting. Quitting, please be patient. + 严重处罚 您已无法继续记账。正在退出,请耐心等待... + + + + Quitting, please be patient. + 正在退出,请耐心等待... + + + + Show the last 20 Accounting records. If you want to see more, you can go to the browser to view the block. + 显示最近20条记账记录。如需查看更多,可去区块浏览器查看。 + + + + time + 时间 + + + + money + 金额 + + + + Total income: + 记账总额 + + + + TallyApply + + + Form + + + + + bond + 保证金 + + + + After entering the accounting, your IPC will be locked, and the transaction can not be done. + 加入记账之后,您的保证金将处于锁定状态,不可以进行交易操作。 + + + + It's in sync. Please wait a moment. + 正在同步,请稍候... + + + + error + + + + + address + 地址 + + + + select address + 选择地址 + + + + Apply for accession + 申请加入 + + + + money error + 金额错误 + + + + address error + 地址错误 + + + + InvalidAddress + 无效地址 + + + + InvalidAmount + 无效金额 + + + + AmountExceedsBalance + 余额不足 + + + + AmountWithFeeExceedsBalance + 余额不足 + + + + DuplicateAddress + 重复地址 + + + + TransactionCreationFailed + 交易创建失败 + + + + TransactionCommitFailed + 交易提交失败 + + + + txn-campaign-JOIN_PUBKEY_IS_TIMEOUT_PUNISHED + 正在进行处罚,请检测网络,稍候重试。 + + + + txn-campaign-EXIT_PUBKEY_IS_DEPOSING + 正在进行退款,请稍后重试。 + + + + AbsurdFee + 不合理费用 + + + + PaymentRequestExpired + 支付请求过期 + + + + other error + 其它错误 + + + + password error + 密码错误 + + + + TallyClause + + + Form + + + + + terms of use + 使用条款 + + + + have readed + 已阅读 + + + + OK + 确认 + + + + Cancle + 取消 + + + + TallyDscribe + + + Form + + + + + I want to keep accounts + 我要记账 + + + + recording rules + 记账规则 + + + + TallyOutAccount + + + Form + + + + + After you withdraw from the account, you will not be able to receive bonus fees. + 退出记账后,您将不能通过记账获得奖励金。 + + + + Your deposit will be returned to your wallet at the end of this posting. + 您的保证金将在本次记账结束返还到您的钱包。 + + + + txn-campaign-EXIT_PUBKEY_NOT_EXIST_IN_LIST + 您不在当前记账列表中,请稍后重试。 + + + + OK + 确认退出 + + + + Cancle + 取消 + + + + InvalidAddress + 无效地址 + + + + InvalidAmount + 无效金额 + + + + AmountExceedsBalance + 余额不足 + + + + AmountWithFeeExceedsBalance + 余额不足 + + + + DuplicateAddress + 重复地址 + + + + TransactionCreationFailed + 交易创建失败 + + + + TransactionCommitFailed + 交易提交失败 + + + + AbsurdFee + 不合理费用 + + + + PaymentRequestExpired + 支付请求过期 + + + + other error + 其它错误 + + + + password error + 密码错误 + + + + Not join the campaign yet + 当前钱包并没有加入共识! + + + + Transaction amounts must not be negative + 交易中ipc数额不合法(小于0) + + + + Transaction must have at least one recipient + 收件人为空 + + + + ExitCampaign The Address which you want to ExitCampaign must have some money! + 退出竞选交易的钱包地址需要有余额来支付退出竞选的交易费 + + + + The Address which you want to ExitCampaign must have some money! + 退出竞选交易的钱包地址需要有余额来支付退出竞选的交易费 + + + + Transaction amount too small + 微尘交易,拒绝! + + + + Insufficient funds + 退出竞选交易的钱包地址需要有余额来支付退出竞选的交易费。 + + + + Keypool ran out, please call keypoolrefill first + 密钥池退出,请先激活密钥池。 + + + + The transaction amount is too small to send after the fee has been deducted + 微尘交易 + + + + Signing transaction failed + 交易签名失败 + + + + Transaction too large for fee policy + 不合法的交易费金额(小于最小的交易费费率费用) + + + + Transaction too large + 交易尺寸过大。 + + + + Transaction has too long of a mempool chain + 当前交易池没有足够空间放入构造的交易,构建失败。 + + + + WalletPassword + + + password + 密码 + + + + OK + 确定 + + + + sendhistory + + + Form + + + + SenderAdd + 发送到 + + + + PayFee + 手续费 + + + + date + 时间 + + + + IsSend + 已发送 + + + + payinfo + 确认 + + + + sendipchistory + + SenderAdd + 发送到 + + + patent + 专利 + + + trademark + 商标 + + + Electronic document + 电子文档 + + + Photo + 照片 + + + Journalism + 新闻 + + + video + 视频 + + + audio frequency + 音频 + + + security code + 防伪码 + + + Electronic tag + 电子标签 + + + Form + + + + ipcname + 已发送 + + + IsSend + 已发送 + + + ipctype + 知产类型 + + + ownership + 所有权 + + + Use right + 使用权 + + + can authorization + 可再授权 + + + cannot authorization + 不可再授权 + + + forever + 永久 + + + authtype + 授权类型 + + + authlimit + 授权限制 + + + authdate + 授权期限 + + + ipcdate + 时间 + + + ipcfee + 手续费 + + + ipcinfo + 确认 + + + + SendTokenHistory + + SenderAdd + 发送到 + + + fee + 手续费 + + + + Form + + + + + sendtoken + 已发送 + + + + date + 时间 + + + + info + 确认 + + + + RecvHistory + + add + 地址 + + + + Form + + + + + IsRecv + 已接收 + + + + date + 时间 + + + + info + 确认 + + + + recvipchistory + + + fee + 手续费 + + + + add + 地址 + + + authtype + 授权类型 + + + authlimit + 授权限制 + + + authdate + 授权期限 + + + patent + 专利 + + + trademark + 商标 + + + Electronic document + 电子文档 + + + Photo + 照片 + + + Journalism + 新闻 + + + video + 视频 + + + audio frequency + 音频 + + + security code + 防伪码 + + + Electronic tag + 电子标签 + + + ownership + 所有权 + + + Use right + 使用权 + + + can authorization + 可再授权 + + + cannot authorization + 不可再授权 + + + forever + 永久 + + + Form + + + + IsRecv + 已接收 + + + ipctype + 类型 + + + ipcdate + 时间 + + + ipcinfo + 确认 + + + + RecvTokenHistory + + + fee + 手续费 + + + + Form + + + + add + 地址 + + + + RecvToken + 已接收 + + + + date + 时间 + + + + info + 确认 + + + + SendResultWidget + + + Form + + + + + sucessful + 发送成功 + + + + ok + 确定 + + + + ecoinsendaffrimdialog + + + receiver + 收件人 + + + + + + TokenLabel + 代币名称 + + + + + + num + 发送金额 + + + + sendToken + 确认发送 + + + + input info + 请输入信息 + + + + The recipient address is not valid. Please recheck. + 收件人地址无效。请复查。 + + + + The amount to pay must be larger than 0. + 支付金额必须大于0。 + + + + The amount exceeds your balance. + 金额超过你的余额。 + + + + The total exceeds your balance. + 总数超过了你的余额。 + + + + Duplicate address found: addresses should only be used once each. + 重复地址:地址只能使用一次。 + + + + Transaction creation failed! + 事务创建失败! + + + + The transaction was rejected with the following reason + 该交易被否决的原因如下 + + + + A fee higher + 交易费被认为是荒谬的高费率。 + + + + Payment request expired. + 付款请求过期。 + + + + The tokenvalue is too big,you have not enough tokencoins. + 没有足够的数字资产。 + + + + The Tokenvalue is too big,you have not enough Tokencoins. + 没有足够的数字资产。 + + + + password error + 密码错误 + + + + num error + 数目错误 + + + + Token-reg-starttime-is-up-yet + 发送失败:当前时间早于代币发行时间 + + + + Please enter the correct amount + 请输入正确的金额 + + + + CMessageBox + + + Confirm exit? + 确认退出吗? + + + + Is Bookkeeping,Confirm exit? + 正在记账,退出后财产可能损失,确认退出吗? + + + + OK + 确认 + + + + cancle + 取消 + + + + Date Updating,please waiting... + 数据正在更新,请耐心等待... + + + + General punishment.Long term abnormal bookkeeping, please standardize bookkeeping behavior. + 普通处罚 长期非正常记账,请规范记账行为。 + + + + Password input error more than 5 times, please prohibit input within a day! + 密码输入错误超过5次,24小时内禁止输入! + + + + + QLineEdit + + + &Undo + 撤消(&U) + + + + &Redo + 恢复(&R) + + + + Cu&t + 剪切(&T) + + + + &Copy + 复制(&C) + + + + &Paste + 粘贴(&P) + + + + Delete + 删除 + + + + Select All + 选择全部 + + + + QFrame + + &Undo + 撤消(&U) + + + &Redo + 恢复(&R) + + + Cu&t + 剪切(&T) + + + &Copy + 复制(&C) + + + Copy &Link Location + 复制链接位置(&L) + + + &Paste + 粘贴(&P) + + + Delete + 删除 + + + Select All + 选择全部 + + + + QWidgetTextControl + + &Undo + 撤消(&U) + + + &Redo + 恢复(&R) + + + Cu&t + 剪切(&T) + + + &Copy + 复制(&C) + + + Copy &Link Location + 复制链接位置(&L) + + + &Paste + 粘贴(&P) + + + Delete + 删除 + + + Select All + 选择全部 + + + + QTextControl + + + &Undo + 撤消(&U) + + + + &Redo + 恢复(&R) + + + + Cu&t + 剪切(&T) + + + + &Copy + 复制(&C) + + + + Copy &Link Location + 复制链接位置 + + + + &Paste + 粘贴(&P) + + + + Delete + 删除 + + + + Select All + 选择全部 + + + + UpdateDialog + + laterUpdate + 稍后升级 + + + goUpdate + 马上升级 + + + address: + 网址: + + + diff --git a/.svn/pristine/48/48131b9e534d7c4b7605817dd9d2f71628b2cb52.svn-base b/.svn/pristine/48/48131b9e534d7c4b7605817dd9d2f71628b2cb52.svn-base new file mode 100644 index 0000000..ca81dd6 --- /dev/null +++ b/.svn/pristine/48/48131b9e534d7c4b7605817dd9d2f71628b2cb52.svn-base @@ -0,0 +1,186 @@ +// Copyright (c) 2011-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sendcoinsdialog.h" +#include "ui_sendcoinsdialog.h" +#include "addresstablemodel.h" +#include "ipchainunits.h" +#include "clientmodel.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "platformstyle.h" +#include "walletmodel.h" +#include "base58.h" +#include "chainparams.h" +#include "wallet/coincontrol.h" +#include "validation.h" +#include "ui_interface.h" +#include "txmempool.h" +#include "wallet/wallet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "config/bitcoin-config.h" /* for USE_QRCODE */ +#include +#include "log/stateinfo.h" + +#define SEND_CONFIRM_DELAY 3 + +SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) : + QDialog(parent), + ui(new Ui::SendCoinsDialog), + clientModel(0), + model(0), + fNewRecipientAllowed(true), + fFeeMinimized(true), + platformStyle(_platformStyle) +{ + ui->setupUi(this); + eTag =0; + ui->comboBox->addItem(tr("IPC"), IPC); + ui->comboBox->addItem(tr("mIPC"),mIPC); + ui->ReceiverEdit->setPlaceholderText(tr("input receiver")); + ui->labelEdit->setPlaceholderText(tr("input label")); + ui->CoinEdit->setPlaceholderText(tr("input num")); + QRegExp regx("^\\d+(\\.\\d+)?$"); + QValidator *validator = new QRegExpValidator(regx, ui->CoinEdit ); + ui->CoinEdit->setValidator( validator ); + connect(ui->comboBox, SIGNAL(activated(int)), this, SLOT(coinUpdate(int))); + +} +void SendCoinsDialog::coinUpdate(int idx) +{ + QDate current = QDate::currentDate(); + switch(ui->comboBox->itemData(idx).toInt()) + { + case IPC: + { + eTag = 0; + } + break; + case mIPC: + { + eTag = 1; + } + break; + case uIPC: + { + eTag = 2; + } + break; + case zhi: + { + eTag = 3; + } + break; + default: + { + eTag = 0; + } + } +} +void SendCoinsDialog::setClientModel(ClientModel *_clientModel) +{ + this->clientModel = _clientModel; +} + +void SendCoinsDialog::setModel(WalletModel *_model) +{ + this->model = _model; +} + +SendCoinsDialog::~SendCoinsDialog() +{ + QSettings settings; + delete ui; +} +void SendCoinsDialog::setAddress(const QString &address,const QString &label) +{ + ui->ReceiverEdit->setText(address); + ui->labelEdit->setText(label); +} +void SendCoinsDialog::on_scanBtn_pressed() +{ + QString filename = GUIUtil::getOpenFileName(this, tr("Select payment request file to open"), "", "", NULL); + if(filename.isEmpty()) + return; + QRcode_encodeString("http://www.ipcchain.com/", 2, QR_ECLEVEL_Q, QR_MODE_8, 0); + QRcode *code = QRcode_encodeString("uri.toUtf8().constData()", 0, QR_ECLEVEL_L, QR_MODE_8, 1); + if (!code) + { + return; + } + QImage qrImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); + qrImage.fill(0xffffff); + unsigned char *p = code->data; + for (int y = 0; y < code->width; y++) + { + for (int x = 0; x < code->width; x++) + { + qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); + p++; + } + } + QRcode_free(code); + + QImage qrAddrImage = QImage(300, 300+20, QImage::Format_RGB32); + qrAddrImage.fill(0xffffff); + + QPainter painter(&qrAddrImage); + painter.drawImage(0, 0, qrImage.scaled(300, 300)); + QFont font = GUIUtil::fixedPitchFont(); + font.setPixelSize(12); + painter.setFont(font); + QRect paddedRect = qrAddrImage.rect(); + paddedRect.setHeight(300+12); + painter.end(); + +} +void SendCoinsDialog::on_AddAdsButton_pressed() +{ + Q_EMIT openAddBookPagewidget(model->getAddressTableModel(),2); +} +void SendCoinsDialog::on_GoSettingBtn_pressed() +{ + + QString a = ui->ReceiverEdit->text(); + QString label = ui->labelEdit->text(); + QString b = ui->CoinEdit->text(); + if(a != NULL && b != NULL) + { + if(!model->validateAddress(a)) + { + ui->tiplabel->setText(tr("The recipient address is not valid. Please recheck.")); + return; + }else{ + ui->tiplabel->setText(tr("")); + } + } + else + { + ui->tiplabel->setText(tr("input info")); + return; + } + bool isCrypted = this->model->CheckIsCrypted(); + if(isCrypted) + { + + Q_EMIT openSendAffrimwidget(a,b,label,eTag); + } + else + { + Q_EMIT openSettingwidget(a,b,label,eTag); + } +} +void SendCoinsDialog::clearInfo() +{ + ui->ReceiverEdit->setText(""); + ui->labelEdit->setText(""); + ui->CoinEdit->setText(""); +} diff --git a/.svn/pristine/4a/4a83c0836944e87811c798f1c479acc1d6a540c7.svn-base b/.svn/pristine/4a/4a83c0836944e87811c798f1c479acc1d6a540c7.svn-base new file mode 100644 index 0000000..b05075a --- /dev/null +++ b/.svn/pristine/4a/4a83c0836944e87811c798f1c479acc1d6a540c7.svn-base @@ -0,0 +1,881 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// NOTE: This file is intended to be customised by the end user, and includes only local node policy logic + +#include "primitives/transaction.h" +#include "policy/policy.h" +#include "chainparams.h" + +#include "validation.h" +#include "tinyformat.h" +#include "util.h" +#include "utilstrencodings.h" +#include "base58.h" + +#include +#include +#include +#include // std::pair +#include "dpoc//ConsensusAccountPool.h" + + /** + * Check transaction inputs to mitigate two + * potential denial-of-service attacks: + * + * 1. scriptSigs with extra data stuffed into them, + * not consumed by scriptPubKey (or P2SH script) + * 2. P2SH scripts with a crazy number of expensive + * CHECKSIG/CHECKMULTISIG operations + * + * Why bother? To avoid denial-of-service attacks; an attacker + * can submit a standard HASH... OP_EQUAL transaction, + * which will get accepted into blocks. The redemption + * script can be anything; an attacker could use a very + * expensive-to-check-upon-redemption script like: + * DUP CHECKSIG DROP ... repeated 100 times... OP_1 + */ + +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled) +{ + std::vector > vSolutions; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_MULTISIG) + { + unsigned char m = vSolutions.front()[0]; + unsigned char n = vSolutions.back()[0]; + // Support up to x-of-3 multisig txns as standard + if (n < 1 || n > 3) + return false; + if (m < 1 || m > n) + return false; + } else if (whichType == TX_NULL_DATA && + (!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes)) + return false; + + else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH)) + return false; + + return whichType != TX_NONSTANDARD; +} + +bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled) +{ + if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) { + reason = "version"; + return false; + } + + // Extremely large transactions with lots of inputs can cost the network + // almost as much to process as they cost the sender in fees, because + // computing signature hashes is O(ninputs*txsize). Limiting transactions + // to MAX_STANDARD_TX_WEIGHT mitigates CPU exhaustion attacks. + unsigned int sz = GetTransactionWeight(tx); + if (sz >= MAX_STANDARD_TX_WEIGHT) { + reason = "tx-size"; + return false; + } + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed + // keys (remember the 520 byte limit on redeemScript size). That works + // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 + // bytes of scriptSig, which we round off to 1650 bytes for some minor + // future-proofing. That's also enough to spend a 20-of-20 + // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not + // considered standard. + if (txin.scriptSig.size() > 1650) { + reason = "scriptsig-size"; + return false; + } + if (!txin.scriptSig.IsPushOnly()) { + reason = "scriptsig-not-pushonly"; + return false; + } + } + + unsigned int nDataOut = 0; + txnouttype whichType; + BOOST_FOREACH(const CTxOut& txout, tx.vout) { + if (!::IsStandard(txout.scriptPubKey, whichType, witnessEnabled)) { + reason = "scriptpubkey"; + return false; + } + + if (whichType == TX_NULL_DATA) + nDataOut++; + else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { + reason = "bare-multisig"; + return false; + } else if (txout.IsDust(dustRelayFee)) { + reason = "dust"; + return false; + } + } + + // only one OP_RETURN txout is permitted + if (nDataOut > 1) { + reason = "multi-op-return"; + return false; + } + + return true; +} + +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) +{ + if (tx.IsCoinBase()) + return true; // Coinbases don't use vin normally + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); + + std::vector > vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + const CScript& prevScript = prev.scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return false; + + if (whichType == TX_SCRIPTHASH) + { + std::vector > stack; + // convert the scriptSig into a stack, so we can inspect the redeemScript + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE)) + return false; + if (stack.empty()) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) { + return false; + } + } + } + + return true; +} + +//Check the subfunctions of the model type constraints of the output trading model +bool IsValidIPCModelCheck(const CTransaction& tx, std::map& InOwnerRecord, std::map >& InAuthorRecord, + std::map& OutOwnerRecord, std::map& OutUniqueRecord, std::multimap >& OutAuthorRecord, CValidationState& state) +{ + std::map::iterator ipcTxIteator; + std::map >::iterator ipcInAuthorIteator; + std::multimap >::iterator ipcOutNormalAuthorIteator; + + //For ownership input + ipcTxIteator = InOwnerRecord.begin(); + while (ipcTxIteator != InOwnerRecord.end()) + { + //Joint inspection. For each ownership input, the output must have a same hash, with the same extension type of ownership output. The output date cannot exceed the date of the input + if (OutOwnerRecord.count(ipcTxIteator->first) > 0) + { + if (ipcTxIteator->second.ExtendType != OutOwnerRecord[ipcTxIteator->first].ExtendType) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-extendtype-changed"); + + if(ipcTxIteator->second.startTime > OutOwnerRecord[ipcTxIteator->first].startTime || + ((ipcTxIteator->second.stopTime < OutOwnerRecord[ipcTxIteator->first].stopTime) && ipcTxIteator->second.stopTime !=0) || + (ipcTxIteator->second.stopTime != 0 && OutOwnerRecord[ipcTxIteator->first].stopTime == 0)) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-timecheck-error"); + + ipcTxIteator++; + continue; + } + else + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-ownership-lost"); + } + + //For authorization input + ipcInAuthorIteator = InAuthorRecord.begin(); + while (ipcInAuthorIteator != InAuthorRecord.end()) + { + //If the reauthorization mark entered is 0, the error is reported + if (ipcInAuthorIteator->second.second.reAuthorize == 0) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-reAuthor-with-no-permission"); + //Authorize operation constraints + if (InOwnerRecord.count(ipcInAuthorIteator->first) <= 0) + { + //For reauthorization operations, no ownership output of the same hash can be found + if (OutOwnerRecord.count(ipcInAuthorIteator->first) > 0) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-reAuthor-with-ownership-output"); + + bool losted = true; + bool changed = true; + if (ipcInAuthorIteator->second.second.uniqueAuthorize == 0) //the authorization for non-exclusive authorization is compared to OutAuthorRecord. + { + ipcOutNormalAuthorIteator = OutAuthorRecord.lower_bound(ipcInAuthorIteator->first); + if (ipcOutNormalAuthorIteator == OutAuthorRecord.upper_bound(ipcInAuthorIteator->first)) //The equivalent specification does not have this hash authorized output + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-Authorize-output(hash have changed)"); + while (ipcOutNormalAuthorIteator != OutAuthorRecord.upper_bound(ipcInAuthorIteator->first)) + { + if (ipcOutNormalAuthorIteator->second.first == ipcInAuthorIteator->second.first) + { + losted = false; + break; + } + + ipcOutNormalAuthorIteator++; + } + } + else if (ipcInAuthorIteator->second.second.uniqueAuthorize == 1) //Input authorization is exclusive authorization + { + +// if (OutUniqueRecord.count(ipcInAuthorIteator->first) == 0) +// return state.DoS(100, false, REJECT_INVALID, "bad-IPC-Authorize(unique)-hash-changed"); +// +// losted = false; + } + + if (losted) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-Authorization-lost"); + + } + + ipcInAuthorIteator++; + } + + //For the output of ownership + ipcTxIteator = OutOwnerRecord.begin(); + while (ipcTxIteator != OutOwnerRecord.end()) + { + if (OutUniqueRecord.count(ipcTxIteator->first) == 0 && + OutAuthorRecord.count(ipcTxIteator->first) == 0 && + InOwnerRecord.count(ipcTxIteator->first) > 0 ) + { + + } + else if (OutUniqueRecord.count(ipcTxIteator->first) == 0 && + OutAuthorRecord.count(ipcTxIteator->first) == 0 && + InOwnerRecord.count(ipcTxIteator->first) == 0) + { + //For ownership registration transactions, there is no duplication of registration, and txid has different hash values for this transaction + if (pIPCCheckMaps->IPCHashMap.count(ipcTxIteator->first) && + pIPCCheckMaps->IPCHashMap[ipcTxIteator->first].first != tx.GetHash()) + { + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-IPChash-repeat"); + } + //For ownership of the ownership, the exclusive authorization mark of its output must be 0, ensuring that the ownership of the registration can be exclusive authorized + if (ipcTxIteator->second.uniqueAuthorize != 0) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-ownership-output-uniqueAuthorize-error"); + + } + //For each property type output, the reauthorizing mark for its output must be 1 + if (ipcTxIteator->second.reAuthorize != 1) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-ownership-output-no-reAuthor"); + + ipcTxIteator++; + continue; + } + + //Ordinary authorized output + ipcOutNormalAuthorIteator = OutAuthorRecord.begin(); + while (ipcOutNormalAuthorIteator != OutAuthorRecord.end()) + { + //For each authorized output, the start-stop date cannot exceed the entered date + if (InOwnerRecord.count(ipcOutNormalAuthorIteator->first) > 0) //Input is the type of ownership + { + if (ipcOutNormalAuthorIteator->second.second.startTime < InOwnerRecord[ipcOutNormalAuthorIteator->first].startTime || + ((ipcOutNormalAuthorIteator->second.second.stopTime > InOwnerRecord[ipcOutNormalAuthorIteator->first].stopTime) && (InOwnerRecord[ipcOutNormalAuthorIteator->first].stopTime != 0))) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-normalAuthor-timecheck-error"); + } + else if (InAuthorRecord.count(ipcOutNormalAuthorIteator->first) > 0) //Authorized to enter + { + //Time to check + + if (ipcOutNormalAuthorIteator->second.second.startTime < InAuthorRecord[ipcOutNormalAuthorIteator->first].second.startTime || + ipcOutNormalAuthorIteator->second.second.stopTime > InAuthorRecord[ipcOutNormalAuthorIteator->first].second.stopTime) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-reAuthor-timecheck-error"); + + } + else + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-Author-no-mother"); //There is no ownership or authorization in the input + + ipcOutNormalAuthorIteator++; + continue; + } + + return true; +} + +//Scrip transaction model type constraint checking subfunctions +bool IsValidTokenModelCheck(std::map& tokenInRegRecord, std::map& tokenInRecord, + std::map& tokenOutRegRecord, std::map& tokenOutRecord, CValidationState& state) +{ + std::map::iterator tokenTxIteator; + + tokenTxIteator = tokenInRegRecord.begin(); + while (tokenTxIteator != tokenInRegRecord.end()) + { + //Joint check: for the same Symbol, there should be no registration type and type of transaction in the input + if (tokenInRecord.count(tokenTxIteator->first) > 0) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Multi-inType"); + + //For a register type, the output must have a transaction type output corresponding to Symbol + if (tokenOutRecord.count(tokenTxIteator->first) <= 0) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-reg-to-none"); + + tokenTxIteator++; + continue; + } + + tokenTxIteator = tokenOutRegRecord.begin(); + while (tokenTxIteator != tokenOutRegRecord.end()) + { + //Joint check: for the same Symbol, there should be no registration type and type of transaction in the output + if (tokenOutRecord.count(tokenTxIteator->first) > 0) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Multi-outType"); + + tokenTxIteator++; + continue; + } + + //The type of token token type is checked for the output + tokenTxIteator = tokenOutRecord.begin(); + while (tokenTxIteator != tokenOutRecord.end()) + { + //If there is a registration record for Symbol in the input, this is a distribution transaction + if (tokenInRegRecord.count(tokenTxIteator->first) > 0) + { + //Verify that the total amount of the corresponding transaction in the input is equal to the total amount corresponding to the transaction in the output. Otherwise, the report is wrong + if (tokenTxIteator->second != tokenInRegRecord[tokenTxIteator->first]) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-regtotoken-value-unequal"); + tokenTxIteator++; + continue; + } + + //Otherwise, if there is a transaction record of Symbol in the input, it proves that this is a token currency transaction + if (tokenInRecord.count(tokenTxIteator->first) > 0) + { + //Verify that the total amount of the corresponding transaction in the input is equal to the total amount corresponding to the transaction in the output. Otherwise, the report is wrong + if (tokenTxIteator->second != tokenInRecord[tokenTxIteator->first]) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-value-unequal"); + tokenTxIteator++; + continue; + } + + return state.DoS(100, false, REJECT_INVALID, "bad-Token-output-error"); + } + return true; +} + +bool AreIPCStandard(const CTransaction& tx, CValidationState &state) +{ + + if (tx.IsCoinBase()) + return true; // Coinbases don't use vin normally + + if (!CheckIPCFinalTx(tx)) + return state.DoS(100, false, REJECT_INVALID, "is-no-IPC-final-tx"); + + txnouttype type; + std::vector txoutdestes; + int nRequired; + + BOOST_FOREACH(const CTxOut& txout, tx.vout) { + //Parse the output address of txout + if (!ExtractDestinations(txout.scriptPubKey, type, txoutdestes, nRequired)) + return state.DoS(100, false, REJECT_INVALID, "txout-address-unextracted,type=" + type); + + if (txout.txType != TXOUT_CAMPAIGN){ + BOOST_FOREACH(CTxDestination &dest, txoutdestes){ + if (Params().system_account_address == CBitcoinAddress(dest).ToString()) + return state.DoS(100, false, REJECT_INVALID, "send-to-system-address-forbidden"); + } + } + } + + uint64_t tokenTxInputTotalValue = 0; + std::map tokenInRegRecord; //With Symbol as the keyword, record the token registration type in the input with the amount value + std::map tokenInRecord; //Symbol is the key word to record the token value transaction type in the input. The input of multiple tokens with Symbol should be added together. + + std::map ipcInOwnerRecord; //The ipc hash tag is the keyword, recording the type of ownership in the input, and the label. + std::map > ipcInAuthorRecord; //With ipc hash tag as the keyword, record the authorization type in the input with its tag. + + CTransactionRef prevTx; + uint256 hashBlock; + + + int devoteinCount = 0; + int IPCinCount = 0; + int tokeninCount = 0; + CAmount totalvintvalues = 0; + uint8_t fatheruraccy = 10; //The legal value can't be 10 + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + if (!GetTransaction(tx.vin[i].prevout.hash, prevTx, Params().GetConsensus(), hashBlock, true)) + { + //If the parent trades in the same block + if (!GetCachedChainTransaction(tx.vin[i].prevout.hash, prevTx)) + { + return state.DoS(100, false, REJECT_INVALID, "bad-no-input"); + } + if (prevTx->vout[tx.vin[i].prevout.n].txType == TXOUT_TOKENREG)//Tokens to register + fatheruraccy = prevTx->vout[tx.vin[i].prevout.n].tokenRegLabel.accuracy; + } + const CTxOut& prev = prevTx->vout[tx.vin[i].prevout.n]; + + totalvintvalues += prev.nValue; + //If the front-end output (whatever the type) is the system regulatory account, the report is wrong. You're not allowed to spend it from a regulatory account. + if (!ExtractDestinations(prev.scriptPubKey, type, txoutdestes, nRequired)) + return state.DoS(100, false, REJECT_INVALID, "txin-address-unextracted,type=" + type); + + BOOST_FOREACH(CTxDestination &dest, txoutdestes){ + if (Params().system_account_address == CBitcoinAddress(dest).ToString()) + return state.DoS(100, false, REJECT_INVALID, "cost-from-systemaccount-forbidden"); + } + if (prev.txLabelLen > TXLABLE_MAX_LENGTH - 1) //More than 511, txLabelLen shows the incorrect length + return state.DoS(100, false, REJECT_INVALID, "Vin-txLabelLen-erro"); + if (prev.txLabel.size() != prev.txLabelLen) //txLabelLen,txLabel.size(). Don't agree + return state.DoS(100, false, REJECT_INVALID, "Vin-txLabel-length-not-feet-txLabelLen"); + + + switch (prev.txType) + { + case 1: + if (prev.devoteLabel.ExtendType != TYPE_CONSENSUS_REGISTER) + { + return state.DoS(100, false, REJECT_INVALID, "bad-campaign-input"); + } + if (!CConsensusAccountPool::Instance().IsAviableUTXO(prevTx->GetHash())) //After the application to join utxo (deposit), then determine whether this txid is defrosted. + { + LogPrintf("txhash :%s , vin[%d] ---bad-campaign-input,UTXO-is-unusable.\n",tx.GetHash().ToString(),i); + return false; + } + devoteinCount++; + break; + + case 2: + if (prev.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vin2-IPC-nValue-must-be-zero"); + + if (prev.labelLen > 255) + return state.DoS(100, false, REJECT_INVALID, "Vin2-IPCLabel-length-out-of-bounds"); + if (prev.ipcLabel.size() != prev.labelLen) + return state.DoS(100, false, REJECT_INVALID, "Vin2-IPCLabel-length-not-feet-labelLen"); + + if (prev.ipcLabel.startTime != 0 && prev.ipcLabel.startTime > chainActive.Tip()->GetBlockTime()) + return state.DoS(100, false, REJECT_INVALID, "IPC-owner-starttime-is-up-yet"); + + if (ipcInOwnerRecord.count(prev.ipcLabel.hash) > 0) + return state.DoS(100, false, REJECT_INVALID, "multi-IPC-ownership-in-with-same-hash"); + + if (ipcInAuthorRecord.count(prev.ipcLabel.hash) > 0) + return state.DoS(100, false, REJECT_INVALID, "multi-IPC-authorization-and-ownership-in-with-same-hash"); + + ipcInOwnerRecord[prev.ipcLabel.hash] = prev.ipcLabel; + IPCinCount++; + break; + + case 3: + + if (prev.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vin3-IPC-nValue-must-be-zero"); + + if (prev.labelLen > 255) + return state.DoS(100, false, REJECT_INVALID, "Vin3-IPCLabel-length-out-of-bounds"); + if (prev.ipcLabel.size() != prev.labelLen) + return state.DoS(100, false, REJECT_INVALID, "Vin3-IPCLabel-length-not-feet-labelLen"); + + if (prev.ipcLabel.startTime != 0 && prev.ipcLabel.startTime > chainActive.Tip()->GetBlockTime()) + return state.DoS(100, false, REJECT_INVALID, "IPC-Author-starttime-is-up-yet"); + + if (ipcInOwnerRecord.count(prev.ipcLabel.hash) > 0) + return state.DoS(100, false, REJECT_INVALID, "multi-IPC-ownership-in-with-same-hash"); + + if (ipcInAuthorRecord.count(prev.ipcLabel.hash) > 0) + return state.DoS(100, false, REJECT_INVALID, "multi-IPC-authorization-and-ownership-in-with-same-hash"); + + ipcInAuthorRecord[prev.ipcLabel.hash] = std::make_pair(prev.scriptPubKey, prev.ipcLabel); + IPCinCount++; + break; + + case 4: + + if (prev.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vin4-IPC-nValue-must-be-zero"); + + if (tokenInRegRecord.count(prev.tokenRegLabel.getTokenSymbol()) > 0) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Symbol-repeat"); + + if (prev.tokenRegLabel.issueDate != 0 && prev.tokenRegLabel.issueDate > chainActive.Tip()->GetBlockTime()) + return state.DoS(100, false, REJECT_INVALID, "Token-reg-starttime-is-up-yet"); + + if (prev.tokenRegLabel.accuracy != tokenDataMap[prev.tokenRegLabel.getTokenSymbol()].accuracy && fatheruraccy != prev.tokenRegLabel.accuracy) + { + return state.DoS(100, false, REJECT_INVALID, "Vin-Token-accuracy-error"); + } + + tokenInRegRecord[prev.tokenRegLabel.getTokenSymbol()] = prev.tokenRegLabel.totalCount; + tokeninCount++; + break; + + case 5: + + if (prev.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vin5-IPC-nValue-must-be-zero"); + + if (prev.tokenLabel.accuracy != tokenDataMap[prev.tokenLabel.getTokenSymbol()].accuracy) + return state.DoS(100, false, REJECT_INVALID, "Vin-Token-accuracy-error"); + + if (tokenInRecord.count(prev.tokenLabel.getTokenSymbol()) > 0) + { + tokenTxInputTotalValue = tokenInRecord[prev.tokenLabel.getTokenSymbol()]; + tokenTxInputTotalValue += prev.tokenLabel.value; + tokenInRecord[prev.tokenLabel.getTokenSymbol()] = tokenTxInputTotalValue; + } + else + { + tokenInRecord[prev.tokenLabel.getTokenSymbol()] = prev.tokenLabel.value; + } + + + tokeninCount++; + break; + + default: + continue; + break; + } + } + + //Joint constraint. The same transaction cannot have any other two model inputs besides the ordinary transaction + if ( (IPCinCount > 0 && tokeninCount > 0) ) + return state.DoS(100, false, REJECT_INVALID, "multi-txType-input-forbidden"); + + std::string checkStr; + + uint64_t tokenTxOutputTotalValue = 0; + std::map tokenOutRegRecord; //Symbol is the key word to record the token token transaction type in the output. + std::map tokenOutRecord; //With Symbol as the key word, the token value transaction type of the output is recorded, and the output of multiple tokens of the same Symbol is added together. + + std::map ipcOutOwnerRecord; //The ipc hash tag is the key word to record the type of ownership in the output, and its label. + std::map ipcOutUniqueRecord; //A MAP that records exclusive authorization. + std::multimap> ipcOutAuthorRecord; //Multiple authorization outputs are allowed, and many different situations and values are required for each authorization output, so you need to bring an output public key address to distinguish multiple authorized outputs. + + CBitcoinAddress address(Params().system_account_address); + CScript scriptSystem = GetScriptForDestination(address.Get()); + CScript tmpscript; + uint160 devoterhash; + + CTxOut prev; + + int IPCoutCount = 0; + int devoteoutCount = 0; + int tokenoutCount = 0; + + std::vector prevdestes; + std::string curaddress; + CAmount totalvoutvalues = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) { + + if (txout.txLabelLen > TXLABLE_MAX_LENGTH - 1) //More than 511, txLabelLen shows the incorrect length. + return state.DoS(100, false, REJECT_INVALID, "Vout-txLabelLen-erro"); + if (txout.txLabel.size() != txout.txLabelLen) + return state.DoS(100, false, REJECT_INVALID, "Vout-txLabel-length-not-feet-txLabelLen"); + totalvoutvalues += txout.nValue; + bool systenaddressfounded = false; + switch (txout.txType) + { + case 1: + + if (txout.devoteLabel.ExtendType == TYPE_CONSENSUS_QUITE) + { + + bool founded = false; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + founded = false; + if (!GetTransaction(tx.vin[i].prevout.hash, prevTx, Params().GetConsensus(), hashBlock, true)) + { + if (!GetCachedChainTransaction(tx.vin[i].prevout.hash, prevTx)) + { + return state.DoS(100, false, REJECT_INVALID, "bad-no-input"); + } + } + prev = prevTx->vout[tx.vin[i].prevout.n]; + devoterhash = txout.devoteLabel.hash; + + //Gets the address from the current hash value + curaddress = CBitcoinAddress(CKeyID(devoterhash)).ToString(); + //Restore CTXDestination from the prevout script + if (!ExtractDestinations(prev.scriptPubKey, type, prevdestes, nRequired)) + return state.DoS(100, false, REJECT_INVALID, "exit-campaign-prevout-Unextracted,type=" + type); + + BOOST_FOREACH(CTxDestination &prevdest, prevdestes){ + if (curaddress == CBitcoinAddress(prevdest).ToString()) + { + founded = true; + break; + } + } + if (!founded) + { + return state.DoS(100, false, REJECT_INVALID, "bad-exit-campaign-devotepubkey-address"); + } + } + + + } + else if (txout.devoteLabel.ExtendType != TYPE_CONSENSUS_REGISTER && txout.devoteLabel.ExtendType != TYPE_CONSENSUS_SEVERE_PUNISHMENT_REQUEST) + return state.DoS(100, false, REJECT_INVALID, "construct-other-campaign-tx-forbidden"); + + devoteoutCount++; + break; + + case 2: + if (txout.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vout2-IPC-nValue-must-be-zero"); + if (txout.labelLen > 255) + return state.DoS(100, false, REJECT_INVALID, "Vout2-IPCLabel-length-out-of-bounds"); + + if (txout.ipcLabel.size() != txout.labelLen) + return state.DoS(100, false, REJECT_INVALID, "Vout2-IPCLabel-length-not-feet-labelLen"); + + if (txout.ipcLabel.hash.GetHex().length() != 32) + return state.DoS(100, false, REJECT_INVALID, "Vout2-IPCHash-length-must-be-32"); + + if (ipcOutOwnerRecord.count(txout.ipcLabel.hash) > 0) + return state.DoS(100, false, REJECT_INVALID, "multi-IPC-ownership-output"); + + if (txout.ipcLabel.hash.IsNull()) + return state.DoS(100, false, REJECT_INVALID, "IPC-ownership-hash-can't-be-NULL"); + if (txout.ipcLabel.stopTime!=0 && txout.ipcLabel.startTime>=txout.ipcLabel.stopTime) + return state.DoS(100, false, REJECT_INVALID, "IPC-ownership-starttime-can't-larger-than-stoptime"); + + if ((txout.ipcLabel.reAuthorize != 0 && txout.ipcLabel.reAuthorize !=1 )|| + (txout.ipcLabel.uniqueAuthorize != 0 && txout.ipcLabel.uniqueAuthorize != 1)) + return state.DoS(100, false, REJECT_INVALID, "IPCLabel-reAuthorize-or-uniqueAuthorize-out-of-bounds"); + + if (ipcInOwnerRecord.count(txout.ipcLabel.hash) > 0) + { + if (ipcInOwnerRecord[txout.ipcLabel.hash].hash != txout.ipcLabel.hash || + ipcInOwnerRecord[txout.ipcLabel.hash].ExtendType != txout.ipcLabel.ExtendType || + ipcInOwnerRecord[txout.ipcLabel.hash].labelTitle != txout.ipcLabel.labelTitle) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-send-or-rethorize-output(hash/ExtendType/labelTitle)"); + + + if (ipcInOwnerRecord[txout.ipcLabel.hash].reAuthorize != txout.ipcLabel.reAuthorize ) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-send-or-rethorize-output(reAuthorize)"); + + } + ipcOutOwnerRecord[txout.ipcLabel.hash] = txout.ipcLabel; + IPCoutCount++; + break; + + case 3: + + if (txout.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vout3-IPC-nValue-must-be-zero"); + if (txout.labelLen > 255) + return state.DoS(100, false, REJECT_INVALID, "Vout3-IPCLabel-length-out-of-bounds"); + + if (txout.ipcLabel.size() != txout.labelLen) + return state.DoS(100, false, REJECT_INVALID, "Vout3-IPCLabel-length-not-feet-labelLen"); + + if (txout.ipcLabel.hash.GetHex().length() != 32) + return state.DoS(100, false, REJECT_INVALID, "Vout3-IPCHash-length-must-be-32"); + + if (txout.ipcLabel.startTime == 0 || txout.ipcLabel.stopTime == 0) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-author-time"); + + + if ((txout.ipcLabel.reAuthorize != 0 && txout.ipcLabel.reAuthorize != 1) || + (txout.ipcLabel.uniqueAuthorize != 0 && txout.ipcLabel.uniqueAuthorize != 1)) + return state.DoS(100, false, REJECT_INVALID, "IPCLabel-reAuthorize-or-uniqueAuthorize-out-of-bounds"); + + if (ipcInOwnerRecord.count(txout.ipcLabel.hash) > 0) + { + if (ipcInOwnerRecord[txout.ipcLabel.hash].hash != txout.ipcLabel.hash || + ipcInOwnerRecord[txout.ipcLabel.hash].ExtendType != txout.ipcLabel.ExtendType || + ipcInOwnerRecord[txout.ipcLabel.hash].labelTitle != txout.ipcLabel.labelTitle) + return state.DoS(100, false, REJECT_INVALID, "bad-IPC-Authorize-output(hash/ExtendType/labelTitle)"); + } + + + if (txout.ipcLabel.uniqueAuthorize == 1) + { + //moddify by xxy 20171216 Exclusive license notes +// //There can only be one exclusive authorized output in the same transaction +// if (ipcOutUniqueRecord.count(txout.ipcLabel.hash) > 0) +// return state.DoS(100, false, REJECT_INVALID, "bad-IPC-multi-uniqueAuthor-output"); +// +// ipcOutUniqueRecord[txout.ipcLabel.hash] = txout.ipcLabel; + //end moddify + } + else + ipcOutAuthorRecord.insert(std::make_pair(txout.ipcLabel.hash, std::make_pair(txout.scriptPubKey, txout.ipcLabel))); + + IPCoutCount++; + break; + + case 4: + + if (txout.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vout4-Token-nValue-must-be-zero"); + + checkStr = txout.tokenRegLabel.getTokenSymbol(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != std::string::npos || checkStr.find("RMB") != std::string::npos + || checkStr.find("USD") != std::string::npos || checkStr.find("EUR") != std::string::npos ) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Symbol-contain-errvalue"); + checkStr = txout.tokenRegLabel.getTokenLabel(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != std::string::npos || checkStr.find("RMB") != std::string::npos + || checkStr.find("USD") != std::string::npos || checkStr.find("EUR") != std::string::npos) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Label-contain-errvalue"); + + if (txout.tokenRegLabel.hash.GetHex().length() != 32) + return state.DoS(100, false, REJECT_INVALID, "Vout4-Hash-length-must-be-32"); + + if (txout.tokenRegLabel.issueDate < TOKEN_REGTIME_BOUNDARY) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Reg-issueDate(Regtime)"); + + if (txout.tokenRegLabel.accuracy < 0 || txout.tokenRegLabel.accuracy > 8) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Reg-accuracy(must be 0-8)"); + + if (txout.tokenRegLabel.totalCount > TOKEN_MAX_VALUE) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Reg-totalCount"); + + if (pIPCCheckMaps->TokenSymbolMap.count(txout.tokenRegLabel.getTokenSymbol()) > 0 && + pIPCCheckMaps->TokenSymbolMap[txout.tokenRegLabel.getTokenSymbol()].first != tx.GetHash()) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-tokensymbol-repeat"); + + if (pIPCCheckMaps->TokenHashMap.count(txout.tokenRegLabel.hash) > 0 && + pIPCCheckMaps->TokenHashMap[txout.tokenRegLabel.hash].first != tx.GetHash()) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-tokenhash-repeat"); + + + if (tokenOutRegRecord.count(txout.tokenRegLabel.getTokenSymbol()) > 0) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-tokensymbol-repeat"); + + tokenOutRegRecord[txout.tokenRegLabel.getTokenSymbol()] = txout.tokenRegLabel.totalCount; + + tokenoutCount++; + break; + + case 5: + + if (txout.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "Vout5-Token-nValue-must-be-zero"); + + if (txout.tokenLabel.accuracy < 0 || txout.tokenLabel.accuracy > 8) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-accuracy(must be 0-8)"); + + if (txout.tokenLabel.accuracy != tokenDataMap[txout.tokenLabel.getTokenSymbol()].accuracy && fatheruraccy != txout.tokenLabel.accuracy) + return state.DoS(100, false, REJECT_INVALID, "Vout-Token-accuracy-error"); + + checkStr = txout.tokenLabel.getTokenSymbol(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != std::string::npos) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Symbol-contain-errvalue"); + + + if (tokenOutRecord.count(txout.tokenLabel.getTokenSymbol()) > 0) + { + tokenTxOutputTotalValue = tokenOutRecord[txout.tokenLabel.getTokenSymbol()]; + tokenTxOutputTotalValue += txout.tokenLabel.value; + tokenOutRecord[txout.tokenLabel.getTokenSymbol()] = tokenTxOutputTotalValue; + } + else + tokenOutRecord[txout.tokenLabel.getTokenSymbol()] = txout.tokenLabel.value; + + tokenoutCount++; + break; + + case 0: + continue; + break; + default: + return state.DoS(100, false, REJECT_INVALID, "can't-support-output-Type"); + } + } + + //Joint constraint. The same transaction cannot have any other two model outputs other than ordinary transactions. + if ( (IPCoutCount > 0 && tokenoutCount > 0) || + (IPCoutCount > 0 && devoteoutCount > 0) || + (devoteoutCount > 0 && tokenoutCount > 0)) + return state.DoS(100, false, REJECT_INVALID, "multi-txType-output-forbidden"); + + if (!IsValidTokenModelCheck(tokenInRegRecord, tokenInRecord, tokenOutRegRecord, tokenOutRecord, state)) + return false; + + if (!IsValidIPCModelCheck(tx, ipcInOwnerRecord, ipcInAuthorRecord, ipcOutOwnerRecord, ipcOutUniqueRecord, ipcOutAuthorRecord, state)) + return false; + //Increase the rate of check transactions + const CTransaction& txver = tx; + if (!VerifyFee(txver,totalvintvalues-totalvoutvalues)) + return state.DoS(100, false, REJECT_INVALID, "tx-fee-can't-smaller-than-MinFee(0.001/kb)"); + return true; +} + +bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) +{ + if (tx.IsCoinBase()) + return true; // Coinbases are skipped + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + // We don't care if witness for this input is empty, since it must not be bloated. + // If the script is invalid without witness, it would be caught sooner or later during validation. + if (tx.vin[i].scriptWitness.IsNull()) + continue; + + const CTxOut &prev = mapInputs.GetOutputFor(tx.vin[i]); + + // get the scriptPubKey corresponding to this input: + CScript prevScript = prev.scriptPubKey; + + if (prevScript.IsPayToScriptHash()) { + std::vector > stack; + // If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig + // into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway. + // If the check fails at this stage, we know that this txid must be a bad one. + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE)) + return false; + if (stack.empty()) + return false; + prevScript = CScript(stack.back().begin(), stack.back().end()); + } + + int witnessversion = 0; + std::vector witnessprogram; + + // Non-witness program must not be associated with any witness + if (!prevScript.IsWitnessProgram(witnessversion, witnessprogram)) + return false; + + // Check P2WSH standard limits + if (witnessversion == 0 && witnessprogram.size() == 32) { + if (tx.vin[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) + return false; + size_t sizeWitnessStack = tx.vin[i].scriptWitness.stack.size() - 1; + if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS) + return false; + for (unsigned int j = 0; j < sizeWitnessStack; j++) { + if (tx.vin[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE) + return false; + } + } + } + return true; +} + +CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE); +CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE); +unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; + +int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost) +{ + return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR; +} + +int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost) +{ + return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost); +} diff --git a/.svn/pristine/50/50e676c9db3148e1fbf6619a47a9f42f125e8693.svn-base b/.svn/pristine/50/50e676c9db3148e1fbf6619a47a9f42f125e8693.svn-base new file mode 100644 index 0000000..4c558da --- /dev/null +++ b/.svn/pristine/50/50e676c9db3148e1fbf6619a47a9f42f125e8693.svn-base @@ -0,0 +1,246 @@ +#include "log.h" +#include +#include +#include +#include +#include "util.h" +#ifdef WIN32 + #include + const std::string DIRECTORY_SEPARATOR = "\\"; +#else + const std::string DIRECTORY_SEPARATOR = "/"; +#endif + +static Log g_log; + +Log::Log() +{ + m_logLevel[LOG_ERROR] = "error"; + m_logLevel[LOG_WARNING] = "warn"; + m_logLevel[LOG_INFO] = "info"; + m_logLevel[LOG_DEBUG] = "debug"; + m_file = NULL; + //Init(); + +} + +Log::~Log() +{ + if (m_file != NULL) { + time_t tn = time(NULL); + struct tm* now = localtime(&tn); + + fprintf(m_file, "%02d:%02d:%02d log end..........\n", now->tm_hour, now->tm_min, now->tm_sec); + fclose(m_file); + } + +} + +#ifdef WIN32 +std::string GetExePath() +{ + char buffer[MAX_PATH] = { 0 }; + + GetModuleFileNameA(NULL, buffer, MAX_PATH); + std::string file = buffer; + std::string path = ".\\"; + size_t pos = file.rfind('\\'); + + if (pos < file.size()) { + path = file.substr(0, pos + 1); + } + + return path; +} + +static void CreateDir(const std::string& path) +{ + CreateDirectoryA(path.c_str(), NULL); +} + +#else +#include +#include +#include + +#define MAX_PATH 512 +std::string GetExePath() +{ + char buffer[MAX_PATH] = { 0 }; + ssize_t ret = readlink("/proc/self/exe", buffer, MAX_PATH); + if (ret == -1) { + printf("not found [/proc/self/exe] return [./] \n"); + return "./"; + } + + std::string file = buffer; + std::string path = "./"; + size_t pos = file.rfind('/'); + + if (pos < file.size()) { + path = file.substr(0, pos + 1); + } + + return path; +} + +static void CreateDir(const std::string& path) +{ + mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +} + +#endif + +void Log::Init() +{ + CreateLogFile(false); +} + +void Log::CreateLogFile(bool newFile) +{ + if(!fPrintToDebugLog) + return; + std::string path = GetDataPath();//GetExePath(); + if(path == "")return; + std::string logPath = path + "log"; + CreateDir(logPath); + + time_t tn = time(NULL); + struct tm* now = localtime(&tn); + m_day = now->tm_mday; + + char month[64] = { 0 }; + sprintf(month, "%d", now->tm_mon + 1); + + logPath += DIRECTORY_SEPARATOR; + logPath += month; + CreateDir(logPath); + + char day[64] = { 0 }; + sprintf(day, "%d", now->tm_mday); + std::string logFile = logPath + DIRECTORY_SEPARATOR; + logFile += day; + logFile += ".txt"; + + if (newFile) { + m_file = fopen(logFile.c_str(), "w"); + } else { + m_file = fopen(logFile.c_str(), "a+"); + } + if (m_file == NULL) { + printf("create log file [%s] error\n", logFile.c_str()); + return; + } + + fprintf(m_file, "%02d:%02d:%02d log start..........\n", now->tm_hour, now->tm_min, now->tm_sec); +} +void LOG_WRITE(LOG_LEVEL level,const char* str1,\ + const char* str2, \ + const char* str3,\ + const char* str4,\ + const char* str5,\ + const char* str6,\ + const char* str7) +{ + if(!fPrintToDebugLog) + return; + std::string str(str1); + if(str2) + { + str.append(" "); + str.append(str2); + } + if(str3) + { + str.append(" "); + str.append(str3); + } + if(str4) + { + str.append(" "); + str.append(str4); + } + if(str5) + { + str.append(" "); + str.append(str5); + } + if(str6) + { + str.append(" "); + str.append(str6); + } + if(str7) + { + str.append(" "); + str.append(str7); + } + + g_log.Debug(level, str.c_str()); +} + +void Log::Debug(LOG_LEVEL level,const char* str, ...) +{ + if(!m_file){ + Init(); + } + if(!m_file) + { + return; + } + time_t tn = time(NULL); + struct tm* now = localtime(&tn); + + if (m_day != now->tm_mday) { + m_day = now->tm_mday; + fprintf(m_file, "%02d:%02d:%02d log end ..........\n", now->tm_hour, now->tm_min, now->tm_sec); + fclose(m_file); + CreateLogFile(true); + } + fprintf(m_file, "%02d:%02d:%02d ", now->tm_hour, now->tm_min, now->tm_sec); + va_list args; + va_start(args, str); + vfprintf(m_file, str, args); + va_end(args); + fprintf(m_file, "\n"); + fflush(m_file); +} + +void Log::Append(char* str, ...) +{ + va_list args; + va_start(args, str); + vfprintf(m_file, str, args); + va_end(args); + fflush(m_file); +} + +void Log::Trace(char* data, int len) +{ + fwrite(data, sizeof(char), len, m_file); + fflush(m_file); +} + +void addtest(int a,int v) +{ + return; +} + +void LOG_SETPATH(std::string path) +{ + std::cout<<"SetDataPath"<fromUnicode(qstr); + string cstr = arr.data(); + return cstr; +} diff --git a/.svn/pristine/58/58286b12740f226e5f27ac8ba1966992568b4a8d.svn-base b/.svn/pristine/58/58286b12740f226e5f27ac8ba1966992568b4a8d.svn-base new file mode 100644 index 0000000..fdbe8fd --- /dev/null +++ b/.svn/pristine/58/58286b12740f226e5f27ac8ba1966992568b4a8d.svn-base @@ -0,0 +1,293 @@ +#include "tallyaccount.h" +#include "forms/ui_tallyaccount.h" +#include "walletview.h" +#include "optionsmodel.h" +#include +#include +#include +#include +#include +#include "log/log.h" +#include +#include +#include +#include +#include "dpoc/DpocInfo.h" +#include +#include +#include "log/stateinfo.h" +using namespace std; +#include "cmessagebox.h" +#include +#include +#include "log/stateinfo.h" +extern bool g_bIpchainShutdown; + +class TextNet : public QObject +{ + Q_OBJECT +public: + static QString getHtml(QString url) + { + QNetworkAccessManager *manager = new QNetworkAccessManager(); + QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url))); + QByteArray responseData; + QEventLoop eventLoop; + connect(manager, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit())); + eventLoop.exec(); + responseData = reply->readAll(); + delete manager; + return QString(responseData); + } +}; + +TallyAccount::TallyAccount(QWidget *parent) : + QWidget(parent), + ui(new Ui::TallyAccount) +{ + ui->setupUi(this); + ui->label_neterror->hide(); + m_nTimerId = startTimer(1000*60); + + ui->tableWidget->setColumnCount(2); + ui->tableWidget->setHorizontalHeaderLabels(QStringList() << tr("time")<tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); +#if QT_VERSION < 0x050000 + ui->tableWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch); +#else + ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); +#endif + + ui->tableWidget->horizontalHeader()->setStyleSheet("QHeaderView::section{background-color:rgb(242,241,240);color: black;padding-left: 4px;border: 1px solid #6c6c6c;}"); + m_time = 0; + ui->tableWidget->verticalHeader()->setVisible(false); + ui->label_punishment->setText(""); + + + +} + +TallyAccount::~TallyAccount() +{ + killTimer(m_nTimerId); + delete ui; +} +void TallyAccount::SetnewInfo() +{ + QString info; + info =STATE_READ(); + LOG_WRITE(LOG_INFO,"state",info.toStdString().c_str()); + if("STATE = OFF" == info) + { + ui->label_punishment->setText(tr("Quitting, please be patient.")); + ui->pushButton_outaccount->hide(); + } +} +void TallyAccount::setinfo( WalletModel::keepupaccountInfo info) +{ + info_.Add_=info.Add_; + info_.Coin_=info.Coin_; + ui->label_punishment->setText(""); + ui->pushButton_outaccount->show(); + ui->label_neterror->hide(); + // timerEvent(NULL); + QDateTime time = QDateTime::currentDateTime(); + m_time = time.toTime_t(); + ui->label_shownumtip->show(); + GetTotalAmount(); +} +void TallyAccount::resettime() +{ + if(m_time > 0 )return; + QDateTime time = QDateTime::currentDateTime(); + m_time = time.toTime_t(); +} + +void TallyAccount::setModel(WalletModel * model) +{ + walletmodel = model; + connect(walletmodel,SIGNAL(updateIpcList()),this,SLOT(updateIpcList())); + updateIpcList(); + getPasswordIfTallyAccountIng(); +} +WalletModel::keepupaccountInfo TallyAccount::getinfo() +{ + return info_; +} +void TallyAccount::on_pushButton_outaccount_pressed() +{ + + WalletModel::keepupaccountInfo accinfo = getinfo(); + Q_EMIT next(accinfo.Add_,accinfo.Coin_); +} +void TallyAccount::timerEvent( QTimerEvent *event ) +{ + + if(!CDpocInfo::Instance().IsHasLocalAccount()) + { + return; + } + QString data = TextNet::getHtml(QString("http://www.baidu.com") ); + if(data.isEmpty()) + { + ui->label_neterror->show(); + } + else + { + ui->label_neterror->hide(); + } + + QDateTime timestr = QDateTime::currentDateTime(); + int timetemp = timestr.toTime_t(); + if(m_time == 0 ||(m_time > 0 && timetemp- m_time <120))return; + std::string status,hash; + LOG_WRITE(LOG_INFO,"GetConsensusStatus"); + bool finish= CDpocInfo::Instance().GetConsensusStatus(status,hash); + if(finish) + LOG_WRITE(LOG_INFO,"GetConsensusStatus back ",QString::fromStdString(status).toStdString().c_str()); + else + LOG_WRITE(LOG_INFO,"GetConsensusStatus false "); + static std::string oldstatus; + if(status == "1"||status == "4") + { + ui->label_punishment->setText(tr("General punishment.Long term abnormal bookkeeping, please standardize bookkeeping behavior.")); + ui->pushButton_outaccount->hide(); + ui->label_shownumtip->hide(); + if(oldstatus != "1" && oldstatus != "4"){ + CMessageBox *msgbox = new CMessageBox(); + msgbox->setGeometry(this->x()+(this->width()-msgbox->width())/2, + this->y()+(this->height()-msgbox->height())/2,msgbox->width(),msgbox->height()); + + msgbox->setIsClose(false); + msgbox->setMessage(1); + msgbox->show(); + } + } + else if(status == "2"||status == "5") + { + ui->label_punishment->setText(tr("Serious punishment You can't continue accounting. Quitting, please be patient.")); + ui->pushButton_outaccount->hide(); + ui->label_shownumtip->hide(); + } + else if(status == "0") + { + } + oldstatus = status; +} + +void TallyAccount::updateIpcList() +{ + QDateTime time = QDateTime::currentDateTime(); + int timeT = time.toTime_t(); + static int timeOld = 0; + if(timeOld == timeT) + { + timeOld = timeT; + return; + } + LOG_WRITE(LOG_INFO,"TallyAccountList"); + timeOld = timeT; + ui->tableWidget->clearContents(); + m_bookkeeplist.clear(); + if(walletmodel) + { + walletmodel->getbookkeepList(m_bookkeeplist); + ui->tableWidget->setRowCount(m_bookkeeplist.size()); + for(int i=0;isetTextAlignment(Qt::AlignCenter); + ui->tableWidget->setItem(row, column, item); +} + +void TallyAccount::setfinishinfo() +{ + ui->pushButton_outaccount->hide(); + ui->label_punishment->setText(tr("Quitting, please be patient.")); + ui->label_shownumtip->hide(); + +} +void TallyAccount::GetTotalAmount() +{ + int64_t u32Amount = CDpocInfo::Instance().GetTotalAmount(); + QString strtotal = QString::number(u32Amount); + if(u32Amount>100000000){ + strtotal.insert(strtotal.size()-8,'.'); + } + ui->label_totalincome->setText(strtotal); + +} + +void TallyAccount::getPasswordIfTallyAccountIng() +{ + LOG_WRITE(LOG_INFO,"IsHasLocalAccount"); + if(!CDpocInfo::Instance().IsHasLocalAccount()) + { + LOG_WRITE(LOG_INFO,"IsHasLocalAccount false"); + return; + } + LOG_WRITE(LOG_INFO,"IsHasLocalAccount true"); + QSettings settings; + int iPasswordErrorNum = settings.value("PasswordErrorNum").toInt(); + + if(iPasswordErrorNum >= 5){ + LOG_WRITE(LOG_INFO,"iPasswordErrorNum",QString::number(iPasswordErrorNum).toStdString().c_str()); + showPwdErrAndStop(); + return; + } + QLocale locale; + while(!g_bIpchainShutdown&&iPasswordErrorNum++<=5){ + if(!walletmodel->CheckPassword()) + { + LOG_WRITE(LOG_INFO,"tallyapply","psd error"); + if(iPasswordErrorNum<5){ + if( locale.language() == QLocale::Chinese ) + { + QMessageBox::information(this, tr("IPC"),tr("密码错误,请重新输入。")); + }else{ + QMessageBox::information(this, tr("IPC"),tr("Password error, please re-enter.")); + } + } + } + else{ + iPasswordErrorNum = 0; + break; + } + } + if(!g_bIpchainShutdown&&iPasswordErrorNum >= 5){ + showPwdErrAndStop(); + } + WalletModel::UnlockContext ctx(walletmodel, true, true); +} +void TallyAccount::showPwdErrAndStop() +{ + QLocale locale; + bool was_locked = (walletmodel->getEncryptionStatus() == WalletModel::Locked)?1:0; + if(!was_locked) + return; + if( locale.language() == QLocale::Chinese ) + { + QMessageBox::information(this, tr("IPC"),tr("密码错误,您将终止记账")); + return; + }else{ + QMessageBox::information(this, tr("IPC"),tr("Password Error.You will terminate the account book.")); + } + +} diff --git a/.svn/pristine/60/60d89ba14646fd7de2dbe6ded919cc23c707aa79.svn-base b/.svn/pristine/60/60d89ba14646fd7de2dbe6ded919cc23c707aa79.svn-base new file mode 100644 index 0000000..46b572f --- /dev/null +++ b/.svn/pristine/60/60d89ba14646fd7de2dbe6ded919cc23c707aa79.svn-base @@ -0,0 +1,1168 @@ + + + ecoincreatedialog + + + + 0 + 0 + 840 + 812 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 838 + 810 + + + + + + + + + + 1 + + + + + + 0 + 103 + + + + + 16777215 + 103 + + + + border:none + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 22 + 12 + 271 + 31 + + + + + -1 + + + + font-size : 28px; +color: rgb(51, 51, 51); + + + Createtoken + + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 10 + + + 30 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + TokenLabel + + + + + + + + 400 + 0 + + + + + 16777215 + 16777215 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + + + + + + + 8 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 10 + + + 30 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + PicLabel + + + + + + + + 65 + 20 + + + + + 65 + 20 + + + + + 65 + 20 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); +background-image: url(:/res/png/buttons/scan.png);border:none; + + + openpic + + + + + + + + 320 + 0 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + true + + + + + + + + + + Qt::Horizontal + + + + 371 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 10 + + + 30 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + hash + + + + + + + + 400 + 0 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + true + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 10 + + + 30 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + 100 + 0 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + TokenNum + + + + + + + + 400 + 0 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + 19 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 10 + + + 30 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + TokenTag + + + + + + + + 400 + 0 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + 16 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 30 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + accuracy + + + + + + + + 400 + 0 + + + + + 400 + 16777215 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 400 + 35 + + + + + + + background-color: rgb(150, 133, 133); + + + chooseaddress + + + + + + + Qt::Horizontal + + + + 602 + 20 + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 30 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + -1 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + TimeLabel + + + + + + + + 400 + 0 + + + + + + + + Qt::Horizontal + + + + 367 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 80 + + + + + 9999 + 80 + + + + color: rgb(255, 0, 0); + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Prompt: Payment of the transaction fee and the transaction fee of 100ipc. + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 422 + 50 + + + + + 422 + 50 + + + + background-image: url(:/res/png/buttons/421_50.png);border:none;color: rgb(255, 255, 255); + + + ok + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + CDateEdit + QLineEdit +
../CDateEdit.h
+
+
+ + +
diff --git a/.svn/pristine/62/625fcbfa7305ea835f6fc682968f41d195393bce.svn-base b/.svn/pristine/62/625fcbfa7305ea835f6fc682968f41d195393bce.svn-base new file mode 100644 index 0000000..dce77bc --- /dev/null +++ b/.svn/pristine/62/625fcbfa7305ea835f6fc682968f41d195393bce.svn-base @@ -0,0 +1,622 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_VALIDATION_H +#define BITCOIN_VALIDATION_H + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "amount.h" +#include "chain.h" +#include "coins.h" +#include "protocol.h" // For CMessageHeader::MessageStartChars +#include "script/script_error.h" +#include "sync.h" +#include "versionbits.h" +#include "primitives/transaction.h" //add by xxy + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include "dbwrapper.h" + + +class CBlockIndex; +class CBlockTreeDB; +class CBloomFilter; +class CChainParams; +class CInv; +class CConnman; +class CScriptCheck; +class CTxMemPool; +class CValidationInterface; +class CValidationState; +struct ChainTxData; + +struct PrecomputedTransactionData; +struct LockPoints; + +class IPCCheckMaps; + +#define PAYOFMINING 0.5 //IPC award, unit IPC +#define TXLABLE_MAX_LENGTH 0x0200 +#define IPC_ISSUE_VALUE 96000000 +#define TOKEN_REGTIME_BOUNDARY 1506787200 //The earliest date of registration of scrip: 20171001 time stamp +#define TOKEN_MAX_COINS 10000000000 //A total of 10 billion yuan will be issued +#define TOKEN_MAX_VALUE 1000000000000000000 + +#define ENABLE_TXLEVELDB 1 + +/** Default for DEFAULT_WHITELISTRELAY. */ +static const bool DEFAULT_WHITELISTRELAY = true; +/** Default for DEFAULT_WHITELISTFORCERELAY. */ +static const bool DEFAULT_WHITELISTFORCERELAY = true; +/** Default for -minrelaytxfee, minimum relay fee for transactions */ +static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000; +//! -maxtxfee default +static const CAmount DEFAULT_TRANSACTION_MAXFEE = 100.1 * COIN; +//! Discourage users to set fees higher than this amount (in satoshis) per kB +static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN; +//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) +static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB; +/** Default for -limitancestorcount, max number of in-mempool ancestors */ +static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25; +/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */ +static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 101; +/** Default for -limitdescendantcount, max number of in-mempool descendants */ +static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25; +/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ +static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101; +/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */ +static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336; +/** The maximum size of a blk?????.dat file (since 0.8) */ +static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ +static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB +/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ +static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB + +/** Maximum number of script-checking threads allowed */ +static const int MAX_SCRIPTCHECK_THREADS = 16; +/** -par default (number of script-checking threads, 0 = auto) */ +static const int DEFAULT_SCRIPTCHECK_THREADS = 0; +/** Number of blocks that can be requested at any given time from a single peer. */ +static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16; +/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */ +static const unsigned int BLOCK_STALLING_TIMEOUT = 2; +/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends + * less than this number, we reached its tip. Changing this value is a protocol upgrade. */ +static const unsigned int MAX_HEADERS_RESULTS = 2000; +/** Maximum depth of blocks we're willing to serve as compact blocks to peers + * when requested. For older blocks, a regular BLOCK response will be sent. */ +static const int MAX_CMPCTBLOCK_DEPTH = 5; +/** Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */ +static const int MAX_BLOCKTXN_DEPTH = 10; +/** Size of the "block download window": how far ahead of our current height do we fetch? + * Larger windows tolerate larger download speed differences between peer, but increase the potential + * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning + * harder). We'll probably want to make this a per-peer adaptive value at some point. */ +static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; +/** Time to wait (in seconds) between writing blocks/block index to disk. */ +static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; +/** Time to wait (in seconds) between flushing chainstate to disk. */ +static const unsigned int DATABASE_FLUSH_INTERVAL = 1 * 60 * 60; +/** Maximum length of reject messages. */ +static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; +/** Average delay between local address broadcasts in seconds. */ +static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 24 * 60; +/** Average delay between peer address broadcasts in seconds. */ +static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30; +/** Average delay between trickled inventory transmissions in seconds. + * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */ +static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5; +/** Maximum number of inventory items to send per transmission. + * Limits the impact of low-fee transaction floods. */ +static const unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL; +/** Average delay between feefilter broadcasts in seconds. */ +static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; +/** Maximum feefilter broadcast delay after significant change. */ +static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60; +/** Block download timeout base, expressed in millionths of the block interval (i.e. 10 min) */ +static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000; +/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */ +static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000; + +static const unsigned int DEFAULT_LIMITFREERELAY = 0; +static const bool DEFAULT_RELAYPRIORITY = true; +static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; +/** Maximum age of our tip in seconds for us to be considered current for fee estimation */ +static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60; + +/** Default for -permitbaremultisig */ +static const bool DEFAULT_PERMIT_BAREMULTISIG = true; +static const bool DEFAULT_CHECKPOINTS_ENABLED = true; +static const bool DEFAULT_TXINDEX = true; +static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100; + +/** Default for -mempoolreplacement */ +static const bool DEFAULT_ENABLE_REPLACEMENT = true; +/** Default for using fee filter */ +static const bool DEFAULT_FEEFILTER = true; + +/** Maximum number of headers to announce when relaying blocks with headers message.*/ +static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8; + +/** Maximum number of unconnecting headers announcements before DoS score */ +static const int MAX_UNCONNECTING_HEADERS = 10; + +static const bool DEFAULT_PEERBLOOMFILTERS = true; +extern std::map tokenDataMap; //Token map key:symnbol value:TokenRegLabel +bool getAddressBalanceByTxlevel(std::string& address, CAmount& balance, CAmount& received, CAmount& sended, uint64_t& txidnum); + +CAmount Getfeebytxid(const CTransaction tx); //Gets the transaction fee for the specified transaction +uint64_t getTokenAllcoins(uint8_t accuracy); //The maximum of totalCount is registered according to the precision + +bool VerifyFee(const CTransaction& tx,CAmount nneedFee); //The transaction fee of the calibration transaction +struct BlockHasher +{ + size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); } +}; + +extern bool g_bConsensus; +extern CScript COINBASE_FLAGS; +extern CCriticalSection cs_main; +extern CTxMemPool mempool; +typedef boost::unordered_map BlockMap; +extern BlockMap mapBlockIndex; +extern uint64_t nLastBlockTx; +extern uint64_t nLastBlockSize; +extern uint64_t nLastBlockWeight; +extern const std::string strMessageMagic; +extern CWaitableCriticalSection csBestBlock; +extern CConditionVariable cvBlockChange; +extern std::atomic_bool fImporting; +extern bool fReindex; +extern int nScriptCheckThreads; +extern bool fTxIndex; +extern bool fIsBareMultisigStd; +extern bool fRequireStandard; +extern bool fCheckBlockIndex; +//Browser buffer switch +extern bool fAddressIndex; +// +extern bool fCheckpointsEnabled; +extern size_t nCoinCacheUsage; +/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */ +extern CFeeRate minRelayTxFee; +/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */ +extern CAmount maxTxFee; +/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */ +extern int64_t nMaxTipAge; +extern bool fEnableReplacement; + +/** Block hash whose ancestors we will assume to have valid scripts without checking them. */ +extern uint256 hashAssumeValid; + +/** Best header we've seen so far (used for getheaders queries' starting points). */ +extern CBlockIndex *pindexBestHeader; + +/** Minimum disk space required - used in CheckDiskSpace() */ +static const uint64_t nMinDiskSpace = 52428800; + +/** Pruning-related variables and constants */ +/** True if any block files have ever been pruned. */ +extern bool fHavePruned; +/** True if we're running in -prune mode. */ +extern bool fPruneMode; +/** Number of MiB of block files that we're trying to stay below. */ +extern uint64_t nPruneTarget; +/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */ +static const unsigned int MIN_BLOCKS_TO_KEEP = 288; + +static const signed int DEFAULT_CHECKBLOCKS = 6; +static const unsigned int DEFAULT_CHECKLEVEL = 3; + +// Require that user allocate at least 550MB for block & undo files (blk???.dat and rev???.dat) +// At 1MB per block, 288 blocks = 288MB. +// Add 15% for Undo data = 331MB +// Add 20% for Orphan block rate = 397MB +// We want the low water mark after pruning to be at least 397 MB and since we prune in +// full block file chunks, we need the high water mark which triggers the prune to be +// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB +// Setting the target to > than 550MB will make it likely we can respect the target. +static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; + +/** + * Process an incoming block. This only returns after the best known valid + * block is made active. Note that it does not, however, guarantee that the + * specific block passed to it has been checked for validity! + * + * If you want to *possibly* get feedback on whether pblock is valid, you must + * install a CValidationInterface (see validationinterface.h) - this will have + * its BlockChecked method called whenever *any* block completes validation. + * + * Note that we guarantee that either the proof-of-work is valid on pblock, or + * (and possibly also) BlockChecked will have been called. + * + * Call without cs_main held. + * + * @param[in] pblock The block we want to process. + * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers. + * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call + * @return True if state.IsValid() + */ +bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr pblock, bool fForceProcessing, bool* fNewBlock); + +/** + * Process incoming block headers. + * + * Call without cs_main held. + * + * @param[in] block The block headers themselves + * @param[out] state This may be set to an Error state if any error occurred processing them + * @param[in] chainparams The params for the chain we want to connect to + * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers + */ +bool ProcessNewBlockHeaders(const std::vector& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=NULL); + +/** Check whether enough disk space is available for an incoming block */ +bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); +/** Open a block file (blk?????.dat) */ +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Open an undo file (rev?????.dat) */ +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Translation to a filesystem path */ +boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix); +/** Import blocks from an external file */ +bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = NULL); +/** Initialize a new block tree database + block data on disk */ +bool InitBlockIndex(const CChainParams& chainparams); +/** Load the block tree and coins database from disk */ +bool LoadBlockIndex(const CChainParams& chainparams); +/** Unload database information */ +void UnloadBlockIndex(); +/** Run an instance of the script checking thread */ +void ThreadScriptCheck(); +/** Check whether we are doing an initial block download (synchronizing from disk or network) */ +bool IsInitialBlockDownload(); +/** Format a string that describes several potential problems detected by the core. + * strFor can have three values: + * - "rpc": get critical warnings, which should put the client in safe mode if non-empty + * - "statusbar": get all warnings + * - "gui": get all warnings, translated (where possible) for GUI + * This function only returns the highest priority warning of the set selected by strFor. + */ +std::string GetWarnings(const std::string& strFor); +/** Retrieve a transaction (from memory pool, or from disk, if possible) */ +bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); +/** Look for deals from the current temporary trading list +A temporary trading list is a block of trading links +Only the prevout information that needed to detect txin was sufficient to find the original transaction itself +Thus, a list of transactions that have been detected is cached in the process as a basis for subsequent transactions */ +bool GetCachedChainTransaction(const uint256 &hash, CTransactionRef &tx); + +/** Find the best known block, and make it the tip of the block chain */ +bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr pblock = std::shared_ptr()); +CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); + +/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ +double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex); + +/** + * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. + * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new + * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex + * (which in this case means the blockchain must be re-downloaded.) + * + * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set. + * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.) + * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest). + * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip. + * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files. + * A db flag records the fact that at least some block files have been pruned. + * + * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned + */ +void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight); + +/** + * Mark one block file as pruned. + */ +void PruneOneBlockFile(const int fileNumber); + +/** + * Actually unlink the specified files + */ +void UnlinkPrunedFiles(const std::set& setFilesToPrune); + +/** Create a new block index entry for a given block hash */ +CBlockIndex * InsertBlockIndex(uint256 hash); +/** Flush all state, indexes and buffers to disk. */ +void FlushStateToDisk(); +/** Prune block files and flush state to disk. */ +void PruneAndFlush(); +/** Prune block files up to a given height */ +void PruneBlockFilesManual(int nPruneUpToHeight); + +/** (try to) add transaction to memory pool + * plTxnReplaced will be appended to with all transactions replaced from mempool **/ +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, + bool* pfMissingInputs, std::list* plTxnReplaced = NULL, + bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); + +/** (try to) add transaction to memory pool with a specified acceptance time **/ +bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, + bool* pfMissingInputs, int64_t nAcceptTime, std::list* plTxnReplaced = NULL, + bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); + +/** Convert CValidationState to a human-readable message for logging */ +std::string FormatStateMessage(const CValidationState &state); + +/** Get the BIP9 state for a given deployment at the current tip. */ +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos); + +/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */ +int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos); + +/** + * Count ECDSA signature operations the old-fashioned (pre-0.6) way + * @return number of sigops this transaction's outputs will produce when spent + * @see CTransaction::FetchInputs + */ +unsigned int GetLegacySigOpCount(const CTransaction& tx); + +/** + * Count ECDSA signature operations in pay-to-script-hash inputs. + * + * @param[in] mapInputs Map of previous transactions that have outputs we're spending + * @return maximum number of sigops required to validate this transaction's inputs + * @see CTransaction::FetchInputs + */ +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& mapInputs); + +/** + * Compute total signature operation cost of a transaction. + * @param[in] tx Transaction for which we are computing the cost + * @param[in] inputs Map of previous transactions that have outputs we're spending + * @param[out] flags Script verification flags + * @return Total signature operation cost of tx + */ +int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags); + +/** + * Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) + * This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it + * instead of being performed inline. + */ +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, + unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = NULL); + +/** Apply the effects of this transaction on the UTXO set represented by view */ +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); + +/** Transaction validation functions */ + +/** Context-independent validity checks */ +bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true); + +namespace Consensus { + +/** + * Check whether all inputs of this transaction are valid (no double spends and amounts) + * This does not modify the UTXO set. This does not check scripts and sigs. + * Preconditions: tx.IsCoinBase() is false. + */ +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight); + +} // namespace Consensus + +/** + * Check if transaction is final and can be included in a block with the + * specified height and time. Consensus critical. + */ +bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime); + + +/** + * Check if transaction will be final in the next block to be created. + * + * Calls IsFinalTx() with current block height and appropriate block time. + * + * See consensus/consensus.h for flag definitions. + */ +bool CheckFinalTx(const CTransaction &tx, int flags = -1); + +//IPC's nLockTime detection function +bool IsFinalPrevTx(const CTransactionRef prevtx, int nBlockHeight); +bool CheckIPCFinalTx(const CTransaction &tx); + +/** + * Test whether the LockPoints height and time are still valid on the current chain + */ +bool TestLockPointValidity(const LockPoints* lp); + +/** + * Check if transaction is final per BIP 68 sequence numbers and can be included in a block. + * Consensus critical. Takes as input a list of heights at which tx's inputs (in order) confirmed. + */ +bool SequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeights, const CBlockIndex& block); + +/** + * Check if transaction will be BIP 68 final in the next block to be created. + * + * Simulates calling SequenceLocks() with data from the tip of the current active chain. + * Optionally stores in LockPoints the resulting height and time calculated and the hash + * of the block needed for calculation or skips the calculation and uses the LockPoints + * passed in for evaluation. + * The LockPoints should not be considered valid if CheckSequenceLocks returns false. + * + * See consensus/consensus.h for flag definitions. + */ +bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = NULL, bool useExistingLockPoints = false); + +/** + * Closure representing one script verification + * Note that this stores references to the spending transaction + */ +class CScriptCheck +{ +private: + CScript scriptPubKey; + CAmount amount; + const CTransaction *ptxTo; + unsigned int nIn; + unsigned int nFlags; + bool cacheStore; + ScriptError error; + PrecomputedTransactionData *txdata; + +public: + CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : + scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue), + ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } + + bool operator()(); + + void swap(CScriptCheck &check) { + scriptPubKey.swap(check.scriptPubKey); + std::swap(ptxTo, check.ptxTo); + std::swap(amount, check.amount); + std::swap(nIn, check.nIn); + std::swap(nFlags, check.nFlags); + std::swap(cacheStore, check.cacheStore); + std::swap(error, check.error); + std::swap(txdata, check.txdata); + } + + ScriptError GetScriptError() const { return error; } +}; + + +/** Functions for disk access for blocks */ +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); + +/** Functions for validating blocks and updating the block tree */ + +/** Context-independent validity checks */ +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true); +bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true); + +/** Context-dependent validity checks. + * By "context", we mean only the previous block headers, but not the UTXO + * set; UTXO-related validity checks are done in ConnectBlock(). */ +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime); +bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); + +/** Apply the effects of this block (with given index) on the UTXO set represented by coins. + * Validity checks that depend on the UTXO set are also done; ConnectBlock() + * can fail if those validity checks fail (among other reasons). */ +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, + const CChainParams& chainparams, bool fJustCheck = false); + +/** Undo the effects of this block (with given index) on the UTXO set represented by coins. + * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean + * will be true if no problems were found. Otherwise, the return value will be false in case + * of problems. Note that in any case, coins may be modified. */ +bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); + +/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ +bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); + +/** Check whether witness commitments are required for block. */ +bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); + +/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */ +bool RewindBlockIndex(const CChainParams& params); + +/** Update uncommitted block structures (currently: only the witness nonce). This is safe for submitted blocks. */ +void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); + +/** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */ +std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); + +/** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */ +class CVerifyDB { +public: + CVerifyDB(); + ~CVerifyDB(); + bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); + bool FlushICMToDisk(); + bool LoadICMFromDisk(); +}; + +/** Find the last common block between the parameter chain and a locator. */ +CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator); + +/** Mark a block as precious and reorganize.*/ +bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex); + +/** Mark a block as invalid. */ +bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex); + +/** Remove invalidity status from a block and its descendants. */ +bool ResetBlockFailureFlags(CBlockIndex *pindex); + +/** The currently-connected chain of blocks (protected by cs_main). */ +extern CChain chainActive; + +/** Global variable that points to the active CCoinsView (protected by cs_main) */ +extern CCoinsViewCache *pcoinsTip; + +/** Global variable that points to the active block tree (protected by cs_main) */ +extern CBlockTreeDB *pblocktree; + +/** Global variable that points to the IPC unique symbols*/ +extern IPCCheckMaps* pIPCCheckMaps; + + +extern TxDBProcess* pTxDB; + +/** + * Return the spend height, which is one more than the inputs.GetBestBlock(). + * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) + * This is also true for mempool checks. + */ +int GetSpendHeight(const CCoinsViewCache& inputs); + +extern VersionBitsCache versionbitscache; + +/** + * Determine what nVersion a new block should use. + */ +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params); + +/** Reject codes greater or equal to this can be returned by AcceptToMemPool + * for transactions, to signal internal conditions. They cannot and should not + * be sent over the P2P network. + */ +static const unsigned int REJECT_INTERNAL = 0x100; +/** Too high fee. Can not be triggered by P2P transactions */ +static const unsigned int REJECT_HIGHFEE = 0x100; +/** Transaction is already known (either in mempool or blockchain) */ +static const unsigned int REJECT_ALREADY_KNOWN = 0x101; +/** Transaction conflicts with a transaction already known */ +static const unsigned int REJECT_CONFLICT = 0x102; + +/** Get block file info entry for one block file */ +CBlockFileInfo* GetBlockFileInfo(size_t n); + +/** Dump the mempool to disk. */ +void DumpMempool(); + +/** Load the mempool from disk. */ +bool LoadMempool(); + +#endif // BITCOIN_VALIDATION_H diff --git a/.svn/pristine/66/66c2a68f270de3f5bb3823f6a86505f1798386d9.svn-base b/.svn/pristine/66/66c2a68f270de3f5bb3823f6a86505f1798386d9.svn-base new file mode 100644 index 0000000..8f91dc5 --- /dev/null +++ b/.svn/pristine/66/66c2a68f270de3f5bb3823f6a86505f1798386d9.svn-base @@ -0,0 +1,77 @@ +#include "cmessagebox.h" +#include "forms/ui_cmessagebox.h" +#include "dpoc/DpocInfo.h" +#include "log/stateinfo.h" + +CMessageBox::CMessageBox(QWidget *parent) : + QDialog(parent), + ui(new Ui::CMessageBox) +{ + ui->setupUi(this); + + Qt::WindowFlags flags=Qt::Dialog; + flags |=Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + + + + m_answertype = 0; + this->setFixedSize(524, 287); + this->resize(524, 287); + if( true == getIsClose()) + { + try{ + if(!CDpocInfo::Instance().IsHasLocalAccount()) + { + ui->label_message->setText(tr("Confirm exit?")); + } + else + { + ui->label_message->setText(tr("Is Bookkeeping,Confirm exit?")); + } + } + catch(...){ + } + } +} + +CMessageBox::~CMessageBox() +{ + delete ui; +} +void CMessageBox::setCacelVisible(bool isVisible) +{ + if(!isVisible) + { + ui->pushButton__cancle->setVisible(false); + + ui->horizontalLayout->removeWidget(ui->pushButton__cancle); + ui->pushButton_ok->setGeometry(ui->widget->width()/2,ui->pushButton_ok->y(),205,44); + } + +} +void CMessageBox::on_pushButton_ok_pressed() +{ + m_answertype = 1; + this->accept(); +} + +void CMessageBox::on_pushButton__cancle_pressed() +{ + m_answertype = 0; + this->accept(); +} +void CMessageBox::setMessage(QString msg) +{ + ui->label_message->setText(msg); +} +void CMessageBox::setMessage(int msg) +{ + if(msg == 1) + ui->label_message->setText(tr("General punishment.Long term abnormal bookkeeping, please standardize bookkeeping behavior.")); + if(msg == 2) + ui->label_message->setText(tr("Password input error more than 5 times, please prohibit input within a day!")); + if(msg == 3) + ui->label_message->setText(tr("Date Updating,please waiting...")); + +} diff --git a/.svn/pristine/71/71615e3e5d0a3b179b7807cf2b05064919b13101.svn-base b/.svn/pristine/71/71615e3e5d0a3b179b7807cf2b05064919b13101.svn-base new file mode 100644 index 0000000..1d54ac8 --- /dev/null +++ b/.svn/pristine/71/71615e3e5d0a3b179b7807cf2b05064919b13101.svn-base @@ -0,0 +1,1749 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "init.h" + +#include "addrman.h" +#include "amount.h" +#include "chain.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "compat/sanity.h" +#include "consensus/validation.h" +#include "httpserver.h" +#include "httprpc.h" +#include "key.h" +#include "validation.h" +#include "miner.h" +#include "netbase.h" +#include "net.h" +#include "net_processing.h" +#include "policy/policy.h" +#include "rpc/server.h" +#include "rpc/register.h" +#include "script/standard.h" +#include "script/sigcache.h" +#include "scheduler.h" +#include "timedata.h" +#include "txdb.h" +#include "txmempool.h" +#include "torcontrol.h" +#include "ui_interface.h" +#include "util.h" +#include "utilmoneystr.h" +#include "validationinterface.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif +#include "warnings.h" +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dpoc/DpocInfo.h" +#include "dpoc/DpocMining.h" + +#include "dpoc/TimeService.h" +#include "dpoc/ConsensusAccountPool.h" + +#if ENABLE_ZMQ +#include "zmq/zmqnotificationinterface.h" +#endif + +bool fFeeEstimatesInitialized = false; +static const bool DEFAULT_PROXYRANDOMIZE = true; +static const bool DEFAULT_REST_ENABLE = false; +static const bool DEFAULT_DISABLE_SAFEMODE = false; +static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; +static bool fHaveGenesis = false; +static boost::mutex cs_GenesisWait; +static CConditionVariable condvar_GenesisWait; +std::unique_ptr g_connman; +std::unique_ptr peerLogic; + +#if ENABLE_ZMQ +static CZMQNotificationInterface* pzmqNotificationInterface = NULL; +#endif + +#ifdef WIN32 +// Win32 LevelDB doesn't use filedescriptors, and the ones used for +// accessing block files don't count towards the fd_set size limit +// anyway. +#define MIN_CORE_FILEDESCRIPTORS 0 +#else +#define MIN_CORE_FILEDESCRIPTORS 150 +#endif + +/** Used to pass flags to the Bind() function */ +enum BindFlags { + BF_NONE = 0, + BF_EXPLICIT = (1U << 0), + BF_REPORT_ERROR = (1U << 1), + BF_WHITELIST = (1U << 2), +}; + +static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; + +////////////////////////////////////////////////////////////////////////////// +// +// Shutdown +// + +// +// Thread management and startup/shutdown: +// +// The network-processing threads are all part of a thread group +// created by AppInit() or the Qt main() function. +// +// A clean exit happens when StartShutdown() or the SIGTERM +// signal handler sets fRequestShutdown, which triggers +// the DetectShutdownThread(), which interrupts the main thread group. +// DetectShutdownThread() then exits, which causes AppInit() to +// continue (it .joins the shutdown thread). +// Shutdown() is then +// called to clean up database connections, and stop other +// threads that should only be stopped after the main network-processing +// threads have exited. +// +// Note that if running -daemon the parent process returns from AppInit2 +// before adding any threads to the threadGroup, so .join_all() returns +// immediately and the parent exits from main(). +// +// Shutdown for Qt is very similar, only it uses a QTimer to detect +// fRequestShutdown getting set, and then does the normal Qt +// shutdown thing. +// + +std::atomic fRequestShutdown(false); +std::atomic fDumpMempoolLater(false); + +void StartShutdown() +{ + fRequestShutdown = true; +} +bool ShutdownRequested() +{ + return fRequestShutdown; +} + +/** + * This is a minimally invasive approach to shutdown on LevelDB read errors from the + * chainstate, while keeping user interface out of the common library, which is shared + * between bitcoind, and bitcoin-qt and non-server tools. +*/ +class CCoinsViewErrorCatcher : public CCoinsViewBacked +{ +public: + CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {} + bool GetCoins(const uint256 &txid, CCoins &coins) const { + try { + return CCoinsViewBacked::GetCoins(txid, coins); + } catch(const std::runtime_error& e) { + uiInterface.ThreadSafeMessageBox(_("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR); + LogPrintf("Error reading from database: %s\n", e.what()); + // Starting the shutdown sequence and returning false to the caller would be + // interpreted as 'entry not found' (as opposed to unable to read data), and + // could lead to invalid interpretation. Just exit immediately, as we can't + // continue anyway, and all writes should be atomic. + abort(); + } + } + // Writes do not need similar protection, as failure to write is handled by the caller. +}; + +static CCoinsViewDB *pcoinsdbview = NULL; +static CCoinsViewErrorCatcher *pcoinscatcher = NULL; +static std::unique_ptr globalVerifyHandle; + +void Interrupt(boost::thread_group& threadGroup) +{ + InterruptHTTPServer(); + InterruptHTTPRPC(); + InterruptRPC(); + InterruptREST(); + InterruptTorControl(); + if (g_connman) + g_connman->Interrupt(); + threadGroup.interrupt_all(); +} + +void Shutdown() +{ + LogPrintf("%s: In progress...\n", __func__); + + CDpocMining::Instance().stop(); + + static CCriticalSection cs_Shutdown; + TRY_LOCK(cs_Shutdown, lockShutdown); + if (!lockShutdown) + return; + + /// Note: Shutdown() must be able to handle cases in which AppInit2() failed part of the way, + /// for example if the data directory was found to be locked. + /// Be sure that anything that writes files or flushes caches only does this if the respective + /// module was initialized. + RenameThread("ipchain-shutoff"); + mempool.AddTransactionsUpdated(1); + + StopHTTPRPC(); + StopREST(); + StopRPC(); + StopHTTPServer(); +#ifdef ENABLE_WALLET + if (pwalletMain) + pwalletMain->Flush(false); +#endif + MapPort(false); + UnregisterValidationInterface(peerLogic.get()); + peerLogic.reset(); + g_connman.reset(); + + StopTorControl(); + UnregisterNodeSignals(GetNodeSignals()); + if (fDumpMempoolLater) + DumpMempool(); + + if (fFeeEstimatesInitialized) + { + boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; + LogPrintf("Shutdown(): before CAutoFile est_fileout est_path%s\n",est_path.string().c_str()); + CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION); + if (!est_fileout.IsNull()) + mempool.WriteFeeEstimates(est_fileout); + else + LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string()); + fFeeEstimatesInitialized = false; + } + LogPrintf("Shutdown(): before if (pIPCCheckMaps != NULL)\n"); + { + if (pIPCCheckMaps != NULL) + { + delete(pIPCCheckMaps); + } + pIPCCheckMaps = NULL; + + + if (pTxDB != NULL) + { + delete(pTxDB); + } + pTxDB = NULL; + + LOCK(cs_main); + if (pcoinsTip != NULL) { + LogPrintf("Shutdown(): before FlushStateToDisk();\n"); + FlushStateToDisk(); + LogPrintf("Shutdown(): after FlushStateToDisk();\n"); + } + delete pcoinsTip; + pcoinsTip = NULL; + delete pcoinscatcher; + pcoinscatcher = NULL; + delete pcoinsdbview; + pcoinsdbview = NULL; + delete pblocktree; + pblocktree = NULL; + } +#ifdef ENABLE_WALLET + if (pwalletMain) + pwalletMain->Flush(true); +#endif + +#if ENABLE_ZMQ + if (pzmqNotificationInterface) { + UnregisterValidationInterface(pzmqNotificationInterface); + delete pzmqNotificationInterface; + pzmqNotificationInterface = NULL; + } +#endif + LogPrintf("Shutdown(): before #ifndef WIN32\n"); +#ifndef WIN32 + try { + boost::filesystem::remove(GetPidFile()); + } catch (const boost::filesystem::filesystem_error& e) { + LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what()); + } +#endif + UnregisterAllValidationInterfaces(); +#ifdef ENABLE_WALLET + delete pwalletMain; + pwalletMain = NULL; +#endif + globalVerifyHandle.reset(); + LogPrintf("Shutdown(): before ECC_Stop();\n"); + ECC_Stop(); + timeService.stop(); + LogPrintf("%s: done\n", __func__); +} + +/** + * Signal handlers are very limited in what they are allowed to do, so: + */ +void HandleSIGTERM(int) +{ + fRequestShutdown = true; +} + +void HandleSIGHUP(int) +{ + fReopenDebugLog = true; + fReopenDebugLogQ = true; +} + +bool static Bind(CConnman& connman, const CService &addr, unsigned int flags) { + if (!(flags & BF_EXPLICIT) && IsLimited(addr)) + return false; + std::string strError; + if (!connman.BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) { + if (flags & BF_REPORT_ERROR) + return InitError(strError); + return false; + } + return true; +} +void OnRPCStarted() +{ + uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange); +} + +void OnRPCStopped() +{ + uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange); + RPCNotifyBlockChange(false, nullptr); + cvBlockChange.notify_all(); + LogPrint("rpc", "RPC stopped.\n"); +} + +void OnRPCPreCommand(const CRPCCommand& cmd) +{ + // Observe safe mode + std::string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) && + !cmd.okSafeMode) + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); +} + +std::string HelpMessage(HelpMessageMode mode) +{ + const bool showDebug = GetBoolArg("-help-debug", false); + + // When adding new options to the categories, please keep and ensure alphabetical ordering. + // Do not translate _(...) -help-debug options, Many technical terms, and only a very small audience, so is unnecessary stress to translators. + std::string strUsage = HelpMessageGroup(_("Options:")); + strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); + strUsage += HelpMessageOpt("-version", _("Print version and exit")); + strUsage += HelpMessageOpt("-alertnotify=", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); + strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); + if (showDebug) + strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY)); + strUsage +=HelpMessageOpt("-assumevalid=", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), Params(CBaseChainParams::MAIN).GetConsensus().defaultAssumeValid.GetHex(), Params(CBaseChainParams::TESTNET).GetConsensus().defaultAssumeValid.GetHex())); + strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); + if (mode == HMM_BITCOIND) + { +#if HAVE_DECL_DAEMON + strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands")); +#endif + } + strUsage += HelpMessageOpt("-datadir=", _("Specify data directory")); + strUsage += HelpMessageOpt("-dbcache=", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); + if (showDebug) + strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER)); + strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); + strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); + strUsage += HelpMessageOpt("-maxmempool=", strprintf(_("Keep the transaction memory pool below megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); + strUsage += HelpMessageOpt("-mempoolexpiry=", strprintf(_("Do not keep transactions in the mempool longer than hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); + strUsage += HelpMessageOpt("-blockreconstructionextratxn=", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); + strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), + -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); +#ifndef WIN32 + strUsage += HelpMessageOpt("-pid=", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME)); +#endif + strUsage += HelpMessageOpt("-prune=", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " + "Warning: Reverting this setting requires re-downloading the entire blockchain. " + "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); + strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); +#ifndef WIN32 + strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); +#endif + strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX)); + + strUsage += HelpMessageGroup(_("Connection options:")); + strUsage += HelpMessageOpt("-addnode=", _("Add a node to connect to and attempt to keep the connection open")); + strUsage += HelpMessageOpt("-banscore=", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD)); + strUsage += HelpMessageOpt("-bantime=", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME)); + strUsage += HelpMessageOpt("-bind=", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6")); + strUsage += HelpMessageOpt("-connect=", _("Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections")); + strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)")); + strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP)); + strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect)")); + strUsage += HelpMessageOpt("-externalip=", _("Specify your own public address")); + strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED)); + strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)")); + strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION)); + strUsage += HelpMessageOpt("-maxconnections=", strprintf(_("Maintain at most connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS)); + strUsage += HelpMessageOpt("-maxreceivebuffer=", strprintf(_("Maximum per-connection receive buffer, *1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER)); + strUsage += HelpMessageOpt("-maxsendbuffer=", strprintf(_("Maximum per-connection send buffer, *1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER)); + strUsage += HelpMessageOpt("-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT)); + strUsage += HelpMessageOpt("-onion=", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy")); + strUsage += HelpMessageOpt("-onlynet=", _("Only connect to nodes in network (ipv4, ipv6 or onion)")); + strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG)); + strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS)); + strUsage += HelpMessageOpt("-port=", strprintf(_("Listen for connections on (default: %u or testnet: %u)"), Params(CBaseChainParams::MAIN).GetDefaultPort(), Params(CBaseChainParams::TESTNET).GetDefaultPort())); + strUsage += HelpMessageOpt("-proxy=", _("Connect through SOCKS5 proxy")); + strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE)); + strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION)); + strUsage += HelpMessageOpt("-seednode=", _("Connect to a node to retrieve peer addresses, and disconnect")); + strUsage += HelpMessageOpt("-timeout=", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); + strUsage += HelpMessageOpt("-torcontrol=:", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); + strUsage += HelpMessageOpt("-torpassword=", _("Tor control port password (default: empty)")); +#ifdef USE_UPNP +#if USE_UPNP + strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)")); +#else + strUsage += HelpMessageOpt("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0)); +#endif +#endif + strUsage += HelpMessageOpt("-whitebind=", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6")); + strUsage += HelpMessageOpt("-whitelist=", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") + + " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); + strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); + strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY)); + strUsage += HelpMessageOpt("-maxuploadtarget=", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); + +#ifdef ENABLE_WALLET + strUsage += CWallet::GetWalletHelpString(showDebug); +#endif + +#if ENABLE_ZMQ + strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); + strUsage += HelpMessageOpt("-zmqpubhashblock=
", _("Enable publish hash block in
")); + strUsage += HelpMessageOpt("-zmqpubhashtx=
", _("Enable publish hash transaction in
")); + strUsage += HelpMessageOpt("-zmqpubrawblock=
", _("Enable publish raw block in
")); + strUsage += HelpMessageOpt("-zmqpubrawtx=
", _("Enable publish raw transaction in
")); +#endif + + strUsage += HelpMessageGroup(_("Debugging/Testing options:")); + strUsage += HelpMessageOpt("-uacomment=", _("Append comment to the user agent string")); + if (showDebug) + { + strUsage += HelpMessageOpt("-checkblocks=", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); + strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); + strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); + strUsage += HelpMessageOpt("-checkmempool=", strprintf("Run checks every transactions (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); + strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); + strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE)); + strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE)); + strUsage += HelpMessageOpt("-dropmessagestest=", "Randomly drop 1 of every network messages"); + strUsage += HelpMessageOpt("-fuzzmessagestest=", "Randomly fuzz 1 of every network messages"); + strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT)); + strUsage += HelpMessageOpt("-limitancestorcount=", strprintf("Do not accept transactions if number of in-mempool ancestors is or more (default: %u)", DEFAULT_ANCESTOR_LIMIT)); + strUsage += HelpMessageOpt("-limitancestorsize=", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT)); + strUsage += HelpMessageOpt("-limitdescendantcount=", strprintf("Do not accept transactions if any ancestor would have or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); + strUsage += HelpMessageOpt("-limitdescendantsize=", strprintf("Do not accept transactions if any ancestor would have more than kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); + strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)"); + } + std::string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below + if (mode == HMM_BITCOIN_QT) + debugCategories += ", qt"; + strUsage += HelpMessageOpt("-debug=", strprintf(_("Output debugging information (default: %u, supplying is optional)"), 0) + ". " + + _("If is not supplied or if = 1, output all debugging information.") + _(" can be:") + " " + debugCategories + "."); + if (showDebug) + strUsage += HelpMessageOpt("-nodebug", "Turn off debugging messages, same as -debug=0"); + strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)")); + strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS)); + strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS)); + if (showDebug) + { + strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS)); + strUsage += HelpMessageOpt("-mocktime=", "Replace actual time with seconds since epoch (default: 0)"); + strUsage += HelpMessageOpt("-limitfreerelay=", strprintf("Continuously rate-limit free transactions to *1000 bytes per minute (default: %u)", DEFAULT_LIMITFREERELAY)); + strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", DEFAULT_RELAYPRIORITY)); + strUsage += HelpMessageOpt("-maxsigcachesize=", strprintf("Limit size of signature cache to MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); + strUsage += HelpMessageOpt("-maxtipage=", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE)); + } + strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE))); + strUsage += HelpMessageOpt("-maxtxfee=", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE))); + strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); + if (showDebug) + { + strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction priority and fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); + } + strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); + + AppendParamsHelpMessages(strUsage, showDebug); + + strUsage += HelpMessageGroup(_("Node relay options:")); + if (showDebug) { + strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard())); + strUsage += HelpMessageOpt("-incrementalrelayfee=", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE))); + strUsage += HelpMessageOpt("-dustrelayfee=", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); + } + strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); + strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER)); + strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); + strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT)); + + strUsage += HelpMessageGroup(_("Block creation options:")); + strUsage += HelpMessageOpt("-blockmaxweight=", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); + strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); + strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); + strUsage += HelpMessageOpt("-blockmintxfee=", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); + if (showDebug) + strUsage += HelpMessageOpt("-blockversion=", "Override block version to test forking scenarios"); + + strUsage += HelpMessageGroup(_("RPC server options:")); + strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); + strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE)); + strUsage += HelpMessageOpt("-rpcbind=", _("Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)")); + strUsage += HelpMessageOpt("-rpccookiefile=", _("Location of the auth cookie (default: data dir)")); + strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); + strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); + strUsage += HelpMessageOpt("-rpcauth=", _("Username and hashed password for JSON-RPC connections. The field comes in the format: :$. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=/rpcpassword= pair of arguments. This option can be specified multiple times")); + strUsage += HelpMessageOpt("-rpcport=", strprintf(_("Listen for JSON-RPC connections on (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort())); + strUsage += HelpMessageOpt("-rpcallowip=", _("Allow JSON-RPC connections from specified source. Valid for are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times")); + strUsage += HelpMessageOpt("-rpcthreads=", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS)); + if (showDebug) { + strUsage += HelpMessageOpt("-rpcworkqueue=", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE)); + strUsage += HelpMessageOpt("-rpcservertimeout=", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT)); + } + + return strUsage; +} + +std::string LicenseInfo() +{ + const std::string URL_SOURCE_CODE = ""; + const std::string URL_WEBSITE = ""; + + return CopyrightHolders(strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) + " ") + "\n" + + "\n" + + strprintf(_("Please contribute if you find %s useful. " + "Visit %s for further information about the software."), + PACKAGE_NAME, URL_WEBSITE) + + "\n" + + strprintf(_("The source code is available from %s."), + URL_SOURCE_CODE) + + "\n" + + "\n" + + _("This is experimental software.") + "\n" + + strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s"), "COPYING", "") + "\n" + + "\n" + + strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard."), "") + + "\n"; +} + +static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex) +{ + if (initialSync || !pBlockIndex) + return; + + std::string strCmd = GetArg("-blocknotify", ""); + + boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free +} + + + +static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) +{ + if (pBlockIndex != NULL) { + { + boost::unique_lock lock_GenesisWait(cs_GenesisWait); + fHaveGenesis = true; + } + condvar_GenesisWait.notify_all(); + } +} + +struct CImportingNow +{ + CImportingNow() { + assert(fImporting == false); + fImporting = true; + } + + ~CImportingNow() { + assert(fImporting == true); + fImporting = false; + } +}; + + +// If we're using -prune with -reindex, then delete block files that will be ignored by the +// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile +// is missing, do the same here to delete any later block files after a gap. Also delete all +// rev files since they'll be rewritten by the reindex anyway. This ensures that vinfoBlockFile +// is in sync with what's actually on disk by the time we start downloading, so that pruning +// works correctly. +void CleanupBlockRevFiles() +{ + std::map mapBlockFiles; + + // Glob all blk?????.dat and rev?????.dat files from the blocks directory. + // Remove the rev files immediately and insert the blk file paths into an + // ordered map keyed by block file index. + LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); + boost::filesystem::path blocksdir = GetDataDir() / "blocks"; + for (boost::filesystem::directory_iterator it(blocksdir); it != boost::filesystem::directory_iterator(); it++) { + if (is_regular_file(*it) && + it->path().filename().string().length() == 12 && + it->path().filename().string().substr(8,4) == ".dat") + { + if (it->path().filename().string().substr(0,3) == "blk") + mapBlockFiles[it->path().filename().string().substr(3,5)] = it->path(); + else if (it->path().filename().string().substr(0,3) == "rev") + remove(it->path()); + } + } + + // Remove all block files that aren't part of a contiguous set starting at + // zero by walking the ordered map (keys are block file indices) by + // keeping a separate counter. Once we hit a gap (or if 0 doesn't exist) + // start removing block files. + int nContigCounter = 0; + BOOST_FOREACH(const PAIRTYPE(std::string, boost::filesystem::path)& item, mapBlockFiles) { + if (atoi(item.first) == nContigCounter) { + nContigCounter++; + continue; + } + remove(item.second); + } +} + +void ThreadImport(std::vector vImportFiles) +{ + const CChainParams& chainparams = Params(); + RenameThread("ipchain-loadblk"); + + { + CImportingNow imp; + + // -reindex + if (fReindex) { + int nFile = 0; + while (true) { + CDiskBlockPos pos(nFile, 0); + if (!boost::filesystem::exists(GetBlockPosFilename(pos, "blk"))) + break; // No block files left to reindex + FILE *file = OpenBlockFile(pos, true); + if (!file) + break; // This error is logged in OpenBlockFile + LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); + LoadExternalBlockFile(chainparams, file, &pos); + nFile++; + } + pblocktree->WriteReindexing(false); + fReindex = false; + LogPrintf("Reindexing finished\n"); + // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): + InitBlockIndex(chainparams); + } + + // hardcoded $DATADIR/bootstrap.dat + boost::filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (boost::filesystem::exists(pathBootstrap)) { + FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + if (file) { + boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + LogPrintf("Importing bootstrap.dat...\n"); + LoadExternalBlockFile(chainparams, file); + RenameOver(pathBootstrap, pathBootstrapOld); + } else { + LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string()); + } + } + + // -loadblock= + BOOST_FOREACH(const boost::filesystem::path& path, vImportFiles) { + FILE *file = fopen(path.string().c_str(), "rb"); + if (file) { + LogPrintf("Importing blocks file %s...\n", path.string()); + LoadExternalBlockFile(chainparams, file); + } else { + LogPrintf("Warning: Could not open blocks file %s\n", path.string()); + } + } + + // scan for better chains in the block chain database, that are not yet connected in the active best chain + CValidationState state; + if (!ActivateBestChain(state, chainparams)) { + LogPrintf("Failed to connect best block\n"); + StartShutdown(); + } + + if (GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { + LogPrintf("Stopping after block import\n"); + StartShutdown(); + } + } // End scope of CImportingNow + LoadMempool(); + fDumpMempoolLater = !fRequestShutdown; + LogPrintf("end threadGroup.create_thread\n"); +} + +/** Sanity checks + * Ensure that Bitcoin is running in a usable environment with all + * necessary library support. + */ +bool InitSanityCheck(void) +{ + if(!ECC_InitSanityCheck()) { + InitError("Elliptic curve cryptography sanity check failure. Aborting."); + return false; + } + if (!glibc_sanity_test() || !glibcxx_sanity_test()) + return false; + + return true; +} + +bool AppInitServers(boost::thread_group& threadGroup) +{ + RPCServer::OnStarted(&OnRPCStarted); + RPCServer::OnStopped(&OnRPCStopped); + RPCServer::OnPreCommand(&OnRPCPreCommand); + if (!InitHTTPServer()) + return false; + if (!StartRPC()) + return false; + if (!StartHTTPRPC()) + return false; + if (GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) + return false; + if (!StartHTTPServer()) + return false; + return true; +} + +// Parameter interaction based on rules +void InitParameterInteraction() +{ + // when specifying an explicit binding address, you want to listen on it + // even when -connect or -proxy is specified + if (IsArgSet("-bind")) { + if (SoftSetBoolArg("-listen", true)) + LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__); + } + if (IsArgSet("-whitebind")) { + if (SoftSetBoolArg("-listen", true)) + LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__); + } + + if (mapMultiArgs.count("-connect") && mapMultiArgs.at("-connect").size() > 0) { + // when only connecting to trusted nodes, do not seed via DNS, or listen by default + if (SoftSetBoolArg("-dnsseed", false)) + LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__); + if (SoftSetBoolArg("-listen", false)) + LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__); + } + + if (IsArgSet("-proxy")) { + // to protect privacy, do not listen by default if a default proxy server is specified + if (SoftSetBoolArg("-listen", false)) + LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); + // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 + // to listen locally, so don't rely on this happening through -listen below. + if (SoftSetBoolArg("-upnp", false)) + LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); + // to protect privacy, do not discover addresses by default + if (SoftSetBoolArg("-discover", false)) + LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); + } + + if (!GetBoolArg("-listen", DEFAULT_LISTEN)) { + // do not map ports or try to retrieve public IP when not listening (pointless) + if (SoftSetBoolArg("-upnp", false)) + LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); + if (SoftSetBoolArg("-discover", false)) + LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); + if (SoftSetBoolArg("-listenonion", false)) + LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__); + } + + if (IsArgSet("-externalip")) { + // if an explicit public IP is specified, do not try to find others + if (SoftSetBoolArg("-discover", false)) + LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__); + } + + // disable whitelistrelay in blocksonly mode + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) { + if (SoftSetBoolArg("-whitelistrelay", false)) + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__); + } + + // Forcing relay from whitelisted hosts implies we will accept relays from them in the first place. + if (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { + if (SoftSetBoolArg("-whitelistrelay", true)) + LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); + } +} + +static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) +{ + return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); +} + +void InitLogging() +{ + fPrintToConsole = GetBoolArg("-printtoconsole", false); + fLogTimestamps = GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); + fLogTimeMicros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + fLogIPs = GetBoolArg("-logips", DEFAULT_LOGIPS); + + LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + LogPrintf("Ipchain version %s\n", FormatFullVersion()); +} + +namespace { // Variables internal to initialization process only + +ServiceFlags nRelevantServices = NODE_NETWORK; +int nMaxConnections; +int nUserMaxConnections; +int nFD; +ServiceFlags nLocalServices = NODE_NETWORK; + +} + +[[noreturn]] static void new_handler_terminate() +{ + // Rather than throwing std::bad-alloc if allocation fails, terminate + // immediately to (try to) avoid chain corruption. + // Since LogPrintf may itself allocate memory, set the handler directly + // to terminate first. + std::set_new_handler(std::terminate); + LogPrintf("Error: Out of memory. Terminating.\n"); + + // The log was successful, terminate now. + std::terminate(); +}; + +bool AppInitBasicSetup() +{ + // ********************************************************* Step 1: setup +#ifdef _MSC_VER + // Turn off Microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif +#if _MSC_VER >= 1400 + // Disable confusing "helpful" text message on abort, Ctrl-C + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif +#ifdef WIN32 + // Enable Data Execution Prevention (DEP) + // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 + // A failure is non-critical and needs no further attention! +#ifndef PROCESS_DEP_ENABLE + // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), + // which is not correct. Can be removed, when GCCs winbase.h is fixed! +#define PROCESS_DEP_ENABLE 0x00000001 +#endif + typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); + PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); + if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); +#endif + + if (!SetupNetworking()) + return InitError("Initializing networking failed"); + +#ifndef WIN32 + if (!GetBoolArg("-sysperms", false)) { + umask(077); + } + + // Clean shutdown on SIGTERM + struct sigaction sa; + sa.sa_handler = HandleSIGTERM; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + // Reopen debug.log on SIGHUP + struct sigaction sa_hup; + sa_hup.sa_handler = HandleSIGHUP; + sigemptyset(&sa_hup.sa_mask); + sa_hup.sa_flags = 0; + sigaction(SIGHUP, &sa_hup, NULL); + + // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly + signal(SIGPIPE, SIG_IGN); +#endif + + std::set_new_handler(new_handler_terminate); + + return true; +} + +bool AppInitParameterInteraction() +{ + const CChainParams& chainparams = Params(); + // ********************************************************* Step 2: parameter interactions + + // also see: InitParameterInteraction() + + // if using block pruning, then disallow txindex + if (GetArg("-prune", 0)) { + if (GetBoolArg("-txindex", DEFAULT_TXINDEX)) + return InitError(_("Prune mode is incompatible with -txindex.")); + } + + // Make sure enough file descriptors are available + int nBind = std::max( + (mapMultiArgs.count("-bind") ? mapMultiArgs.at("-bind").size() : 0) + + (mapMultiArgs.count("-whitebind") ? mapMultiArgs.at("-whitebind").size() : 0), size_t(1)); + nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); + nMaxConnections = std::max(nUserMaxConnections, 0); + + // Trim requested connection counts, to fit into system limitations + nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS)), 0); + nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS); + if (nFD < MIN_CORE_FILEDESCRIPTORS) + return InitError(_("Not enough file descriptors available.")); + nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections); + + if (nMaxConnections < nUserMaxConnections) + InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections)); + + // ********************************************************* Step 3: parameter-to-internal-flags + + fDebug = mapMultiArgs.count("-debug"); + // Special-case: if -debug=0/-nodebug is set, turn off debugging messages + if (fDebug) { + const std::vector& categories = mapMultiArgs.at("-debug"); + if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("0")) != categories.end()) + fDebug = false; + } + + // Check for -debugnet + if (GetBoolArg("-debugnet", false)) + InitWarning(_("Unsupported argument -debugnet ignored, use -debug=net.")); + // Check for -socks - as this is a privacy risk to continue, exit here + if (IsArgSet("-socks")) + return InitError(_("Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); + // Check for -tor - as this is a privacy risk to continue, exit here + if (GetBoolArg("-tor", false)) + return InitError(_("Unsupported argument -tor found, use -onion.")); + + if (GetBoolArg("-benchmark", false)) + InitWarning(_("Unsupported argument -benchmark ignored, use -debug=bench.")); + + if (GetBoolArg("-whitelistalwaysrelay", false)) + InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.")); + + if (IsArgSet("-blockminsize")) + InitWarning("Unsupported argument -blockminsize ignored."); + + // Checkmempool and checkblockindex default to true in regtest mode + int ratio = std::min(std::max(GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); + if (ratio != 0) { + mempool.setSanityCheck(1.0 / ratio); + } + fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); + fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); + + hashAssumeValid = uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex())); + if (!hashAssumeValid.IsNull()) + LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex()); + else + LogPrintf("Validating signatures for all blocks.\n"); + + // mempool limits + int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t nMempoolSizeMin = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; + if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) + return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0))); + // incremental relay fee sets the minimimum feerate increase necessary for BIP 125 replacement in the mempool + // and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting. + if (IsArgSet("-incrementalrelayfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-incrementalrelayfee", ""), n)) + return InitError(AmountErrMsg("incrementalrelayfee", GetArg("-incrementalrelayfee", ""))); + incrementalRelayFee = CFeeRate(n); + } + + // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency + nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); + if (nScriptCheckThreads <= 0) + nScriptCheckThreads += GetNumCores(); + if (nScriptCheckThreads <= 1) + nScriptCheckThreads = 0; + else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) + nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; + + // block pruning; get the amount of disk space (in MiB) to allot for block & undo files + int64_t nPruneArg = GetArg("-prune", 0); + if (nPruneArg < 0) { + return InitError(_("Prune cannot be configured with a negative value.")); + } + nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024; + if (nPruneArg == 1) { // manual pruning: -prune=1 + LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); + nPruneTarget = std::numeric_limits::max(); + fPruneMode = true; + } else if (nPruneTarget) { + if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { + return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + } + LogPrintf("Prune configured to target %uMiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024); + fPruneMode = true; + } + + RegisterAllCoreRPCCommands(tableRPC); +#ifdef ENABLE_WALLET + RegisterWalletRPCCommands(tableRPC); +#endif + + nConnectTimeout = GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); + if (nConnectTimeout <= 0) + nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; + + // Fee-per-kilobyte amount considered the same as "free" + // If you are mining, be careful setting this: + // if you set it to zero then + // a transaction spammer can cheaply fill blocks using + // 1-satoshi-fee transactions. It should be set above the real + // cost to you of processing a transaction. + if (IsArgSet("-minrelaytxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-minrelaytxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("minrelaytxfee", GetArg("-minrelaytxfee", ""))); + // High fee check is done afterward in CWallet::ParameterInteraction() + ::minRelayTxFee = CFeeRate(n); + } else if (incrementalRelayFee > ::minRelayTxFee) { + // Allow only setting incrementalRelayFee to control both + ::minRelayTxFee = incrementalRelayFee; + LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString()); + } + + // Sanity check argument for min fee for including tx in block + // TODO: Harmonize which arguments need sanity checking and where that happens + if (IsArgSet("-blockmintxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-blockmintxfee", ""), n)) + return InitError(AmountErrMsg("blockmintxfee", GetArg("-blockmintxfee", ""))); + } + + // Feerate used to define dust. Shouldn't be changed lightly as old + // implementations may inadvertently create non-standard transactions + if (IsArgSet("-dustrelayfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-dustrelayfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("dustrelayfee", GetArg("-dustrelayfee", ""))); + dustRelayFee = CFeeRate(n); + } + + fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); + if (chainparams.RequireStandard() && !fRequireStandard) + return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); + nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp); + +#ifdef ENABLE_WALLET + if (!CWallet::ParameterInteraction()) + return false; +#endif + + fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); + fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); + nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); + + // Option to startup with mocktime set (used for regression testing): + SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op + + if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) + nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); + + if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0) + return InitError("rpcserialversion must be non-negative."); + + if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1) + return InitError("unknown rpcserialversion requested."); + + nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); + + fEnableReplacement = GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT); + if ((!fEnableReplacement) && IsArgSet("-mempoolreplacement")) { + // Minimal effort at forwards compatibility + std::string strReplacementModeList = GetArg("-mempoolreplacement", ""); // default is impossible + std::vector vstrReplacementModes; + boost::split(vstrReplacementModes, strReplacementModeList, boost::is_any_of(",")); + fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end()); + } + + if (mapMultiArgs.count("-bip9params")) { + // Allow overriding BIP9 parameters for testing + if (!chainparams.MineBlocksOnDemand()) { + return InitError("BIP9 parameters may only be overridden on regtest."); + } + const std::vector& deployments = mapMultiArgs.at("-bip9params"); + for (auto i : deployments) { + std::vector vDeploymentParams; + boost::split(vDeploymentParams, i, boost::is_any_of(":")); + if (vDeploymentParams.size() != 3) { + return InitError("BIP9 parameters malformed, expecting deployment:start:end"); + } + int64_t nStartTime, nTimeout; + if (!ParseInt64(vDeploymentParams[1], &nStartTime)) { + return InitError(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); + } + if (!ParseInt64(vDeploymentParams[2], &nTimeout)) { + return InitError(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2])); + } + bool found = false; + for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) + { + if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) { + //zou mark UpdateRegtestBIP9Parameters(Consensus::DeploymentPos(j), nStartTime, nTimeout); + found = true; + LogPrintf("Setting BIP9 activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout); + break; + } + } + if (!found) { + return InitError(strprintf("Invalid deployment (%s)", vDeploymentParams[0])); + } + } + } + return true; +} + +static bool LockDataDirectory(bool probeOnly) +{ + std::string strDataDir = GetDataDir().string(); + + // Make sure only a single ipcchain process is using the data directory. + boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; + FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. + if (file) fclose(file); + + try { + static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); + if (!lock.try_lock()) { + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), strDataDir, _(PACKAGE_NAME))); + } + if (probeOnly) { + lock.unlock(); + } + } catch(const boost::interprocess::interprocess_exception& e) { + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what())); + } + return true; +} + +bool AppInitSanityChecks() +{ + // ********************************************************* Step 4: sanity checks + + // Initialize elliptic curve code + ECC_Start(); + globalVerifyHandle.reset(new ECCVerifyHandle()); + + // Sanity check + if (!InitSanityCheck()) + return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME))); + + // Probe the data directory lock to give an early error message, if possible + return LockDataDirectory(true); +} + +bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) +{ + const CChainParams& chainparams = Params(); + // ********************************************************* Step 4a: application initialization + // After daemonization get the data directory lock again and hold on to it until exit + // This creates a slight window for a race condition to happen, however this condition is harmless: it + // will at most make us exit without printing a message to console. + if (!LockDataDirectory(false)) { + // Detailed error printed inside LockDataDirectory + return false; + } + +#ifndef WIN32 + CreatePidFile(GetPidFile(), getpid()); +#endif + if (GetBoolArg("-shrinkdebugfile", !fDebug)) { + // Do this first since it both loads a bunch of debug.log into memory, + // and because this needs to happen before any other debug.log printing + ShrinkDebugFile(); + } + + if (fPrintToDebugLog) + OpenDebugLog(); + + if (!fLogTimestamps) + LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); + LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); + LogPrintf("Using data directory %s\n", GetDataDir().string()); + LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); + LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); + + InitSignatureCache(); + + LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); + if (nScriptCheckThreads) { + for (int i=0; i, "scheduler", serviceLoop)); + + /* Start the RPC server already. It will be started in "warmup" mode + * and not really process calls already (but it will signify connections + * that the server is there and will be ready later). Warmup mode will + * be disabled when initialisation is finished. + */ + if (GetBoolArg("-server", false)) + { + uiInterface.InitMessage.connect(SetRPCWarmupStatus); + if (!AppInitServers(threadGroup)) + return InitError(_("Unable to start HTTP server. See debug log for details.")); + } + + int64_t nStart; + + // ********************************************************* Step 5: verify wallet database integrity +#ifdef ENABLE_WALLET + if (!CWallet::Verify()) + return false; +#endif + // ********************************************************* Step 6: network initialization + // Note that we absolutely cannot open any actual connections + // until the very end ("start node") as the UTXO/block state + // is not yet setup and may end up being set up twice if we + // need to reindex later. + + assert(!g_connman); + g_connman = std::unique_ptr(new CConnman(GetRand(std::numeric_limits::max()), GetRand(std::numeric_limits::max()))); + CConnman& connman = *g_connman; + + peerLogic.reset(new PeerLogicValidation(&connman)); + RegisterValidationInterface(peerLogic.get()); + RegisterNodeSignals(GetNodeSignals()); + + // sanitize comments per BIP-0014, format user agent and check total size + std::vector uacomments; + if (mapMultiArgs.count("-uacomment")) { + BOOST_FOREACH(std::string cmt, mapMultiArgs.at("-uacomment")) + { + if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) + return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); + uacomments.push_back(cmt); + } + } + strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); + if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { + return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."), + strSubVersion.size(), MAX_SUBVERSION_LENGTH)); + } + + if (mapMultiArgs.count("-onlynet")) { + std::set nets; + BOOST_FOREACH(const std::string& snet, mapMultiArgs.at("-onlynet")) { + enum Network net = ParseNetwork(snet); + if (net == NET_UNROUTABLE) + return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); + nets.insert(net); + } + for (int n = 0; n < NET_MAX; n++) { + enum Network net = (enum Network)n; + if (!nets.count(net)) + SetLimited(net); + } + } + + if (mapMultiArgs.count("-whitelist")) { + BOOST_FOREACH(const std::string& net, mapMultiArgs.at("-whitelist")) { + CSubNet subnet; + LookupSubNet(net.c_str(), subnet); + if (!subnet.IsValid()) + return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net)); + connman.AddWhitelistedRange(subnet); + } + } + + bool proxyRandomize = GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE); + // -proxy sets a proxy for all outgoing network traffic + // -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default + std::string proxyArg = GetArg("-proxy", ""); + SetLimited(NET_TOR); + if (proxyArg != "" && proxyArg != "0") { + CService resolved(LookupNumeric(proxyArg.c_str(), 9050)); + proxyType addrProxy = proxyType(resolved, proxyRandomize); + if (!addrProxy.IsValid()) + return InitError(strprintf(_("Invalid -proxy address: '%s'"), proxyArg)); + + SetProxy(NET_IPV4, addrProxy); + SetProxy(NET_IPV6, addrProxy); + SetProxy(NET_TOR, addrProxy); + SetNameProxy(addrProxy); + SetLimited(NET_TOR, false); // by default, -proxy sets onion as reachable, unless -noonion later + } + + // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses + // -noonion (or -onion=0) disables connecting to .onion entirely + // An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none) + std::string onionArg = GetArg("-onion", ""); + if (onionArg != "") { + if (onionArg == "0") { // Handle -noonion/-onion=0 + SetLimited(NET_TOR); // set onions as unreachable + } else { + CService resolved(LookupNumeric(onionArg.c_str(), 9050)); + proxyType addrOnion = proxyType(resolved, proxyRandomize); + if (!addrOnion.IsValid()) + return InitError(strprintf(_("Invalid -onion address: '%s'"), onionArg)); + SetProxy(NET_TOR, addrOnion); + SetLimited(NET_TOR, false); + } + } + + // see Step 2: parameter interactions for more information about these + fListen = GetBoolArg("-listen", DEFAULT_LISTEN); + fDiscover = GetBoolArg("-discover", true); + fNameLookup = GetBoolArg("-dns", DEFAULT_NAME_LOOKUP); + fRelayTxes = !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); + + if (fListen) { + bool fBound = false; + if (mapMultiArgs.count("-bind")) { + BOOST_FOREACH(const std::string& strBind, mapMultiArgs.at("-bind")) { + CService addrBind; + if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) + return InitError(ResolveErrMsg("bind", strBind)); + fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); + } + } + if (mapMultiArgs.count("-whitebind")) { + BOOST_FOREACH(const std::string& strBind, mapMultiArgs.at("-whitebind")) { + CService addrBind; + if (!Lookup(strBind.c_str(), addrBind, 0, false)) + return InitError(ResolveErrMsg("whitebind", strBind)); + if (addrBind.GetPort() == 0) + return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind)); + fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST)); + } + } + if (!mapMultiArgs.count("-bind") && !mapMultiArgs.count("-whitebind")) { + struct in_addr inaddr_any; + inaddr_any.s_addr = INADDR_ANY; + fBound |= Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE); + fBound |= Bind(connman, CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); + } + if (!fBound) + return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); + } + + if (mapMultiArgs.count("-externalip")) { + BOOST_FOREACH(const std::string& strAddr, mapMultiArgs.at("-externalip")) { + CService addrLocal; + if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) + AddLocal(addrLocal, LOCAL_MANUAL); + else + return InitError(ResolveErrMsg("externalip", strAddr)); + } + } + + if (mapMultiArgs.count("-seednode")) { + BOOST_FOREACH(const std::string& strDest, mapMultiArgs.at("-seednode")) + connman.AddOneShot(strDest); + } + +#if ENABLE_ZMQ + pzmqNotificationInterface = CZMQNotificationInterface::Create(); + + if (pzmqNotificationInterface) { + RegisterValidationInterface(pzmqNotificationInterface); + } +#endif + uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set + uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME; + + if (IsArgSet("-maxuploadtarget")) { + nMaxOutboundLimit = GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024; + } + + // ********************************************************* Step 7: load block chain + + fReindex = GetBoolArg("-reindex", false); + bool fReindexChainState = GetBoolArg("-reindex-chainstate", false); + + fAddressIndex = GetBoolArg("-addressindex", false); + + // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ + boost::filesystem::path blocksDir = GetDataDir() / "blocks"; + if (!boost::filesystem::exists(blocksDir)) + { + boost::filesystem::create_directories(blocksDir); + bool linked = false; + for (unsigned int i = 1; i < 10000; i++) { + boost::filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i); + if (!boost::filesystem::exists(source)) break; + boost::filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); + try { + boost::filesystem::create_hard_link(source, dest); + LogPrintf("Hardlinked %s -> %s\n", source.string(), dest.string()); + linked = true; + } catch (const boost::filesystem::filesystem_error& e) { + // Note: hardlink creation failing is not a disaster, it just means + // blocks will get re-downloaded from peers. + LogPrintf("Error hardlinking blk%04u.dat: %s\n", i, e.what()); + break; + } + } + if (linked) + { + fReindex = true; + } + } + + // cache size calculations + int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); + nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache + nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache + int64_t nBlockTreeDBCache = nTotalCache / 8; + nBlockTreeDBCache = std::min(nBlockTreeDBCache, (GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20); + nTotalCache -= nBlockTreeDBCache; + int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache + nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache + nTotalCache -= nCoinDBCache; + nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache + int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + LogPrintf("Cache configuration:\n"); + LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); + + bool fLoaded = false; + while (!fLoaded) { + bool fReset = fReindex; + std::string strLoadError; + + uiInterface.InitMessage(_("Loading block index...")); + + nStart = GetTimeMillis(); + do { + try { + UnloadBlockIndex(); + delete pcoinsTip; + delete pcoinsdbview; + delete pcoinscatcher; + delete pblocktree; + + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState); + pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); + pcoinsTip = new CCoinsViewCache(pcoinscatcher); + + + delete pIPCCheckMaps; + pIPCCheckMaps = new IPCCheckMaps(); + + delete pTxDB; + pTxDB = new TxDBProcess(); + pTxDB->Init(); + + + if (fReindex) { + pblocktree->WriteReindexing(true); + //If we're reindexing in prune mode, wipe away unusable block files and all undo data files + if (fPruneMode) + CleanupBlockRevFiles(); + } + + LogPrintf("[INIT] begin LoadBlockIndex\n"); + if (!LoadBlockIndex(chainparams)) { + strLoadError = _("Error loading block database"); + break; + } + + // If the loaded chain has a wrong genesis, bail out immediately + // (we're likely using a testnet datadir, or the other way around). + if (!mapBlockIndex.empty() && mapBlockIndex.count(chainparams.GetConsensus().hashGenesisBlock) == 0) + return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); + + // Initialize the block index (no-op if non-empty database was already loaded) + if (!InitBlockIndex(chainparams)) { + strLoadError = _("Error initializing block database"); + break; + } + + // Check for changed -txindex state + if (fTxIndex != GetBoolArg("-txindex", DEFAULT_TXINDEX)) { + strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -txindex"); + break; + } + + // Check for changed -prune state. What we are concerned about is a user who has pruned blocks + // in the past, but is now trying to run unpruned. + if (fHavePruned && !fPruneMode) { + strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain"); + break; + } + + if (!fReindex && chainActive.Tip() != NULL) { + uiInterface.InitMessage(_("Rewinding blocks...")); + if (!RewindBlockIndex(chainparams)) { + strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain"); + break; + } + } + + LogPrintf("[INIT] uiInterface.InitMessage(_(\"Verifying blocks...\"))\n"); + uiInterface.InitMessage(_("Verifying blocks...")); + if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { + LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks", + MIN_BLOCKS_TO_KEEP); + } + + { + LOCK(cs_main); + CBlockIndex* tip = chainActive.Tip(); + RPCNotifyBlockChange(true, tip); + if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { + strLoadError = _("The block database contains a block which appears to be from the future. " + "This may be due to your computer's date and time being set incorrectly. " + "Only rebuild the block database if you are sure that your computer's date and time are correct"); + break; + } + } + + LogPrintf("[INIT] prepare CVerifyDB().VerifyDB\n"); + if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, GetArg("-checklevel", DEFAULT_CHECKLEVEL), + GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { + strLoadError = _("Corrupted block database detected"); + break; + } + + LogPrintf("[INIT] Start the time server\n"); + timeService.start(); + + LogPrintf("[INIT] Analyze local snapshots\n"); + struct timeval begintime; + gettimeofday(&begintime, NULL); + CConsensusAccountPool::Instance().analysisConsensusSnapshots(); + + //Set consensus + std::string strPublicKey; + strPublicKey.clear(); + if (CDpocInfo::Instance().GetLocalAccount(strPublicKey)) + { + CDpocInfo::Instance().setJoinCampaign(true); + CDpocInfo::Instance().setLocalAccoutVar(strPublicKey); + } + + struct timeval finishtime; + gettimeofday(&finishtime, NULL); + LogPrintf("[INIT] Analyzing local blocks takes time %d seconds \n", finishtime.tv_sec - begintime.tv_sec); + + LogPrintf("[INIT] Start the meeting thread\n"); + CDpocMining::Instance().start(); + //------------------------------------dpoc end + + + LogPrintf("[INIT] CVerifyDB().LoadICMFromDisk\n"); + //Change to read local disk data into memory + tokenDataMap.clear(); + CVerifyDB().LoadICMFromDisk(); + + + + + } catch (const std::exception& e) { + if (fDebug) LogPrintf("%s\n", e.what()); + strLoadError = _("Error opening block database"); + break; + } + + fLoaded = true; + } while(false); + + if (!fLoaded) { + // first suggest a reindex + if (!fReset) { + bool fRet = uiInterface.ThreadSafeQuestion( + strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), + strLoadError + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", + "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); + if (fRet) { + fReindex = true; + fRequestShutdown = false; + } else { + LogPrintf("Aborted block database rebuild. Exiting.\n"); + return false; + } + } else { + return InitError(strLoadError); + } + } + } + + + // As LoadBlockIndex can take several minutes, it's possible the user + // requested to kill the GUI during the last operation. If so, exit. + // As the program has not fully started yet, Shutdown() is possibly overkill. + if (fRequestShutdown) + { + LogPrintf("Shutdown requested. Exiting.\n"); + return false; + } + LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); + + boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; + CAutoFile est_filein(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION); + // Allowed to fail as this file IS missing on first startup. + if (!est_filein.IsNull()) + mempool.ReadFeeEstimates(est_filein); + fFeeEstimatesInitialized = true; + + // ********************************************************* Step 8: load wallet +#ifdef ENABLE_WALLET + if (!CWallet::InitLoadWallet()) + return false; +#else + LogPrintf("No wallet support compiled in!\n"); +#endif + + if (IsArgSet("-pswd")) { + pwalletMain->curstrWalletPassphrase.reserve(100); + pwalletMain->curstrWalletPassphrase = GetArg("-pswd", "").c_str(); + } + + + // ********************************************************* Step 9: data directory maintenance + + // if pruning, unset the service bit and perform the initial blockstore prune + // after any wallet rescanning has taken place. + if (fPruneMode) { + LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); + nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK); + if (!fReindex) { + uiInterface.InitMessage(_("Pruning blockstore...")); + PruneAndFlush(); + } + } + + if (chainparams.GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) { + // Only advertise witness capabilities if they have a reasonable start time. + // This allows us to have the code merged without a defined softfork, by setting its + // end time to 0. + // Note that setting NODE_WITNESS is never required: the only downside from not + // doing so is that after activation, no upgraded nodes will fetch from you. + nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS); + // Only care about others providing witness capabilities if there is a softfork + // defined. + nRelevantServices = ServiceFlags(nRelevantServices | NODE_WITNESS); + } + + // ********************************************************* Step 10: import blocks + + if (!CheckDiskSpace()) + return false; + + // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly. + // No locking, as this happens before any background thread is started. + if (chainActive.Tip() == NULL) { + uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait); + } else { + fHaveGenesis = true; + } + + if (IsArgSet("-blocknotify")) + uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); + + std::vector vImportFiles; + if (mapMultiArgs.count("-loadblock")) + { + BOOST_FOREACH(const std::string& strFile, mapMultiArgs.at("-loadblock")) + vImportFiles.push_back(strFile); + } + threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); + + // Wait for genesis block to be processed + { + + boost::unique_lock lock(cs_GenesisWait); + while (!fHaveGenesis&&condvar_GenesisWait.wait_for(lock,boost::chrono::seconds(3)) == boost::cv_status::timeout) { + if(fRequestShutdown){ + LogPrintf("fRequestShutdown,condvar_GenesisWait return)\n"); + return false; + } + } + uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); + } + + // ********************************************************* Step 11: start node + + //// debug print + LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); + LogPrintf("nBestHeight = %d\n", chainActive.Height()); + if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) + StartTorControl(threadGroup, scheduler); + + Discover(threadGroup); + + // Map ports with UPnP + MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); + + std::string strNodeError; + CConnman::Options connOptions; + connOptions.nLocalServices = nLocalServices; + connOptions.nRelevantServices = nRelevantServices; + connOptions.nMaxConnections = nMaxConnections; + connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); + connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; + connOptions.nMaxFeeler = 1; + connOptions.nBestHeight = chainActive.Height(); + connOptions.uiInterface = &uiInterface; + connOptions.nSendBufferMaxSize = 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); + connOptions.nReceiveFloodSize = 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); + + connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; + connOptions.nMaxOutboundLimit = nMaxOutboundLimit; + + if (!connman.Start(scheduler, strNodeError, connOptions)) + return InitError(strNodeError); + + // ********************************************************* Step 12: finished + + SetRPCWarmupFinished(); + uiInterface.InitMessage(_("Done loading")); + +#ifdef ENABLE_WALLET + if (pwalletMain) + pwalletMain->postInitProcess(threadGroup); +#endif + + return !fRequestShutdown; +} diff --git a/.svn/pristine/7d/7d0084573460651c36d76633c9987748a4bdb4f5.svn-base b/.svn/pristine/7d/7d0084573460651c36d76633c9987748a4bdb4f5.svn-base new file mode 100644 index 0000000..26f93db --- /dev/null +++ b/.svn/pristine/7d/7d0084573460651c36d76633c9987748a4bdb4f5.svn-base @@ -0,0 +1,152 @@ +#include "stateinfo.h" +#include "log.h" +#include +#include +#include +#include +#include "util.h" +#ifdef WIN32 +#include +const std::string DIRECTORY_SEPARATOR = "\\"; +#else +const std::string DIRECTORY_SEPARATOR = "/"; +#endif + +//static Log g_log; +static stateinfo state_info; +stateinfo::stateinfo() +{ +} + +stateinfo::~stateinfo() +{ +} + +#ifdef WIN32 + + +static void CreateDir(const std::string& path) +{ + CreateDirectoryA(path.c_str(), NULL); +} + +#else +#include +#include +#include + +#define MAX_PATH 512 + +static void CreateDir(const std::string& path) +{ + mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +} + +#endif + +void STATE_SETPATH(std::string path) +{ + std::cout<<"SetDataPath"< + +#include + +#include + +#include "script/ismine.h" +#include "dpoc/DpocInfo.h" +#include "dpoc/ConsensusAccountPool.h" + + +using namespace std; + +int64_t nWalletUnlockTime; +static CCriticalSection cs_nWalletUnlockTime; +extern bool b_TestTxLarge; +std::string HelpRequiringPassphrase() +{ + return pwalletMain && pwalletMain->IsCrypted() + ? "\nRequires wallet passphrase to be set with walletpassphrase call." + : ""; +} + +bool EnsureWalletIsAvailable(bool avoidException) +{ + if (!pwalletMain) + { + if (!avoidException) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + else + return false; + } + return true; +} + +void EnsureWalletIsUnlocked() +{ + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); +} + +void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) +{ + int confirms = wtx.GetDepthInMainChain(); + entry.push_back(Pair("confirmations", confirms)); + if (wtx.IsCoinBase()) + entry.push_back(Pair("generated", true)); + if (confirms > 0) + { + entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); + entry.push_back(Pair("blockindex", wtx.nIndex)); + entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); + } else { + entry.push_back(Pair("trusted", wtx.IsTrusted())); + } + uint256 hash = wtx.GetHash(); + entry.push_back(Pair("txid", hash.GetHex())); + UniValue conflicts(UniValue::VARR); + BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) + conflicts.push_back(conflict.GetHex()); + entry.push_back(Pair("walletconflicts", conflicts)); + entry.push_back(Pair("time", wtx.GetTxTime())); + entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); + + // Add opt-in RBF status + std::string rbfStatus = "no"; + if (confirms <= 0) { + LOCK(mempool.cs); + RBFTransactionState rbfState = IsRBFOptIn(wtx, mempool); + if (rbfState == RBF_TRANSACTIONSTATE_UNKNOWN) + rbfStatus = "unknown"; + else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125) + rbfStatus = "yes"; + } + entry.push_back(Pair("bip125-replaceable", rbfStatus)); + + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const UniValue& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name"); + return strAccount; +} + +UniValue getnewaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1) + throw runtime_error( + "getnewaddress ( \"account\" )\n" + "\nReturns a new Bitcoin address for receiving payments.\n" + "If 'account' is specified (DEPRECATED), it is added to the address book \n" + "so payments received with the address will be credited to 'account'.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) DEPRECATED. The account name for the address to be linked to. If not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n" + "\nResult:\n" + "\"address\" (string) The new bitcoin address\n" + "\nExamples:\n" + + HelpExampleCli("getnewaddress", "") + + HelpExampleRpc("getnewaddress", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (request.params.size() > 0) + strAccount = AccountFromValue(request.params[0]); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + // Generate a new key that is added to wallet + CPubKey newKey; + if (!pwalletMain->GetKeyFromPool(newKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + CKeyID keyID = newKey.GetID(); + + pwalletMain->SetAddressBook(keyID, "", "receive"); + + return CBitcoinAddress(keyID).ToString(); +} + + +CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) +{ + CPubKey pubKey; + if (!pwalletMain->GetAccountPubkey(pubKey, strAccount, bForceNew)) { + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + } + + return CBitcoinAddress(pubKey.GetID()); +} + +UniValue getaccountaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "getaccountaddress \"account\"\n" + "\nDEPRECATED. Returns the current IPChain address for receiving payments to this account.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The account name for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n" + "\nResult:\n" + "\"address\" (string) The account IPChain address\n" + "\nExamples:\n" + + HelpExampleCli("getaccountaddress", "") + + HelpExampleCli("getaccountaddress", "\"\"") + + HelpExampleCli("getaccountaddress", "\"myaccount\"") + + HelpExampleRpc("getaccountaddress", "\"myaccount\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(request.params[0]); + + UniValue ret(UniValue::VSTR); + + ret = GetAccountAddress(strAccount).ToString(); + return ret; +} + + +UniValue getrawchangeaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1) + throw runtime_error( + "getrawchangeaddress\n" + "\nReturns a new Bitcoin address, for receiving change.\n" + "This is for use with raw transactions, NOT normal use.\n" + "\nResult:\n" + "\"address\" (string) The address\n" + "\nExamples:\n" + + HelpExampleCli("getrawchangeaddress", "") + + HelpExampleRpc("getrawchangeaddress", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + CReserveKey reservekey(pwalletMain); + CPubKey vchPubKey; + if (!reservekey.GetReservedKey(vchPubKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + reservekey.KeepKey(); + + CKeyID keyID = vchPubKey.GetID(); + + return CBitcoinAddress(keyID).ToString(); +} + + +UniValue setaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "setaccount \"address\" \"account\"\n" + "\nDEPRECATED. Sets the account associated with the given address.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to be associated with an account.\n" + "2. \"account\" (string, required) The account to assign the address to.\n" + "\nExamples:\n" + + HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + string strAccount; + if (request.params.size() > 1) + strAccount = AccountFromValue(request.params[1]); + + // Only add the account if the address is yours. + if (IsMine(*pwalletMain, address.Get())) + { + // Detect when changing the account of an address that is the 'unused current key' of another account: + if (pwalletMain->mapAddressBook.count(address.Get())) + { + string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name; + if (address == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + pwalletMain->SetAddressBook(address.Get(), strAccount, "receive"); + } + else + throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); + + return NullUniValue; +} + + +UniValue getaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "getaccount \"address\"\n" + "\nDEPRECATED. Returns the account associated with the given address.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address for account lookup.\n" + "\nResult:\n" + "\"accountname\" (string) the account address\n" + "\nExamples:\n" + + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + string strAccount; + map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) + strAccount = (*mi).second.name; + return strAccount; +} + + +UniValue getaddressesbyaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \"account\"\n" + "\nDEPRECATED. Returns the list of addresses for the given account.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The account name.\n" + "\nResult:\n" + "[ (json array of string)\n" + " \"address\" (string) a bitcoin address associated with the given account\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") + + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount = AccountFromValue(request.params[0]); + + // Find all addresses that have the given account + UniValue ret(UniValue::VARR); + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strName = item.second.name; + if (strName == strAccount) + ret.push_back(address.ToString()); + } + return ret; +} + +UniValue setTesttx(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (b_TestTxLarge) + b_TestTxLarge = false; + else + b_TestTxLarge = true; + + return b_TestTxLarge; +} +static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; + vecSend.push_back(recipient); + if (!pwalletMain->CreateNormalTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + std::cout << "error transaction id=" << wtxNew.GetHash().GetHex() << "\n"; + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + +UniValue sendtoaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) + throw runtime_error( + "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" + "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "4. \"comment_to\" (string, optional) A comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the \n" + " transaction, just kept in your wallet.\n" + "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" + " The recipient will receive less bitcoins than you enter in the amount field.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = AmountFromValue(request.params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + + // Wallet comments + CWalletTx wtx; + if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) + wtx.mapValue["comment"] = request.params[2].get_str(); + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["to"] = request.params[3].get_str(); + + bool fSubtractFeeFromAmount = false; + if (request.params.size() > 4) + fSubtractFeeFromAmount = request.params[4].get_bool(); + + EnsureWalletIsUnlocked(); + + SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//add by xxy +//General trading +static void NormalSendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateNormalTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + { + std::cout << "nFeeRequired" << nFeeRequired << std::endl; + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + } + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue NormalSendToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) + throw runtime_error( + "NormalSendToAddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" + "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "4. \"comment_to\" (string, optional) A comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the \n" + " transaction, just kept in your wallet.\n" + "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" + " The recipient will receive less bitcoins than you enter in the amount field.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("NormalSendToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = AmountFromValue(request.params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + + // Wallet comments + CWalletTx wtx; + if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) + wtx.mapValue["comment"] = request.params[2].get_str(); + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["to"] = request.params[3].get_str(); + + bool fSubtractFeeFromAmount = false; + if (request.params.size() > 4) + fSubtractFeeFromAmount = request.params[4].get_bool(); + + EnsureWalletIsUnlocked(); + + NormalSendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//IPC Reg Tx +static void IPCRegSendMoney(std::string strReglabel,const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateIPCRegTransaction(strReglabel, vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCRegisterToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "IPCRegisterToAddress \"address\" \"icplabel\" (amount \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"icplabel\" (string) ipclabel...\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("IPCRegisterToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = 0; + CWalletTx wtx; + std::string strReglabel = ""; + if (request.params.size() > 1 && !request.params[1].isNull() && !request.params[1].get_str().empty()) + strReglabel = request.params[1].get_str(); + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + IPCRegSendMoney(strReglabel, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//IPCSend Transaction +static void IPCSdSendMoney(std::string& txid,int Index,const CTxDestination &address,CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + + if (!pwalletMain->CreateIPCSendTransaction(txid,Index,vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCSendToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 3) + throw runtime_error( + "IPCSendToAddress \"txid\" \"index\" \"address\" ( subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"txid\" (string) The transaction id of putin.\n" + "2. \"index\" (string)The id of preVout.\n" + "3. \"address\" (string, required) The bitcoin address to send to.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCSendToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" ") + + HelpExampleCli("IPCSendToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" " "false") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::string TXid = request.params[0].get_str(); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + std::string iIndex = request.params[1].get_str(); + int Index = std::atoi(iIndex.c_str()); + if (Index < 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid prevout index"); + CBitcoinAddress address(request.params[2].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = 0; + // Wallet comments + CWalletTx wtx; + bool fSubtractFeeFromAmount = false; + + + EnsureWalletIsUnlocked(); + + IPCSdSendMoney(TXid, Index, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} +//IPC Authorize Transaction +static void IPCAuthorSendMoney(std::string& txid, int Index, const CTxDestination &address,std::string& strLabel, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateIPCAuthorizationTransaction(txid, Index, vecSend, strLabel, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCAuthorToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 4 || request.params.size() > 4) + throw runtime_error( + "IPCAuthorToAddress \"txid\" \"index\" \"address\" \"ipclabel\"( subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1.\"txid\" (string) The transaction id of putin.\n" + "2. \"index\" (string)The id of preVout.\n" + "3. \"address\" (string, required) The bitcoin address to send to.\n" + "4. \"ipclabel\" (string).The ipclabel of IPCAuthorToAddress.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCAuthorToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" " "\"...(ipclabel)\"") + + HelpExampleCli("IPCAuthorToAddress", "txid" "index" "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" " "\"...(ipclabel)\"" "false") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::string TXid = request.params[0].get_str(); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + std::string iIndex = request.params[1].get_str(); + int Index = std::atoi(iIndex.c_str()); + if (Index < 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid prevout index"); + CBitcoinAddress address(request.params[2].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + std::string strIPClabel = request.params[3].get_str(); + // Amount + CAmount nAmount = 0; + // Wallet comments + CWalletTx wtx; + bool fSubtractFeeFromAmount = false; + + + EnsureWalletIsUnlocked(); + + IPCAuthorSendMoney(TXid, Index, address.Get(), strIPClabel,nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +static void JoinCampaignCommand(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + //CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired = 0; + std::string strError; + int nChangePosRet = -1; + if (!pwalletMain->JoinCampaign(address, nValue, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitJoinCampaignTransaction(address, wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} +UniValue decodeinfobyaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "decodeinfobyaddress \"address\"\n" + "\ndecode info by a address .\n" + "\nArguments:\n" + "1. \"address\" (string) The wallet address\n" + "\nExamples:\n" + "\ndecode the address\n" + + HelpExampleCli("decodeinfobyaddress", "\"address\"") + ); + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid IPChain address"); + CKeyID keyid; + if (!address.GetKeyID(keyid)) + throw JSONRPCError(RPC_WALLET_ERROR, "Can't get CKeyID of the address"); + uint160 pkhash = keyid; + CKey vchPrivKey; + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Please enter the wallet passphrase with walletpassphrase first."); + if (!pwalletMain->GetKey(keyid, vchPrivKey)) + throw JSONRPCError(RPC_WALLET_ERROR, "Can't get CKey of the address"); + CPubKey pubkey = vchPrivKey.GetPubKey(); + assert(vchPrivKey.VerifyPubKey(pubkey)); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("pkhash160", pkhash.GetHex())); + result.push_back(Pair("privatekey", CBitcoinSecret(vchPrivKey).ToString())); + return result; +} +UniValue getcurdeposit(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 0) + throw runtime_error( + "getcurdeposit \"\" \n" + "\nget the current deposit.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "\nResult:\n" + "\"amount\" (string) The deposit amount.\n" + "\nExamples:\n" + + HelpExampleCli("getcurdeposit","") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CAmount deposit = pwalletMain->GetDeposit(); + + return ValueFromAmount(deposit); +} + + +UniValue joincampaign(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() !=2) + throw runtime_error( + "joincampaign \"address\" \"amount\" \n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("joincampaign", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 50") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = AmountFromValue(request.params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + + // Wallet comments + CWalletTx wtx; + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + JoinCampaignCommand(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +static void ExitCampaignCommand(bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + CAmount nValue = 0; + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + int nChangePosRet = -1; + if (!pwalletMain->ExitCampaign(wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + +static void PunishRequestCommand(const CTxDestination &address, const std::string evidence, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + //CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired = 0; + std::string strError; + int nChangePosRet = -1; + //std::cout << "PunishRequestCommand:evidence=" << evidence << std::endl; + if (!pwalletMain->PunishRequest(address, evidence, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + +UniValue punishrequest(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 2) + throw runtime_error( + "punishrequest \"address\" \n" + "\nSend an punish request to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to punish to.\n" + "2. \"evidence\" (string, required) the evidence.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("punishrequest", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"{badblockpubtexthex:Reconstructs the bad block signature plaintext hex string,badblocksign:The signature field of the bad block}\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + const std::string evidence = request.params[1].get_str(); + if (evidence.empty()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Evidence"); + + std::cout << "punishrequest:evidence=" << evidence << std::endl; + + // Wallet comments + CWalletTx wtx; + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + PunishRequestCommand(address.Get(), evidence, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +UniValue exitcampaign(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "exitcampaign \n" + "\exitcampaign from the address.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("exitcampaign" ," ") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Wallet comments + CWalletTx wtx; + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + ExitCampaignCommand(fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +UniValue isdepositabled(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "isdepositabled \"txid\" \"index\" \n" + "\n Isadled or not of the deposit.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"txid\" (string) The transaction id of putin.\n" + "2. \"index\" (string)The id of preVout.\n" + "\nResult:\n" + "\"bool\" (bool) able disable.\n" + "\nExamples:\n" + + HelpExampleCli("isdepositabled", "txid" "index" ) + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Wallet comments + std::string TXid = request.params[0].get_str(); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + std::string iIndex = request.params[1].get_str(); + int Index = std::atoi(iIndex.c_str()); + if (Index < 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid prevout index"); + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + if (tx->vout[Index].txType != TXOUT_CAMPAIGN || tx->vout[Index].devoteLabel.ExtendType != TYPE_CONSENSUS_REGISTER) + throw JSONRPCError(RPC_INVALID_PARAMETER, "This is not a deposit"); + bool isAbled = CConsensusAccountPool::Instance().IsAviableUTXO(tx->GetHash()); + UniValue results(UniValue::VARR); + results.push_back(isAbled); + + return results; +} + +UniValue listreward(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "listreward \n" + "\nlistreward .\n" + "\nResult:\n" + "\"\" (string) the list.\n" + "\nExamples:\n" + + HelpExampleCli("listreward", " ") + ); + UniValue results(UniValue::VARR); + vector rewardtimelist; + vector rewardvaluelist; + + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + { + pwalletMain->GetCurrentRewards(rewardtimelist, rewardvaluelist); + } + int listcount = rewardvaluelist.size(); + for (int i = 0; i < listcount; i++) + { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("Time", rewardtimelist.at(i))); + entry.push_back(Pair("Reward", rewardvaluelist.at(i))); + results.push_back(entry); + } + + return results; +} + +//Tokens Reg Transaction +static void IPCTokenRegSendMoney(std::string strReglabel, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + // Check amount + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + if (!pwalletMain->CreateTokenRegTransaction(strReglabel, vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + + +UniValue IPCTokenRegToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "IPCTokenRegToAddress \"address\" \"icplabel\" ( \"comment\" \"comment_to\" subtractfeefromamount )\n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to send to.\n" + "2. \"tokenlabel\" (string) ipclabel...\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("IPCTokenRegToAddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + // Amount + CAmount nAmount = 0; + CWalletTx wtx; + std::string strReglabel = ""; + if (request.params.size() > 1 && !request.params[1].isNull() && !request.params[1].get_str().empty()) + strReglabel = request.params[1].get_str(); + + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + IPCTokenRegSendMoney(strReglabel, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +//Tokens trading +static void IPCTokenSendMoney(std::string& tokensymbol, uint64_t value, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) +{ + CAmount curBalance = pwalletMain->GetBalance(); + + if (nValue < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse Bitcoin address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = { scriptPubKey, nValue, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + + if (!pwalletMain->CreateTokenTransaction(tokensymbol, value, vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { + if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } +} + + +UniValue IPCTokenSendToAddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 3) + throw runtime_error( + "IPCTokenSendToAddress \"tokensymbol\" \"address\" \n" + "\nSend an amount to a given address.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"tokensymbol\" (string) The tokensymbol of what you want to send.\n" + "2. \"address\" (string, required) The bitcoin address to send to.\n" + "3. \"value\" (int)The value of Tokentransaction.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("IPCTokenSendToAddress", "tokensymbol \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1 ") + + HelpExampleCli("IPCTokenSendToAddress", "tokensymbol \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1 ") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::string tokensymbol = request.params[0].get_str(); + + CBitcoinAddress address(request.params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + int nacc = (int)tokenDataMap[tokensymbol].accuracy; + int64_t TokenValue = TCoinsFromValue(request.params[2], nacc); + if (TokenValue <= 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Tokentransaction value"); + // Amount + CAmount nAmount = 0; + uint64_t nTokenCount = TokenValue; + // Wallet comments + CWalletTx wtx; + bool fSubtractFeeFromAmount = false; + + EnsureWalletIsUnlocked(); + + IPCTokenSendMoney(tokensymbol, nTokenCount, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + + return wtx.GetHash().GetHex(); +} +//end + + + +UniValue listaddressgroupings(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp) + throw runtime_error( + "listaddressgroupings\n" + "\nLists groups of addresses which have had their common ownership\n" + "made public by common use as inputs or as the resulting change\n" + "in past transactions\n" + "\nResult:\n" + "[\n" + " [\n" + " [\n" + " \"address\", (string) The bitcoin address\n" + " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" + " \"account\" (string, optional) DEPRECATED. The account\n" + " ]\n" + " ,...\n" + " ]\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("listaddressgroupings", "") + + HelpExampleRpc("listaddressgroupings", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + UniValue jsonGroupings(UniValue::VARR); + map balances = pwalletMain->GetAddressBalances(); + BOOST_FOREACH(set grouping, pwalletMain->GetAddressGroupings()) + { + UniValue jsonGrouping(UniValue::VARR); + BOOST_FOREACH(CTxDestination address, grouping) + { + UniValue addressInfo(UniValue::VARR); + addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(ValueFromAmount(balances[address])); + { + if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) + addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); + } + jsonGrouping.push_back(addressInfo); + } + jsonGroupings.push_back(jsonGrouping); + } + return jsonGroupings; +} + +UniValue signmessage(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 2) + throw runtime_error( + "signmessage \"address\" \"message\"\n" + "\nSign a message with the private key of an address" + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to use for the private key.\n" + "2. \"message\" (string, required) The message to create a signature of.\n" + "\nResult:\n" + "\"signature\" (string) The signature of the message encoded in base 64\n" + "\nExamples:\n" + "\nUnlock the wallet for 30 seconds\n" + + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + + "\nCreate the signature\n" + + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + + "\nVerify the signature\n" + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + + "\nAs json rpc\n" + + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + string strAddress = request.params[0].get_str(); + string strMessage = request.params[1].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + vector vchSig; + if (!key.SignCompact(ss.GetHash(), vchSig)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); + + return EncodeBase64(&vchSig[0], vchSig.size()); +} + +//Debug the interface to generate the package person's signature in Coinbase +UniValue dpocsign(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "dpocsign \"hexdata\"\n" + "\nSign a hexdata with the private key of current dpoc account" + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"hexdata\" (string, required) The hexstring of data to create a signature of.\n" + "\nResult:\n" + "\"signature\" (string) The signature of the message with the format in coinbase\n" + "\nExamples:\n" + "\nTo Do\n" + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + string hexdata = request.params[0].get_str(); + if (!IsHex(hexdata)) + { + return false; + } + + std::string pubkeyhex; + if (!CDpocInfo::Instance().GetLocalAccount(pubkeyhex)) + return false; + + uint160 devoterhash; + devoterhash.SetHex(pubkeyhex); + + CKeyID publickeyID(devoterhash); + CPubKey vchPubKeyOut; + if (pwalletMain->GetPubKey(publickeyID, vchPubKeyOut)) { + //LogPrintf("[addRulersToCoinbase] public keyhash=%s", vchPubKeyOut.GetHash().ToString().c_str()); + } + else { + //LogPrintf("[addSignToCoinBase] don't find public key "); + return false; + } + + CKey vchPrivKeyOut; + if (pwalletMain->GetKey(publickeyID, vchPrivKeyOut)) { + } + else { + //LogPrintf("[addSignToCoinBase] don't find private key"); + return false; + } + + uint256 hash; + std::vector data = ParseHex(hexdata); + CHash256 hashoperator; + for (std::vector::iterator it = data.begin(); it != data.end(); it++) + { + hashoperator.Write((unsigned char*)&(*it), sizeof(unsigned char)); + } + hashoperator.Finalize(hash.begin()); + + std::vector vchSig; + vchPrivKeyOut.Sign(hash, vchSig); + + // Generate signature cache + std::vector vchSigSend; + vchSigSend.resize(2 + vchPubKeyOut.size() + vchSig.size()); + + //Cached signature + unsigned char vchSigLen = (unsigned char)vchSig.size(); + vchSigSend[0] = vchSigLen; + for (std::vector::size_type ix = 0; ix < vchSig.size(); ++ix) { + vchSigSend[ix + 1] = vchSig[ix]; + } + + //The cache of public key + vchSigSend[vchSigLen + 1] = (unsigned char)vchPubKeyOut.size(); + for (std::vector::size_type ix = 0; ix < vchPubKeyOut.size(); ++ix) { + vchSigSend[ix + 2 + vchSigLen] = vchPubKeyOut[ix]; + } + + // std::cout << "vchSig.size=" << vchSig.size() << " vchPubKeyOut.size=" << vchPubKeyOut.size() << " vchSigSend.size=" << vchSigSend.size() << std::endl; + std::string strSin2Publickey = EncodeBase58(vchSigSend); + + return strSin2Publickey; +} + +UniValue getreceivedbyaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "getreceivedbyaddress \"address\" ( minconf )\n" + "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address for transactions.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" + "\nExamples:\n" + "\nThe amount from transactions with at least 1 confirmation\n" + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + + "\nThe amount including unconfirmed transactions, zero confirmations\n" + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + + "\nThe amount with at least 6 confirmation, very safe\n" + + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + + "\nAs a json rpc call\n" + + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Bitcoin address + CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CScript scriptPubKey = GetScriptForDestination(address.Get()); + if (!IsMine(*pwalletMain, scriptPubKey)) + return ValueFromAmount(0); + + // Minimum confirmations + int nMinDepth = 1; + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); + + // Tally + CAmount nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + + return ValueFromAmount(nAmount); +} + + +UniValue getreceivedbyaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "getreceivedbyaccount \"account\" ( minconf )\n" + "\nDEPRECATED. Returns the total amount received by addresses with in transactions with at least [minconf] confirmations.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The selected account, may be the default account using \"\".\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" + "\nExamples:\n" + "\nAmount received by the default account with at least 1 confirmation\n" + + HelpExampleCli("getreceivedbyaccount", "\"\"") + + "\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n" + + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") + + "\nThe amount with at least 6 confirmation, very safe\n" + + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") + + "\nAs a json rpc call\n" + + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Minimum confirmations + int nMinDepth = 1; + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); + + // Get the set of pub keys assigned to account + string strAccount = AccountFromValue(request.params[0]); + set setAddress = pwalletMain->GetAccountAddresses(strAccount); + + // Tally + CAmount nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) + { + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return ValueFromAmount(nAmount); +} + + +UniValue getbalance(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 3) + throw runtime_error( + "getbalance ( \"account\" minconf include_watchonly )\n" + "\nIf account is not specified, returns the server's total available balance.\n" + "If account is specified (DEPRECATED), returns the balance in the account.\n" + "Note that the account \"\" is not the same as leaving the parameter out.\n" + "The server total may be different to the balance in the default \"\" account.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) DEPRECATED. The account string may be given as a\n" + " specific account name to find the balance associated with wallet keys in\n" + " a named account, or as the empty string (\"\") to find the balance\n" + " associated with wallet keys not in any named account, or as \"*\" to find\n" + " the balance associated with all wallet keys regardless of account.\n" + " When this option is specified, it calculates the balance in a different\n" + " way than when it is not specified, and which can count spends twice when\n" + " there are conflicting pending transactions (such as those created by\n" + " the bumpfee command), temporarily resulting in low or even negative\n" + " balances. In general, account balance calculation is not considered\n" + " reliable and has resulted in confusing outcomes, so it is recommended to\n" + " avoid passing this argument.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" + "\nExamples:\n" + "\nThe total amount in the wallet\n" + + HelpExampleCli("getbalance", "") + + "\nThe total amount in the wallet at least 5 blocks confirmed\n" + + HelpExampleCli("getbalance", "\"*\" 6") + + "\nAs a json rpc call\n" + + HelpExampleRpc("getbalance", "\"*\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.params.size() == 0) + return ValueFromAmount(pwalletMain->GetBalance()); + + int nMinDepth = 1; + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if(request.params.size() > 2) + if(request.params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + if (request.params[0].get_str() == "*") { + // Calculate total balance in a very different way from GetBalance(). + // The biggest difference is that GetBalance() sums up all unspent + // TxOuts paying to the wallet, while this sums up both spent and + // unspent TxOuts paying to the wallet, and then subtracts the values of + // TxIns spending from the wallet. This also has fewer restrictions on + // which unconfirmed transactions are considered trusted. + CAmount nBalance = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + continue; + + CAmount allFee; + string strSentAccount; + list listReceived; + list listSent; + wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const COutputEntry& r, listReceived) + nBalance += r.amount; + } + BOOST_FOREACH(const COutputEntry& s, listSent) + nBalance -= s.amount; + nBalance -= allFee; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(request.params[0]); + + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, filter); + + return ValueFromAmount(nBalance); +} + +UniValue getunconfirmedbalance(const JSONRPCRequest &request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 0) + throw runtime_error( + "getunconfirmedbalance\n" + "Returns the server's total unconfirmed balance\n"); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); +} + + +UniValue movecmd(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 5) + throw runtime_error( + "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" + "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) The name of the account to move funds from. May be the default account using \"\".\n" + "2. \"toaccount\" (string, required) The name of the account to move funds to. May be the default account using \"\".\n" + "3. amount (numeric) Quantity of " + CURRENCY_UNIT + " to move between accounts.\n" + "4. (dummy) (numeric, optional) Ignored. Remains for backward compatibility.\n" + "5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" + "\nResult:\n" + "true|false (boolean) true if successful.\n" + "\nExamples:\n" + "\nMove 0.01 " + CURRENCY_UNIT + " from the default account to the account named tabby\n" + + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + + "\nMove 0.01 " + CURRENCY_UNIT + " timotei to akiko with a comment and funds have 6 confirmations\n" + + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strFrom = AccountFromValue(request.params[0]); + string strTo = AccountFromValue(request.params[1]); + CAmount nAmount = AmountFromValue(request.params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + if (request.params.size() > 3) + // unused parameter, used to be nMinDepth, keep type-checking it though + (void)request.params[3].get_int(); + string strComment; + if (request.params.size() > 4) + strComment = request.params[4].get_str(); + + if (!pwalletMain->AccountMove(strFrom, strTo, nAmount, strComment)) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + return true; +} + + +UniValue sendfrom(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 6) + throw runtime_error( + "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf \"comment\" \"comment_to\" )\n" + "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a bitcoin address." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n" + " Specifying an account does not influence coin selection, but it does associate the newly created\n" + " transaction with the account, so the account's balance computation and transaction history can reflect\n" + " the spend.\n" + "2. \"toaddress\" (string, required) The ipchain address to send funds to.\n" + "3. amount (numeric or string, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n" + "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" + "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "6. \"comment_to\" (string, optional) An optional comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the transaction, \n" + " it is just kept in your wallet.\n" + "\nResult:\n" + "\"txid\" (string) The transaction id.\n" + "\nExamples:\n" + "\nSend 0.01 " + CURRENCY_UNIT + " from the default account to the address, must have at least 1 confirmation\n" + + HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + + "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" + + HelpExampleCli("sendfrom", "\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount = AccountFromValue(request.params[0]); + CBitcoinAddress address(request.params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CAmount nAmount = AmountFromValue(request.params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + int nMinDepth = 1; + if (request.params.size() > 3) + nMinDepth = request.params[3].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty()) + wtx.mapValue["comment"] = request.params[4].get_str(); + if (request.params.size() > 5 && !request.params[5].isNull() && !request.params[5].get_str().empty()) + wtx.mapValue["to"] = request.params[5].get_str(); + + EnsureWalletIsUnlocked(); + + // Check funds + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + if (nAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + SendMoney(address.Get(), nAmount, false, wtx); + + return wtx.GetHash().GetHex(); +} + + +UniValue sendmany(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) + throw runtime_error( + "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" + "\nSend multiple times. Amounts are double-precision floating point numbers." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) DEPRECATED. The account to send the funds from. Should be \"\" for the default account\n" + "2. \"amounts\" (string, required) A json object with addresses and amounts\n" + " {\n" + " \"address\":amount (numeric or string) The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" + " ,...\n" + " }\n" + "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" + "4. \"comment\" (string, optional) A comment\n" + "5. subtractfeefrom (array, optional) A json array with addresses.\n" + " The fee will be equally deducted from the amount of each selected address.\n" + " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" + " If no addresses are specified here, the sender pays the fee.\n" + " [\n" + " \"address\" (string) Subtract fee from this address\n" + " ,...\n" + " ]\n" + "\nResult:\n" + "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + + "\nSend two amounts to two different addresses, subtract fee from amount:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + string strAccount = AccountFromValue(request.params[0]); + UniValue sendTo = request.params[1].get_obj(); + int nMinDepth = 1; + if (request.params.size() > 2) + nMinDepth = request.params[2].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["comment"] = request.params[3].get_str(); + + UniValue subtractFeeFromAmount(UniValue::VARR); + if (request.params.size() > 4) + subtractFeeFromAmount = request.params[4].get_array(); + + set setAddress; + vector vecSend; + + CAmount totalAmount = 0; + vector keys = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, keys) + { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(sendTo[name_]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + totalAmount += nAmount; + + bool fSubtractFeeFromAmount = false; + for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) { + const UniValue& addr = subtractFeeFromAmount[idx]; + if (addr.get_str() == name_) + fSubtractFeeFromAmount = true; + } + + CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; + vecSend.push_back(recipient); + } + + EnsureWalletIsUnlocked(); + + // Check funds + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + CAmount nFeeRequired = 0; + int nChangePosRet = -1; + string strFailReason; + bool fCreated = pwalletMain->CreateNormalTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + CValidationState state; + if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), state)) { + strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); + } + + return wtx.GetHash().GetHex(); +} + +UniValue sendtokenmany(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 3) + throw runtime_error( + "sendtokenmany \"fromaccount\" \"tokensymbol\" {\"address\":amount,...}\n" + "\nSend multiple times. Amounts are double-precision floating point numbers." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) DEPRECATED. The account to send the funds from. Should be \"\" for the default account\n" + "2. \"tokensymbol\" (string, required) Tokensymbol which you want to send\n" + "3. \"amounts\" (string, required) A json object with addresses and amounts\n" + " {\n" + " \"address\":amount (numeric or string) The ipchain address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" + " ,...\n" + " }\n" + "\nResult:\n" + "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli("sendtokenmany", "\"\" \"tokensymbol\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + string strAccount = AccountFromValue(request.params[0]); + string tokensymbol = request.params[1].get_str(); + if (tokenDataMap.count(tokensymbol) == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Can't found 'accuracy' of the Token"); + + int tokenaccuracy = tokenDataMap[tokensymbol].accuracy; + std::cout << "tokenaccuracy = " << tokenaccuracy << std::endl; + UniValue sendTo = request.params[2].get_obj(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + + set setAddress; + vector vecSend; + vecSend.clear(); + uint64_t totalAmount = 0; + vector keys = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, keys) + { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid IPchain address: ") + name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + int64_t nAmount = TCoinsFromValue(sendTo[name_], tokenaccuracy); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid token amount for send"); + totalAmount += nAmount; + + bool fSubtractFeeFromAmount = false; + CAmount nipcAmount = 0; + uint64_t ntokenamount = nAmount; + CRecipientToken recipient = { scriptPubKey, nipcAmount, ntokenamount, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + } + + EnsureWalletIsUnlocked(); + + // Check funds + uint64_t nBalance = 0; + pwalletMain->GetSymbolbalance(tokensymbol, nBalance); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + std::cout << "totalAmount =" << totalAmount << std::endl; + + // Send + CReserveKey keyChange(pwalletMain); + CAmount nFeeRequired = 0; + int nChangePosRet = -1; + string strFailReason; + bool fCreated = pwalletMain->CreateTokenTransactionM(tokensymbol,vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); + std::cout << "33333333 " << std::endl; + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + CValidationState state; + if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), state)) { + strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); + } + + return wtx.GetHash().GetHex(); +} +// Defined in rpc/misc.cpp +extern CScript _createmultisig_redeemScript(const UniValue& params); + +UniValue addmultisigaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) + { + string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" + "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" + "Each key is a Bitcoin address or hex-encoded public key.\n" + "If 'account' is specified (DEPRECATED), assign address to that account.\n" + + "\nArguments:\n" + "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" + "2. \"keys\" (string, required) A json array of bitcoin addresses or hex-encoded public keys\n" + " [\n" + " \"address\" (string) bitcoin address or hex-encoded public key\n" + " ...,\n" + " ]\n" + "3. \"account\" (string, optional) DEPRECATED. An account to assign the addresses to.\n" + + "\nResult:\n" + "\"address\" (string) A bitcoin address associated with the keys.\n" + + "\nExamples:\n" + "\nAdd a multisig address from 2 addresses\n" + + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + "\nAs json rpc call\n" + + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + ; + throw runtime_error(msg); + } + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount; + if (request.params.size() > 2) + strAccount = AccountFromValue(request.params[2]); + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig_redeemScript(request.params); + CScriptID innerID(inner); + pwalletMain->AddCScript(inner); + + pwalletMain->SetAddressBook(innerID, strAccount, "send"); + return CBitcoinAddress(innerID).ToString(); +} + +class Witnessifier : public boost::static_visitor +{ +public: + CScriptID result; + + bool operator()(const CNoDestination &dest) const { return false; } + + bool operator()(const CKeyID &keyID) { + CPubKey pubkey; + if (pwalletMain) { + CScript basescript = GetScriptForDestination(keyID); + isminetype typ; + typ = IsMine(*pwalletMain, basescript, SIGVERSION_WITNESS_V0); + if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE) + return false; + CScript witscript = GetScriptForWitness(basescript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } + + bool operator()(const CScriptID &scriptID) { + CScript subscript; + if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { + int witnessversion; + std::vector witprog; + if (subscript.IsWitnessProgram(witnessversion, witprog)) { + result = scriptID; + return true; + } + isminetype typ; + typ = IsMine(*pwalletMain, subscript, SIGVERSION_WITNESS_V0); + if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE) + return false; + CScript witscript = GetScriptForWitness(subscript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } +}; + +UniValue addwitnessaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + { + string msg = "addwitnessaddress \"address\"\n" + "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" + "It returns the witness script.\n" + + "\nArguments:\n" + "1. \"address\" (string, required) An address known to the wallet\n" + + "\nResult:\n" + "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "}\n" + ; + throw runtime_error(msg); + } + + { + LOCK(cs_main); + if (!IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()) && !GetBoolArg("-walletprematurewitness", false)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Segregated witness not enabled on network"); + } + } + + CBitcoinAddress address(request.params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + Witnessifier w; + CTxDestination dest = address.Get(); + bool ret = boost::apply_visitor(w, dest); + if (!ret) { + throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); + } + + pwalletMain->SetAddressBook(w.result, "", "receive"); + + return CBitcoinAddress(w.result).ToString(); +} + +struct tallyitem +{ + CAmount nAmount; + int nConf; + vector txids; + bool fIsWatchonly; + tallyitem() + { + nAmount = 0; + nConf = std::numeric_limits::max(); + fIsWatchonly = false; + } +}; + +UniValue ListReceived(const UniValue& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + isminefilter filter = ISMINE_SPENDABLE; + if(params.size() > 2) + if(params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + // Tally + map mapTally; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) + { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + continue; + + isminefilter mine = IsMine(*pwalletMain, address); + if(!(mine & filter)) + continue; + + tallyitem& item = mapTally[address]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + item.txids.push_back(wtx.GetHash()); + if (mine & ISMINE_WATCH_ONLY) + item.fIsWatchonly = true; + } + } + + // Reply + UniValue ret(UniValue::VARR); + map mapAccountTally; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strAccount = item.second.name; + map::iterator it = mapTally.find(address); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + CAmount nAmount = 0; + int nConf = std::numeric_limits::max(); + bool fIsWatchonly = false; + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + fIsWatchonly = (*it).second.fIsWatchonly; + } + + if (fByAccounts) + { + tallyitem& _item = mapAccountTally[strAccount]; + _item.nAmount += nAmount; + _item.nConf = min(_item.nConf, nConf); + _item.fIsWatchonly = fIsWatchonly; + } + else + { + UniValue obj(UniValue::VOBJ); + if(fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); + obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + if (!fByAccounts) + obj.push_back(Pair("label", strAccount)); + UniValue transactions(UniValue::VARR); + if (it != mapTally.end()) + { + BOOST_FOREACH(const uint256& _item, (*it).second.txids) + { + transactions.push_back(_item.GetHex()); + } + } + obj.push_back(Pair("txids", transactions)); + ret.push_back(obj); + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + CAmount nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + UniValue obj(UniValue::VOBJ); + if((*it).second.fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + ret.push_back(obj); + } + } + + return ret; +} + +UniValue listreceivedbyaddress(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 3) + throw runtime_error( + "listreceivedbyaddress ( minconf include_empty include_watchonly)\n" + "\nList balances by receiving address.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" + "2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" + " \"address\" : \"receivingaddress\", (string) The receiving address\n" + " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" + " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" + " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"txids\": [\n" + " n, (numeric) The ids of transactions received with the address \n" + " ...\n" + " ]\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listreceivedbyaddress", "") + + HelpExampleCli("listreceivedbyaddress", "6 true") + + HelpExampleRpc("listreceivedbyaddress", "6, true, true") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + return ListReceived(request.params, false); +} + +UniValue listreceivedbyaccount(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 3) + throw runtime_error( + "listreceivedbyaccount ( minconf include_empty include_watchonly)\n" + "\nDEPRECATED. List balances by account.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" + "2. include_empty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" + " \"account\" : \"accountname\", (string) The account name of the receiving account\n" + " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" + " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" + " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listreceivedbyaccount", "") + + HelpExampleCli("listreceivedbyaccount", "6 true") + + HelpExampleRpc("listreceivedbyaccount", "6, true, true") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + return ListReceived(request.params, true); +} + +static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) +{ + CBitcoinAddress addr; + if (addr.Set(dest)) + entry.push_back(Pair("address", addr.ToString())); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) +{ + CAmount nFee; + string strSentAccount; + list listReceived; + list listSent; + + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); + + bool fAllAccounts = (strAccount == string("*")); + bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const COutputEntry& s, listSent) + { + UniValue entry(UniValue::VOBJ); + if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + entry.push_back(Pair("account", strSentAccount)); + MaybePushAddress(entry, s.destination); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); + if (pwalletMain->mapAddressBook.count(s.destination)) + entry.push_back(Pair("label", pwalletMain->mapAddressBook[s.destination].name)); + entry.push_back(Pair("vout", s.vout)); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + entry.push_back(Pair("abandoned", wtx.isAbandoned())); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const COutputEntry& r, listReceived) + { + string account; + if (pwalletMain->mapAddressBook.count(r.destination)) + account = pwalletMain->mapAddressBook[r.destination].name; + if (fAllAccounts || (account == strAccount)) + { + UniValue entry(UniValue::VOBJ); + if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + entry.push_back(Pair("account", account)); + MaybePushAddress(entry, r.destination); + if (wtx.IsCoinBase()) + { + if (wtx.GetDepthInMainChain() < 1) + entry.push_back(Pair("category", "orphan")); + else if (wtx.GetBlocksToMaturity() > 0) + entry.push_back(Pair("category", "immature")); + else + entry.push_back(Pair("category", "generate")); + } + else + { + entry.push_back(Pair("category", "receive")); + } + entry.push_back(Pair("amount", ValueFromAmount(r.amount))); + if (pwalletMain->mapAddressBook.count(r.destination)) + entry.push_back(Pair("label", account)); + entry.push_back(Pair("vout", r.vout)); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) + { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +UniValue listtransactions(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listtransactions ( \"account\" count skip include_watchonly)\n" + "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" + "2. count (numeric, optional, default=10) The number of transactions to return\n" + "3. skip (numeric, optional, default=0) The number of transactions to skip\n" + "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "[\n" + " {\n" + " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" + " It will be \"\" for the default account.\n" + " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for \n" + " move transactions (category = move).\n" + " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" + " transaction between accounts, and not associated with an address,\n" + " transaction id or block. 'send' and 'receive' transactions are \n" + " associated with an address, transaction id and block details\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" + " 'move' category for moves outbound. It is positive for the 'receive' category,\n" + " and for the 'move' category for inbound funds.\n" + " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" + " \"vout\": n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" + " 'receive' category of transactions. Negative confirmations indicate the\n" + " transaction conflicts with the block chain\n" + " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" + " category of transactions.\n" + " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive'\n" + " category of transactions.\n" + " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" + " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" + " for 'send' and 'receive' category of transactions.\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " \"otheraccount\": \"accountname\", (string) DEPRECATED. For the 'move' category of transactions, the account the funds came \n" + " from (for receiving funds, positive amounts), or went to (for sending funds,\n" + " negative amounts).\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" + " }\n" + "]\n" + + "\nExamples:\n" + "\nList the most recent 10 transactions in the systems\n" + + HelpExampleCli("listtransactions", "") + + "\nList transactions 100 to 120\n" + + HelpExampleCli("listtransactions", "\"*\" 20 100") + + "\nAs a json rpc call\n" + + HelpExampleRpc("listtransactions", "\"*\", 20, 100") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strAccount = "*"; + if (request.params.size() > 0) + strAccount = request.params[0].get_str(); + int nCount = 10; + if (request.params.size() > 1) + nCount = request.params[1].get_int(); + int nFrom = 0; + if (request.params.size() > 2) + nFrom = request.params[2].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if(request.params.size() > 3) + if(request.params[3].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + if (nCount < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); + if (nFrom < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); + + UniValue ret(UniValue::VARR); + + const CWallet::TxItems & txOrdered = pwalletMain->wtxOrdered; + + // iterate backwards until we have nCount items to return: + for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret, filter); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if ((int)ret.size() >= (nCount+nFrom)) break; + } + // ret is newest to oldest + + if (nFrom > (int)ret.size()) + nFrom = ret.size(); + if ((nFrom + nCount) > (int)ret.size()) + nCount = ret.size() - nFrom; + + vector arrTmp = ret.getValues(); + + vector::iterator first = arrTmp.begin(); + std::advance(first, nFrom); + vector::iterator last = arrTmp.begin(); + std::advance(last, nFrom+nCount); + + if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); + if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); + + std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest + + ret.clear(); + ret.setArray(); + ret.push_backV(arrTmp); + + return ret; +} + +UniValue listaccounts(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 2) + throw runtime_error( + "listaccounts ( minconf include_watchonly)\n" + "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" + "2. include_watchonly (bool, optional, default=false) Include balances in watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "{ (json object where keys are account names, and values are numeric balances\n" + " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" + " ...\n" + "}\n" + "\nExamples:\n" + "\nList account balances where there at least 1 confirmation\n" + + HelpExampleCli("listaccounts", "") + + "\nList account balances including zero confirmation transactions\n" + + HelpExampleCli("listaccounts", "0") + + "\nList account balances for 6 or more confirmations\n" + + HelpExampleCli("listaccounts", "6") + + "\nAs json rpc call\n" + + HelpExampleRpc("listaccounts", "6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + int nMinDepth = 1; + if (request.params.size() > 0) + nMinDepth = request.params[0].get_int(); + isminefilter includeWatchonly = ISMINE_SPENDABLE; + if(request.params.size() > 1) + if(request.params[1].get_bool()) + includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; + + map mapAccountBalances; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { + if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me + mapAccountBalances[entry.second.name] = 0; + } + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + CAmount nFee; + string strSentAccount; + list listReceived; + list listSent; + int nDepth = wtx.GetDepthInMainChain(); + if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) + continue; + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const COutputEntry& s, listSent) + mapAccountBalances[strSentAccount] -= s.amount; + if (nDepth >= nMinDepth) + { + BOOST_FOREACH(const COutputEntry& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.destination)) + mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; + else + mapAccountBalances[""] += r.amount; + } + } + + const list & acentries = pwalletMain->laccentries; + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + UniValue ret(UniValue::VOBJ); + BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +UniValue listsinceblock(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp) + throw runtime_error( + "listsinceblock ( \"blockhash\" target_confirmations include_watchonly)\n" + "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" + "\nArguments:\n" + "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" + "2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" + "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')" + "\nResult:\n" + "{\n" + " \"transactions\": [\n" + " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" + " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" + " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" + " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" + " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" + " When it's < 0, it means the transaction conflicted that many blocks ago.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" + " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive' category of transactions.\n" + " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" + " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" + " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" + " ],\n" + " \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("listsinceblock", "") + + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") + + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + const CBlockIndex *pindex = NULL; + int target_confirms = 1; + isminefilter filter = ISMINE_SPENDABLE; + + if (request.params.size() > 0) + { + uint256 blockId; + + blockId.SetHex(request.params[0].get_str()); + BlockMap::iterator it = mapBlockIndex.find(blockId); + if (it != mapBlockIndex.end()) + { + pindex = it->second; + if (chainActive[pindex->nHeight] != pindex) + { + // the block being asked for is a part of a deactivated chain; + // we don't want to depend on its perceived height in the block + // chain, we want to instead use the last common ancestor + pindex = chainActive.FindFork(pindex); + } + } + } + + if (request.params.size() > 1) + { + target_confirms = request.params[1].get_int(); + + if (target_confirms < 1) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + } + + if (request.params.size() > 2 && request.params[2].get_bool()) + { + filter = filter | ISMINE_WATCH_ONLY; + } + + int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; + + UniValue transactions(UniValue::VARR); + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) + { + CWalletTx tx = (*it).second; + + if (depth == -1 || tx.GetDepthInMainChain() < depth) + ListTransactions(tx, "*", 0, true, transactions, filter); + } + + CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; + uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("transactions", transactions)); + ret.push_back(Pair("lastblock", lastblock.GetHex())); + + return ret; +} + +UniValue gettransaction(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "gettransaction \"txid\" ( include_watchonly )\n" + "\nGet detailed information about in-wallet transaction \n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]\n" + "\nResult:\n" + "{\n" + " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"blockhash\" : \"hash\", (string) The block hash\n" + " \"blockindex\" : xx, (numeric) The index of the transaction in the block that includes it\n" + " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"txid\" : \"transactionid\", (string) The transaction id.\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"details\" : [\n" + " {\n" + " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" + " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" + " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" + " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" + " }\n" + " ,...\n" + " ],\n" + " \"hex\" : \"data\" (string) Raw data for transaction\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") + + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + uint256 hash; + hash.SetHex(request.params[0].get_str()); + + isminefilter filter = ISMINE_SPENDABLE; + if (request.params.size() > 1) + if (request.params[1].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + UniValue entry(UniValue::VOBJ); + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + CAmount nCredit = wtx.GetCredit(filter); + CAmount nDebit = wtx.GetDebit(filter); + CAmount nNet = nCredit - nDebit; + CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe(filter)) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(wtx, entry); + + UniValue details(UniValue::VARR); + ListTransactions(wtx, "*", 0, false, details, filter); + entry.push_back(Pair("details", details)); + + string strHex = EncodeHexTx(static_cast(wtx), RPCSerializationFlags()); + entry.push_back(Pair("hex", strHex)); + + return entry; +} + + +UniValue abandontransaction(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "abandontransaction \"txid\"\n" + "\nMark in-wallet transaction as abandoned\n" + "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n" + "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n" + "It only works on transactions which are not included in a block and are not currently in the mempool.\n" + "It has no effect on transactions which are already conflicted or abandoned.\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + uint256 hash; + hash.SetHex(request.params[0].get_str()); + + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + if (!pwalletMain->AbandonTransaction(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment"); + + return NullUniValue; +} + + +UniValue backupwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "backupwallet \"destination\"\n" + "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n" + "\nArguments:\n" + "1. \"destination\" (string) The destination directory or file\n" + "\nExamples:\n" + + HelpExampleCli("backupwallet", "\"backup.dat\"") + + HelpExampleRpc("backupwallet", "\"backup.dat\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + string strDest = request.params[0].get_str(); + if (!pwalletMain->BackupWallet(strDest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); + + return NullUniValue; +} + + +UniValue keypoolrefill(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1) + throw runtime_error( + "keypoolrefill ( newsize )\n" + "\nFills the keypool." + + HelpRequiringPassphrase() + "\n" + "\nArguments\n" + "1. newsize (numeric, optional, default=100) The new keypool size\n" + "\nExamples:\n" + + HelpExampleCli("keypoolrefill", "") + + HelpExampleRpc("keypoolrefill", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool + unsigned int kpSize = 0; + if (request.params.size() > 0) { + if (request.params[0].get_int() < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); + kpSize = (unsigned int)request.params[0].get_int(); + } + + EnsureWalletIsUnlocked(); + pwalletMain->TopUpKeyPool(kpSize); + + if (pwalletMain->GetKeyPoolSize() < kpSize) + throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); + + return NullUniValue; +} + + +static void LockWallet(CWallet* pWallet) +{ + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = 0; + pWallet->Lock(); +} + +UniValue walletpassphrase(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2)) + throw runtime_error( + "walletpassphrase \"passphrase\" timeout\n" + "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" + "This is needed prior to performing transactions related to private keys such as sending bitcoins\n" + "\nArguments:\n" + "1. \"passphrase\" (string, required) The wallet passphrase\n" + "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" + "\nNote:\n" + "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" + "time that overrides the old one.\n" + "\nExamples:\n" + "\nunlock the wallet for 60 seconds\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + + "\nLock the wallet again (before 60 seconds)\n" + + HelpExampleCli("walletlock", "") + + "\nAs json rpc call\n" + + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + + // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed + SecureString strWalletPass; + strWalletPass.reserve(100); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + strWalletPass = request.params[0].get_str().c_str(); + + if (strWalletPass.length() > 0) + { + if (!pwalletMain->Unlock(strWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + } + else + throw runtime_error( + "walletpassphrase \n" + "Stores the wallet decryption key in memory for seconds."); + + pwalletMain->TopUpKeyPool(); + + int64_t nSleepTime = request.params[1].get_int64(); + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = GetTime() + nSleepTime; + RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); + + return NullUniValue; +} + + +UniValue walletpassphrasechange(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2)) + throw runtime_error( + "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" + "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" + "\nArguments:\n" + "1. \"oldpassphrase\" (string) The current passphrase\n" + "2. \"newpassphrase\" (string) The new passphrase\n" + "\nExamples:\n" + + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + + // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + SecureString strOldWalletPass; + strOldWalletPass.reserve(100); + strOldWalletPass = request.params[0].get_str().c_str(); + + SecureString strNewWalletPass; + strNewWalletPass.reserve(100); + strNewWalletPass = request.params[1].get_str().c_str(); + + if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + + return NullUniValue; +} + + +UniValue walletlock(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 0)) + throw runtime_error( + "walletlock\n" + "\nRemoves the wallet encryption key from memory, locking the wallet.\n" + "After calling this method, you will need to call walletpassphrase again\n" + "before being able to call any methods which require the wallet to be unlocked.\n" + "\nExamples:\n" + "\nSet the passphrase for 2 minutes to perform a transaction\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + + "\nPerform a send (requires passphrase set)\n" + + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") + + "\nClear the passphrase since we are done before 2 minutes is up\n" + + HelpExampleCli("walletlock", "") + + "\nAs json rpc call\n" + + HelpExampleRpc("walletlock", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); + + { + LOCK(cs_nWalletUnlockTime); + pwalletMain->Lock(); + nWalletUnlockTime = 0; + } + + return NullUniValue; +} + + +UniValue encryptwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (!pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 1)) + throw runtime_error( + "encryptwallet \"passphrase\"\n" + "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" + "After this, any calls that interact with private keys such as sending or signing \n" + "will require the passphrase to be set prior the making these calls.\n" + "Use the walletpassphrase call for this, and then walletlock call.\n" + "If the wallet is already encrypted, use the walletpassphrasechange call.\n" + "Note that this will shutdown the server.\n" + "\nArguments:\n" + "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n" + "\nExamples:\n" + "\nEncrypt you wallet\n" + + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + + "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + + "\nNow we can so something like sign\n" + + HelpExampleCli("signmessage", "\"address\" \"test message\"") + + "\nNow lock the wallet again by removing the passphrase\n" + + HelpExampleCli("walletlock", "") + + "\nAs a json rpc call\n" + + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.fHelp) + return true; + if (pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); + + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + SecureString strWalletPass; + strWalletPass.reserve(100); + strWalletPass = request.params[0].get_str().c_str(); + + if (strWalletPass.length() < 1) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + + if (!pwalletMain->EncryptWallet(strWalletPass)) + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); + + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + StartShutdown(); + return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; +} + +UniValue lockunspent(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n" + "\nUpdates list of temporarily unspendable outputs.\n" + "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" + "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n" + "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n" + "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" + "is always cleared (by virtue of process exit) when a node stops or fails.\n" + "Also see the listunspent call\n" + "\nArguments:\n" + "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n" + "2. \"transactions\" (string, optional) A json array of objects. Each object the txid (string) vout (numeric)\n" + " [ (json array of json objects)\n" + " {\n" + " \"txid\":\"id\", (string) The transaction id\n" + " \"vout\": n (numeric) The output number\n" + " }\n" + " ,...\n" + " ]\n" + + "\nResult:\n" + "true|false (boolean) Whether the command was successful or not\n" + + "\nExamples:\n" + "\nList the unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nLock an unspent transaction\n" + + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nList the locked transactions\n" + + HelpExampleCli("listlockunspent", "") + + "\nUnlock the transaction again\n" + + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (request.params.size() == 1) + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL)); + else + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); + + bool fUnlock = request.params[0].get_bool(); + + if (request.params.size() == 1) { + if (fUnlock) + pwalletMain->UnlockAllCoins(); + return true; + } + + UniValue outputs = request.params[1].get_array(); + for (unsigned int idx = 0; idx < outputs.size(); idx++) { + const UniValue& output = outputs[idx]; + if (!output.isObject()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); + const UniValue& o = output.get_obj(); + + RPCTypeCheckObj(o, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + }); + + string txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + + int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + COutPoint outpt(uint256S(txid), nOutput); + + if (fUnlock) + pwalletMain->UnlockCoin(outpt); + else + pwalletMain->LockCoin(outpt); + } + + return true; +} + +UniValue listlockunspent(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 0) + throw runtime_error( + "listlockunspent\n" + "\nReturns list of temporarily unspendable outputs.\n" + "See the lockunspent call to lock and unlock transactions for spending.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"txid\" : \"transactionid\", (string) The transaction id locked\n" + " \"vout\" : n (numeric) The vout value\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + "\nList the unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nLock an unspent transaction\n" + + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nList the locked transactions\n" + + HelpExampleCli("listlockunspent", "") + + "\nUnlock the transaction again\n" + + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("listlockunspent", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + vector vOutpts; + pwalletMain->ListLockedCoins(vOutpts); + + UniValue ret(UniValue::VARR); + + BOOST_FOREACH(COutPoint &outpt, vOutpts) { + UniValue o(UniValue::VOBJ); + + o.push_back(Pair("txid", outpt.hash.GetHex())); + o.push_back(Pair("vout", (int)outpt.n)); + ret.push_back(o); + } + + return ret; +} + +UniValue settxfee(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + throw runtime_error( + "settxfee amount\n" + "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n" + "\nArguments:\n" + "1. amount (numeric or string, required) The transaction fee in " + CURRENCY_UNIT + "/kB\n" + "\nResult\n" + "true|false (boolean) Returns true if successful\n" + "\nExamples:\n" + + HelpExampleCli("settxfee", "0.00001") + + HelpExampleRpc("settxfee", "0.00001") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Amount + CAmount nAmount = AmountFromValue(request.params[0]); + + payTxFee = CFeeRate(nAmount, 1000); + return true; +} + +UniValue getwalletinfo(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "getwalletinfo\n" + "Returns an object containing various wallet state info.\n" + "\nResult:\n" + "{\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" + " \"hdmasterkeyid\": \"\" (string) the Hash160 of the HD master pubkey\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getwalletinfo", "") + + HelpExampleRpc("getwalletinfo", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); + obj.push_back(Pair("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance()))); + obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size())); + obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + if (pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); + CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID; + if (!masterKeyID.IsNull()) + obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex())); + return obj; +} + +UniValue resendwallettransactions(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 0) + throw runtime_error( + "resendwallettransactions\n" + "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" + "Intended only for testing; the wallet code periodically re-broadcasts\n" + "automatically.\n" + "Returns array of transaction ids that were re-broadcast.\n" + ); + + if (!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::vector txids = pwalletMain->ResendWalletTransactionsBefore(GetTime(), g_connman.get()); + UniValue result(UniValue::VARR); + BOOST_FOREACH(const uint256& txid, txids) + { + result.push_back(txid.ToString()); + } + return result; +} + +UniValue listunspent(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspent", "") + + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableNormalCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + CTxDestination address; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} + +//add by xxy +UniValue listunspentNormal(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspentNormal ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspentNormal", "") + + HelpExampleCli("listunspentNormal", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspentNormal", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableNormalCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + CTxDestination address; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} + +UniValue listunspentIPC(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspentIPC ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspentIPC", "") + + HelpExampleCli("listunspentIPC", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspentIPC", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableIPCCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + if (out.nDepthtx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + entry.push_back(make_pair("UTXOType", out.GetType())); + entry.push_back(make_pair("GetIPCExtendType", out.GetIPCExtendType())); + entry.push_back(make_pair("GetIPCStartTime", out.GetIPCStartTime())); + entry.push_back(make_pair("GetIPCStopTime", out.GetIPCStopTime())); + entry.push_back(make_pair("GetIPCHash", out.GetIPCHash())); + entry.push_back(make_pair("GetIPCTitle", out.GetIPCTitle())); + entry.push_back(make_pair("GetIPCLabel", out.GetIPCLabel())); + entry.push_back(make_pair("CanBeSentToOhter", out.CanBeSentToOhter())); + entry.push_back(make_pair("CanBeAuthorizedToOther", out.CanBeAuthorizedToOther())); + entry.push_back(make_pair("CanBeUniqueAuthorizedToOther", out.CanBeUniqueAuthorizedToOther())); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} +UniValue listtokencoins(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 1 || request.params.size() < 1) + throw runtime_error( + "listtokencoins ( [\"tokensymbol\",...] )\n" + "\nReturns the value of tokensymbol balance \n" + "\nArguments:\n" + "1. tokensymbol (string) The tokensymbol\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"symbol\" : \"symbol\", (string) the TokenSymbol id \n" + " \"balance\" : n, (numeric) the value of tokensymbol\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listtokencoins", "tokensymbol") + ); + std::string tokensymbol = request.params[0].get_str(); + uint64_t balance = 0; + bool getbalanceYet = pwalletMain->GetSymbolbalance(tokensymbol, balance); + if (!getbalanceYet) + throw JSONRPCError(RPC_DATABASE_ERROR, string("Can't find the balance of the tokensymbol!")); + UniValue results(UniValue::VOBJ); + results.push_back(Pair("Tokensymbol", tokensymbol)); + results.push_back(Pair("balance", ValueFromTCoins(balance, (int)tokenDataMap[tokensymbol].accuracy))); + + return results; +} + +UniValue listunspentToken(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() > 4) + throw runtime_error( + "listunspentToken ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " because they come from unconfirmed untrusted transactions or unconfirmed\n" + " replacement transactions (cases where we are less sure that a conflicting\n" + " transaction won't be mined).\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspentToken", "") + + HelpExampleCli("listunspentToken", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspentToken", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + int nMinDepth = 1; + if (request.params.size() > 0 && !request.params[0].isNull()) { + RPCTypeCheckArgument(request.params[0], UniValue::VNUM); + nMinDepth = request.params[0].get_int(); + } + + int nMaxDepth = 9999999; + if (request.params.size() > 1 && !request.params[1].isNull()) { + RPCTypeCheckArgument(request.params[1], UniValue::VNUM); + nMaxDepth = request.params[1].get_int(); + } + + set setAddress; + if (request.params.size() > 2 && !request.params[2].isNull()) { + RPCTypeCheckArgument(request.params[2], UniValue::VARR); + UniValue inputs = request.params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + input.get_str()); + setAddress.insert(address); + } + } + + bool include_unsafe = true; + if (request.params.size() > 3 && !request.params[3].isNull()) { + RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); + include_unsafe = request.params[3].get_bool(); + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableTokenCoins(vecOutputs, !include_unsafe, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + if (out.nDepth < nTxConfirmTarget) + continue; + CTxDestination address; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + entry.push_back(Pair("TokenSymbol", out.GetTokenSymbol())); + entry.push_back(Pair("tokenvalue", ValueFromTCoins(out.GetTokenvalue(), (int)out.GetTokenAccuracy()))); + entry.push_back(Pair("TokenAccuracy", out.GetTokenAccuracy())); + + if (fValidAddress) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); + results.push_back(entry); + } + + return results; +} +//end +UniValue fundrawtransaction(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "fundrawtransaction \"hexstring\" ( options )\n" + "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" + "This will not modify existing inputs, and will add at most one change output to the outputs.\n" + "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n" + "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" + "The inputs added will not be signed, use signrawtransaction for that.\n" + "Note that all existing inputs must have their previous output transaction be in the wallet.\n" + "Note that all inputs selected must be of standard form and P2SH scripts must be\n" + "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n" + "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n" + "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "2. options (object, optional)\n" + " {\n" + " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n" + " \"changePosition\" (numeric, optional, default random) The index of the change output\n" + " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" + " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" + " \"reserveChangeKey\" (boolean, optional, default true) Reserves the change output key from the keypool\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n" + " The fee will be equally deducted from the amount of each specified output.\n" + " The outputs are specified by their zero-based index, before any change output is added.\n" + " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" + " If no outputs are specified here, the sender pays the fee.\n" + " [vout_index,...]\n" + " }\n" + " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" + "\nResult:\n" + "{\n" + " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" + " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" + " \"changepos\": n (numeric) The position of the added change output, or -1\n" + "}\n" + "\nExamples:\n" + "\nCreate a transaction with no inputs\n" + + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + + "\nAdd sufficient unsigned inputs to meet the output value\n" + + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") + + "\nSign the transaction\n" + + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + + "\nSend the transaction\n" + + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") + ); + + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + + CTxDestination changeAddress = CNoDestination(); + int changePosition = -1; + bool includeWatching = false; + bool lockUnspents = false; + bool reserveChangeKey = true; + CFeeRate feeRate = CFeeRate(0); + bool overrideEstimatedFeerate = false; + UniValue subtractFeeFromOutputs; + set setSubtractFeeFromOutputs; + + if (request.params.size() > 1) { + if (request.params[1].type() == UniValue::VBOOL) { + // backward compatibility bool only fallback + includeWatching = request.params[1].get_bool(); + } + else { + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); + + UniValue options = request.params[1]; + + RPCTypeCheckObj(options, + { + {"changeAddress", UniValueType(UniValue::VSTR)}, + {"changePosition", UniValueType(UniValue::VNUM)}, + {"includeWatching", UniValueType(UniValue::VBOOL)}, + {"lockUnspents", UniValueType(UniValue::VBOOL)}, + {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, + {"feeRate", UniValueType()}, // will be checked below + {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, + }, + true, true); + + if (options.exists("changeAddress")) { + CBitcoinAddress address(options["changeAddress"].get_str()); + + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "changeAddress must be a valid bitcoin address"); + + changeAddress = address.Get(); + } + + if (options.exists("changePosition")) + changePosition = options["changePosition"].get_int(); + + if (options.exists("includeWatching")) + includeWatching = options["includeWatching"].get_bool(); + + if (options.exists("lockUnspents")) + lockUnspents = options["lockUnspents"].get_bool(); + + if (options.exists("reserveChangeKey")) + reserveChangeKey = options["reserveChangeKey"].get_bool(); + + if (options.exists("feeRate")) + { + feeRate = CFeeRate(AmountFromValue(options["feeRate"])); + overrideEstimatedFeerate = true; + } + + if (options.exists("subtractFeeFromOutputs")) + subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array(); + } + } + + // parse hex string from parameter + CMutableTransaction tx; + if (!DecodeHexTx(tx, request.params[0].get_str(), true)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + if (tx.vout.size() == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); + + if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds"); + + for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) { + int pos = subtractFeeFromOutputs[idx].get_int(); + if (setSubtractFeeFromOutputs.count(pos)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos)); + if (pos < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos)); + if (pos >= int(tx.vout.size())) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos)); + setSubtractFeeFromOutputs.insert(pos); + } + + CAmount nFeeOut; + string strFailReason; + + if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, reserveChangeKey, changeAddress)) + throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(tx))); + result.push_back(Pair("changepos", changePosition)); + result.push_back(Pair("fee", ValueFromAmount(nFeeOut))); + + return result; +} + +// Calculate the size of the transaction assuming all signatures are max size +// Use DummySignatureCreator, which inserts 72 byte signatures everywhere. +// TODO: re-use this in CWallet::CreateTransaction (right now +// CreateTransaction uses the constructed dummy-signed tx to do a priority +// calculation, but we should be able to refactor after priority is removed). +// NOTE: this requires that all inputs must be in mapWallet (eg the tx should +// be IsAllFromMe). +int64_t CalculateMaximumSignedTxSize(const CTransaction &tx) +{ + CMutableTransaction txNew(tx); + std::vector> vCoins; + // Look up the inputs. We should have already checked that this transaction + // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our + // wallet, with a valid index into the vout array. + for (auto& input : tx.vin) { + const auto mi = pwalletMain->mapWallet.find(input.prevout.hash); + assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); + vCoins.emplace_back(make_pair(&(mi->second), input.prevout.n)); + } + if (!pwalletMain->DummySignTx(txNew, vCoins)) { + // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) + // implies that we can sign for every input. + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed"); + } + return GetVirtualTransactionSize(txNew); +} + +UniValue bumpfee(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { + throw runtime_error( + "bumpfee \"txid\" ( options ) \n" + "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n" + "An opt-in RBF transaction with the given txid must be in the wallet.\n" + "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n" + "If the change output is not big enough to cover the increased fee, the command will currently fail\n" + "instead of adding new inputs to compensate. (A future implementation could improve this.)\n" + "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n" + "By default, the new fee will be calculated automatically using estimatefee.\n" + "The user can specify a confirmation target for estimatefee.\n" + "Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate.\n" + "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n" + "returned by getnetworkinfo) to enter the node's mempool.\n" + "\nArguments:\n" + "1. txid (string, required) The txid to be bumped\n" + "2. options (object, optional)\n" + " {\n" + " \"confTarget\" (numeric, optional) Confirmation target (in blocks)\n" + " \"totalFee\" (numeric, optional) Total fee (NOT feerate) to pay, in satoshis.\n" + " In rare cases, the actual fee paid might be slightly higher than the specified\n" + " totalFee if the tx change output has to be removed because it is too close to\n" + " the dust threshold.\n" + " \"replaceable\" (boolean, optional, default true) Whether the new transaction should still be\n" + " marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n" + " be left unchanged from the original. If false, any input sequence numbers in the\n" + " original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n" + " so the new transaction will not be explicitly bip-125 replaceable (though it may\n" + " still be replacable in practice, for example if it has unconfirmed ancestors which\n" + " are replaceable).\n" + " }\n" + "\nResult:\n" + "{\n" + " \"txid\": \"value\", (string) The id of the new transaction\n" + " \"origfee\": n, (numeric) Fee of the replaced transaction\n" + " \"fee\": n, (numeric) Fee of the new transaction\n" + " \"errors\": [ str... ] (json array of strings) Errors encountered during processing (may be empty)\n" + "}\n" + "\nExamples:\n" + "\nBump the fee, get the new transaction\'s txid\n" + + HelpExampleCli("bumpfee", "")); + } + + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); + uint256 hash; + hash.SetHex(request.params[0].get_str()); + + // retrieve the original tx from the wallet + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + if (!pwalletMain->mapWallet.count(hash)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + } + CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + if (pwalletMain->HasWalletSpend(hash)) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the wallet"); + } + + { + LOCK(mempool.cs); + auto it = mempool.mapTx.find(hash); + if (it != mempool.mapTx.end() && it->GetCountWithDescendants() > 1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the mempool"); + } + } + + if (wtx.GetDepthInMainChain() != 0) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction has been mined, or is conflicted with a mined transaction"); + } + + if (!SignalsOptInRBF(wtx)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction is not BIP 125 replaceable"); + } + + if (wtx.mapValue.count("replaced_by_txid")) { + throw JSONRPCError(RPC_INVALID_REQUEST, strprintf("Cannot bump transaction %s which was already bumped by transaction %s", hash.ToString(), wtx.mapValue.at("replaced_by_txid"))); + } + + // check that original tx consists entirely of our inputs + // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) + if (!pwalletMain->IsAllFromMe(wtx, ISMINE_SPENDABLE)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that don't belong to this wallet"); + } + + // figure out which output was change + // if there was no change output or multiple change outputs, fail + int nOutput = -1; + for (size_t i = 0; i < wtx.tx->vout.size(); ++i) { + if (pwalletMain->IsChange(wtx.tx->vout[i])) { + if (nOutput != -1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has multiple change outputs"); + } + nOutput = i; + } + } + if (nOutput == -1) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output"); + } + + // Calculate the expected size of the new transaction. + int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); + const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx); + + // optional parameters + bool specifiedConfirmTarget = false; + int newConfirmTarget = nTxConfirmTarget; + CAmount totalFee = 0; + bool replaceable = true; + if (request.params.size() > 1) { + UniValue options = request.params[1]; + RPCTypeCheckObj(options, + { + {"confTarget", UniValueType(UniValue::VNUM)}, + {"totalFee", UniValueType(UniValue::VNUM)}, + {"replaceable", UniValueType(UniValue::VBOOL)}, + }, + true, true); + + if (options.exists("confTarget") && options.exists("totalFee")) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction."); + } else if (options.exists("confTarget")) { + specifiedConfirmTarget = true; + newConfirmTarget = options["confTarget"].get_int(); + if (newConfirmTarget <= 0) { // upper-bound will be checked by estimatefee/smartfee + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid confTarget (cannot be <= 0)"); + } + } else if (options.exists("totalFee")) { + totalFee = options["totalFee"].get_int64(); + CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize); + if (totalFee < requiredFee ) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + strprintf("Insufficient totalFee (cannot be less than required fee %s)", + FormatMoney(requiredFee))); + } + } + + if (options.exists("replaceable")) { + replaceable = options["replaceable"].get_bool(); + } + } + + // calculate the old fee and fee-rate + CAmount nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); + CFeeRate nOldFeeRate(nOldFee, txSize); + CAmount nNewFee; + CFeeRate nNewFeeRate; + // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to + // future proof against changes to network wide policy for incremental relay + // fee that our node may not be aware of. + CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE); + if (::incrementalRelayFee > walletIncrementalRelayFee) { + walletIncrementalRelayFee = ::incrementalRelayFee; + } + + if (totalFee > 0) { + CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize); + if (totalFee < minTotalFee) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)", + FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize)))); + } + nNewFee = totalFee; + nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); + } else { + // if user specified a confirm target then don't consider any global payTxFee + if (specifiedConfirmTarget) { + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0)); + } + // otherwise use the regular wallet logic to select payTxFee or default confirm target + else { + nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool); + } + + nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); + + // New fee rate must be at least old rate + minimum incremental relay rate + // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized + // in that unit (fee per kb). + // However, nOldFeeRate is a calculated value from the tx fee/size, so + // add 1 satoshi to the result, because it may have been rounded down. + if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) { + nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()); + nNewFee = nNewFeeRate.GetFee(maxNewTxSize); + } + } + + // Check that in all cases the new fee doesn't violate maxTxFee + if (nNewFee > maxTxFee) { + throw JSONRPCError(RPC_MISC_ERROR, + strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)", + FormatMoney(nNewFee), FormatMoney(maxTxFee))); + } + + // check that fee rate is higher than mempool's minimum fee + // (no point in bumping fee if we know that the new tx won't be accepted to the mempool) + // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps, + // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a + // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment. + CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) { + throw JSONRPCError(RPC_MISC_ERROR, strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK()))); + } + + // Now modify the output to increase the fee. + // If the output is not large enough to pay the fee, fail. + CAmount nDelta = nNewFee - nOldFee; + assert(nDelta > 0); + CMutableTransaction tx(*(wtx.tx)); + CTxOut* poutput = &(tx.vout[nOutput]); + if (poutput->nValue < nDelta) { + throw JSONRPCError(RPC_MISC_ERROR, "Change output is too small to bump the fee"); + } + + // If the output would become dust, discard it (converting the dust to fee) + poutput->nValue -= nDelta; + if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) { + LogPrint("rpc", "Bumping fee and discarding dust output\n"); + nNewFee += poutput->nValue; + tx.vout.erase(tx.vout.begin() + nOutput); + } + + // Mark new tx not replaceable, if requested. + if (!replaceable) { + for (auto& input : tx.vin) { + if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe; + } + } + + // sign the new tx + CTransaction txNewConst(tx); + int nIn = 0; + for (auto& input : tx.vin) { + std::map::const_iterator mi = pwalletMain->mapWallet.find(input.prevout.hash); + assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); + const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; + const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; + SignatureData sigdata; + if (!ProduceSignature(TransactionSignatureCreator(pwalletMain, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); + } + UpdateTransaction(tx, nIn, sigdata); + nIn++; + } + + // commit/broadcast the tx + CReserveKey reservekey(pwalletMain); + CWalletTx wtxBumped(pwalletMain, MakeTransactionRef(std::move(tx))); + wtxBumped.mapValue = wtx.mapValue; + wtxBumped.mapValue["replaces_txid"] = hash.ToString(); + wtxBumped.vOrderForm = wtx.vOrderForm; + wtxBumped.strFromAccount = wtx.strFromAccount; + wtxBumped.fTimeReceivedIsTxTime = true; + wtxBumped.fFromMe = true; + CValidationState state; + if (!pwalletMain->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { + // NOTE: CommitTransaction never returns false, so this should never happen. + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); + } + + UniValue vErrors(UniValue::VARR); + if (state.IsInvalid()) { + // This can happen if the mempool rejected the transaction. Report + // what happened in the "errors" response. + vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); + } + + // mark the original tx as bumped + if (!pwalletMain->MarkReplaced(wtx.GetHash(), wtxBumped.GetHash())) { + // TODO: see if JSON-RPC has a standard way of returning a response + // along with an exception. It would be good to return information about + // wtxBumped to the caller even if marking the original transaction + // replaced does not succeed for some reason. + vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("txid", wtxBumped.GetHash().GetHex())); + result.push_back(Pair("origfee", ValueFromAmount(nOldFee))); + result.push_back(Pair("fee", ValueFromAmount(nNewFee))); + result.push_back(Pair("errors", vErrors)); + + return result; +} + +UniValue loadwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "loadwallet \"filename\"\n" + "\nload a wallet from the wallet file .\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The wallet file\n" + "\nExamples:\n" + "\nload the wallet\n" + + HelpExampleCli("loadwallet", "\"test\"") + + "\nloadwallet the wallet\n" + + HelpExampleCli("loadwallet", "\"test\"") + + "\nloadwallet using the json rpc call\n" + + HelpExampleRpc("loadwallet", "\"test\"") + ); + + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode"); + + CWallet::LoadWalletFromFile(request.params[0].get_str()); + return NullUniValue; +} + +UniValue exportwallet(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "exportwallet \"filename\"\n" + "\nexport a wallet to the file .\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The wallet file\n" + "\nExamples:\n" + "\export the wallet\n" + + HelpExampleRpc("exportwallet", "\"test\"") + ); + + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Exporting wallets is disabled in pruned mode"); + + CWallet::ExportWalletToFile(request.params[0].get_str()); + return NullUniValue; +} + + +extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp +extern UniValue importprivkey(const JSONRPCRequest& request); +extern UniValue importaddress(const JSONRPCRequest& request); +extern UniValue importpubkey(const JSONRPCRequest& request); +extern UniValue dumpwallet(const JSONRPCRequest& request); +extern UniValue importwallet(const JSONRPCRequest& request); +extern UniValue importprunedfunds(const JSONRPCRequest& request); +extern UniValue removeprunedfunds(const JSONRPCRequest& request); +extern UniValue importmulti(const JSONRPCRequest& request); + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} }, + { "hidden", "resendwallettransactions", &resendwallettransactions, true, {} }, + { "wallet", "abandontransaction", &abandontransaction, false, {"txid"} }, + { "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} }, + { "wallet", "backupwallet", &backupwallet, true, {"destination"} }, + { "wallet", "bumpfee", &bumpfee, true, {"txid", "options"} }, + { "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} }, + { "wallet", "dumpwallet", &dumpwallet, true, {"filename"} }, + { "wallet", "encryptwallet", &encryptwallet, true, {"passphrase"} }, + { "wallet", "getaccountaddress", &getaccountaddress, true, {"account"} }, + { "wallet", "getaccount", &getaccount, true, {"address"} }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, {"account"} }, + { "wallet", "getbalance", &getbalance, false, {"account","minconf","include_watchonly"} }, + { "wallet", "getnewaddress", &getnewaddress, true, {"account"} }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf"} }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf"} }, + { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} }, + { "wallet", "getwalletinfo", &getwalletinfo, false, {} }, + { "wallet", "importmulti", &importmulti, true, {"requests","options"} }, + { "wallet", "importprivkey", &importprivkey, true, {"privkey","label","rescan"} }, + { "wallet", "importwallet", &importwallet, true, {"filename"} }, + { "wallet", "importaddress", &importaddress, true, {"address","label","rescan","p2sh"} }, + { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} }, + { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} }, + { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} }, + { "wallet", "listaccounts", &listaccounts, false, {"minconf","include_watchonly"} }, + { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} }, + { "wallet", "listlockunspent", &listlockunspent, false, {} }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, + { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, + { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe"} }, + { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, + + { "wallet", "NormalSendToAddress", &NormalSendToAddress, true, { "address", "amount", "comment", "comment_to", "subtractfeefromamount" } }, + { "wallet", "IPCRegisterToAddress", &IPCRegisterToAddress, false, { "address", "ipclabel" } }, + { "wallet", "IPCSendToAddress", &IPCSendToAddress, false, { "txid", "index", "address" } }, + { "wallet", "IPCAuthorToAddress", &IPCAuthorToAddress, false, { "txid", "index", "address", "ipclabel" } }, + { "wallet", "IPCTokenRegToAddress", &IPCTokenRegToAddress, false, { "address", "tokenlabel" } }, + { "wallet", "IPCTokenSendToAddress", &IPCTokenSendToAddress, false, { "tokensymbol", "address", "value" } }, + { "wallet", "listunspentNormal", &listunspentNormal, false, { "minconf", "maxconf", "addresses", "include_unsafe" } }, + { "wallet", "listunspentIPC", &listunspentIPC, false, { "minconf", "maxconf", "addresses", "include_unsafe" } }, + { "wallet", "listunspentToken", &listunspentToken, false, { "minconf", "maxconf", "addresses", "include_unsafe" } }, + { "wallet", "listtokencoins", &listtokencoins, false, { "tokensymbol"} }, + + { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, + { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, + { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} }, + { "wallet", "sendtokenmany", &sendtokenmany, true, { "fromaccount", "tokensymbol", "amounts" } }, + { "wallet", "sendtoaddress", &sendtoaddress, true, { "address", "amount", "comment", "comment_to", "subtractfeefromamount" } }, + { "wallet", "setaccount", &setaccount, true, {"address","account"} }, + { "wallet", "settxfee", &settxfee, true, {"amount"} }, + { "wallet", "signmessage", &signmessage, true, {"address","message"} }, + { "wallet", "walletlock", &walletlock, true, {} }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, + { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, + { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} }, + { "wallet", "loadwallet", &loadwallet, true, {"filename"} }, + { "wallet", "exportwallet", &exportwallet, true, {"filename"} }, + { "wallet", "decodeinfobyaddress", &decodeinfobyaddress, true, { "address" } }, + + //Add command line operations that exit consensus + { "wallet", "getcurdeposit", &getcurdeposit, true, {} }, + { "wallet", "joincampaign", &joincampaign, true, {"address","deposi"} }, + { "wallet", "exitcampaign", &exitcampaign, true, {} }, + { "wallet", "isdepositabled", &isdepositabled, true, { "txid", "index" } }, + { "wallet", "punishrequest", &punishrequest, true, { "address" } }, + { "wallet", "listreward", &listreward, true, {} }, + + { "wallet", "dpocsign", &dpocsign, true,{ "hexdata" } }, + +}; + +void RegisterWalletRPCCommands(CRPCTable &t) +{ + if (GetBoolArg("-disablewallet", false)) + return; + + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + t.appendCommand(commands[vcidx].name, &commands[vcidx]); +} diff --git a/.svn/pristine/90/905924afaaff267d11788e0f9f7e977eba8b70af.svn-base b/.svn/pristine/90/905924afaaff267d11788e0f9f7e977eba8b70af.svn-base new file mode 100644 index 0000000..db5a00b --- /dev/null +++ b/.svn/pristine/90/905924afaaff267d11788e0f9f7e977eba8b70af.svn-base @@ -0,0 +1,347 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chainparams.h" +#include "consensus/merkle.h" +#include "tinyformat.h" +#include "util.h" +#include "utilstrencodings.h" +#include +#include +#include "chainparamsseeds.h" + + + +static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) +{ + CMutableTransaction txNew; + txNew.nVersion = 1; + txNew.vin.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout.clear(); + CTxOut genesisCoinbase(genesisReward, genesisOutputScript, "75hWHwYVdt265H8UkEdXJdfYofLkVELwSavQc7Nr8ZfxKt1VLSA2vNbSqfCiNL6heYzHqp41F7JT1UjiZhZ8Kp15DdVvKkFFw4yX4XfWHEHy7cYjCDiwpxVdatjRCobGEuWysGXNzoHyNfX61"); + txNew.vout.push_back(std::move(genesisCoinbase)); + + CBlock genesis; + genesis.nTime = nTime; + genesis.nBits = nBits; + genesis.nNonce = nNonce; + genesis.nVersion = nVersion; + genesis.nPeriodCount = 0; + genesis.nPeriodStartTime = 1507824000000; + genesis.nTimePeriod = 0; + genesis.vtx.push_back(MakeTransactionRef(std::move(txNew))); + genesis.hashPrevBlock.SetNull(); + genesis.hashMerkleRoot = BlockMerkleRoot(genesis); + return genesis; +} + +/** + * Build the genesis block. Note that the output of its generation + * transaction cannot be spent since it did not originally exist in the + * database. + * + */ +static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) +{ + const char* pszTimestamp = "2017/10/13 Central bank tests digital currency transactions"; + const CScript genesisOutputScript = CScript() << ParseHex("03f774245f3f0f43e17d84b162d6212f064bb89381fd12cc82b11136d61f594e2a") << OP_CHECKSIG; + return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); +} + +/** + * Main network + */ +/** + * What makes a good checkpoint block? + * + Is surrounded by blocks with reasonable timestamps + * (no blocks before with a timestamp after, none after with + * timestamp before) + * + Contains no strange transactions + */ + +class CMainParams : public CChainParams { +public: + CMainParams() { + strNetworkID = "main"; + consensus.nSubsidyHalvingInterval = 210000; + consensus.BIP34Height = 0; + consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); + consensus.BIP65Height = 0; + consensus.BIP66Height = 0; + consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = false; + consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 1916; + consensus.nMinerConfirmationWindow = 2016; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 + + // Deployment of BIP68, BIP112, and BIP113. + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; + + // Deployment of SegWit (BIP141, BIP143, and BIP147) + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 999999999999ULL; // November 15th, 2016. + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL; // November 15th, 2017. + + // The best chain should have at least this much work. + consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000000000"); + + /** + * The message start string is designed to be unlikely to occur in normal data. + * The characters are rarely used upper ASCII, not valid as UTF-8, and produce + * a large 32-bit integer with any alignment. + */ + //Version 0.6.3Update2 + pchMessageStart[0] = 0xda; + pchMessageStart[1] = 0xd8; + pchMessageStart[2] = 0xd7; + pchMessageStart[3] = 0xdc; + nDefaultPort = 15166; + nPruneAfterHeight = 100000; + genesis = CreateGenesisBlock(1507824000, 1861207187, 0x1d00ffff, 1, 30000000); + consensus.hashGenesisBlock = genesis.GetHash(); + + vSeeds.push_back(CDNSSeedData("vps.ipchainglobal.com", "seed.ipchainglobal.com")); + vSeeds.push_back(CDNSSeedData("vps.qingdoutech.com", "seed.qingdoutech.com")); + + system_account_address = "ZCBSYSTEMZZZZZZZZZZZZZZZZZZZZZVp8TGB"; + MIN_DEPOSI = 10000 * COIN; + CHECK_START_BLOCKCOUNT = 2; + ADJUSTDP_BLOCKS = 2102400; + + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 5); + base58Prefixes[SECRET_KEY] = std::vector(1,128); + base58Prefixes[PUBKEY_ADDRESS] = boost::assign::list_of(0x04)(0x1b)(0x54).convert_to_container >(); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container >(); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container >(); + + vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); + + fMiningRequiresPeers = true; + fDefaultConsistencyChecks = false; + fRequireStandard = true; + fMineBlocksOnDemand = false; + + checkpointData = (CCheckpointData) { + boost::assign::map_list_of + (0, uint256S("00000000092a99c04ceefc5e74dabd905ab1f65166f44bc5f603aa8d1a87ad63")) + }; + + chainTxData = ChainTxData{ + // Data as of block 00000000000000000166d612d5595e2b1cd88d71d695fc580af64d8da8658c23 (height 446482). + 1483472411, // * UNIX timestamp of last known number of transactions + 184495391, // * total number of transactions between genesis and that timestamp + // (the tx=... number in the SetBestChain debug.log lines) + 3.2 // * estimated number of transactions per second after that timestamp + }; + } +}; +static CMainParams mainParams; + +/** + * Testnet (v3) + */ +class CTestNetParams : public CChainParams { +public: + CTestNetParams() { + strNetworkID = "test"; + consensus.nSubsidyHalvingInterval = 210000; + consensus.BIP34Height = 0; + consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); + consensus.BIP65Height = 0; + consensus.BIP66Height = 0; + consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = false; + consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 1512; + consensus.nMinerConfirmationWindow = 2016; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 + + // Deployment of BIP68, BIP112, and BIP113. + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; + + // Deployment of SegWit (BIP141, BIP143, and BIP147) + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL; + + // The best chain should have at least this much work. + consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000000000"); + + //Version + pchMessageStart[0] = 0x2a; + pchMessageStart[1] = 0x28; + pchMessageStart[2] = 0x27; + pchMessageStart[3] = 0x2c; + nDefaultPort = 25166; + nPruneAfterHeight = 1000; + + + genesis = CreateGenesisBlock(1507824000, 1861207187, 0x1d00ffff, 1, 30000000); + consensus.hashGenesisBlock = genesis.GetHash(); + + vFixedSeeds.clear(); + vSeeds.clear(); + + vSeeds.push_back(CDNSSeedData("vpst.ipchainglobal.com", "seedt.ipchainglobal.com")); + vSeeds.push_back(CDNSSeedData("vpst.qingdoutech.com", "seedt.qingdoutech.com")); + + + system_account_address = "TCBSYSTEMZZZZZZZZZZZZZZZZZZZZZUawnx4"; + MIN_DEPOSI = 10000 * COIN; + CHECK_START_BLOCKCOUNT = 2; + ADJUSTDP_BLOCKS = 2102400; + + base58Prefixes[SECRET_KEY] = std::vector(1,239); + base58Prefixes[PUBKEY_ADDRESS] = boost::assign::list_of(0x03)(0x57)(0x62).convert_to_container >(); + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container >(); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container >(); + + vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); + + fMiningRequiresPeers = true; + fDefaultConsistencyChecks = false; + fRequireStandard = false; + fMineBlocksOnDemand = false; + + checkpointData = (CCheckpointData) { + boost::assign::map_list_of + ( 546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")), + }; + + chainTxData = ChainTxData{ + // Data as of block 00000000c2872f8f8a8935c8e3c5862be9038c97d4de2cf37ed496991166928a (height 1063660) + 1483546230, + 12834668, + 0.15 + }; + + } +}; +static CTestNetParams testNetParams; + +/** + * Regression test + */ +class CRegTestParams : public CChainParams { +public: + CRegTestParams() { + strNetworkID = "regtest"; + consensus.nSubsidyHalvingInterval = 150; + consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests) + consensus.BIP34Hash = uint256(); + consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests) + consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in rpc activation tests) + consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains + consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL; + + // The best chain should have at least this much work. + consensus.nMinimumChainWork = uint256S("0x00"); + + // By default assume that the signatures in ancestors of this block are valid. + consensus.defaultAssumeValid = uint256S("0x00"); + + pchMessageStart[0] = 0x1c; + pchMessageStart[1] = 0x2c; + pchMessageStart[2] = 0x3c; + pchMessageStart[3] = 0x4c; + nDefaultPort = 19444; + nPruneAfterHeight = 1000; + + genesis = CreateGenesisBlock(1501818090, 1861207187, 0x1d00ffff, 1, 96000000 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + + vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds. + vSeeds.clear(); //!< Regtest mode doesn't have any DNS seeds. + + fMiningRequiresPeers = false; + fDefaultConsistencyChecks = true; + fRequireStandard = false; + fMineBlocksOnDemand = true; + + checkpointData = (CCheckpointData){ + boost::assign::map_list_of + ( 0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")) + }; + + chainTxData = ChainTxData{ + 0, + 0, + 0 + }; + + base58Prefixes[SECRET_KEY] = std::vector(1, 239); + base58Prefixes[PUBKEY_ADDRESS] = boost::assign::list_of(0x03)(0x57)(0x63).convert_to_container >(); + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 196); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container >(); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container >(); + } + + void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) + { + consensus.vDeployments[d].nStartTime = nStartTime; + consensus.vDeployments[d].nTimeout = nTimeout; + } +}; +static CRegTestParams regTestParams; + +static CChainParams *pCurrentParams = 0; + +const CChainParams &Params() { + assert(pCurrentParams); + return *pCurrentParams; +} + +CChainParams& Params(const std::string& chain) +{ + if (chain == CBaseChainParams::MAIN) + return mainParams; + else if (chain == CBaseChainParams::TESTNET) + return testNetParams; + else if (chain == CBaseChainParams::REGTEST) + return regTestParams; + else + throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); +} + +void SelectParams(const std::string& network) +{ + SelectBaseParams(network); + pCurrentParams = &Params(network); +} + +void UpdateRegtestBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) +{ + regTestParams.UpdateBIP9Parameters(d, nStartTime, nTimeout); +} + diff --git a/.svn/pristine/9b/9bc848f71bc1359d1e16adc3cc029bcc4638f109.svn-base b/.svn/pristine/9b/9bc848f71bc1359d1e16adc3cc029bcc4638f109.svn-base new file mode 100644 index 0000000..ab9c595 --- /dev/null +++ b/.svn/pristine/9b/9bc848f71bc1359d1e16adc3cc029bcc4638f109.svn-base @@ -0,0 +1,43 @@ +#ifndef STATEINFO +#define STATEINFO +#include +#include +#include +using namespace std; +#include +#include +#include +#include + + +class stateinfo { +public: + stateinfo(); + ~stateinfo(); + + void Init(); + void write(const char* str); + void clear(); + QString read(); + void SetDataPath(std::string path){m_datapath=path+"/";} +private: + void CreateLogFile(); + std::string GetDataPath(){return m_datapath;} + std::string m_datapath; + // QFile* m_file; + QString m_file; + int m_day; +}; + + + +void STATE_WRITE(const char* str1); +void STATE_SETPATH(std::string); +QString STATE_READ(); +void STATE_CLEAR(); + + + + +#endif // STATEINFO + diff --git a/.svn/pristine/9f/9f4fe9010fa89745f03102f90e0c3898c1bcec3c.svn-base b/.svn/pristine/9f/9f4fe9010fa89745f03102f90e0c3898c1bcec3c.svn-base new file mode 100644 index 0000000..8901d74 --- /dev/null +++ b/.svn/pristine/9f/9f4fe9010fa89745f03102f90e0c3898c1bcec3c.svn-base @@ -0,0 +1,287 @@ +#include "ecoinsendaffrimdialog.h" +#include "forms/ui_ecoinsendaffrimdialog.h" +#include "addresstablemodel.h" +#include "optionsmodel.h" +#include "ipchainunits.h" +#include "wallet/coincontrol.h" +#include "guiutil.h" +#include +#include "wallet/wallet.h" +#include +#include "log/log.h" +#include +#include +#include + +ecoinsendaffrimdialog::ecoinsendaffrimdialog(QWidget *parent) : + QWidget(parent), + ui(new Ui::ecoinsendaffrimdialog) +{ + ui->setupUi(this); +} +void ecoinsendaffrimdialog::showEvent(QShowEvent * p) +{ + ui->addEdit->setText(""); + ui->numEdit->setText(""); + ui->tiplabel->setText(""); + ui->addEdit->setFocusPolicy(Qt::StrongFocus); + ui->addEdit->setFocus(Qt::OtherFocusReason); + ui->numEdit->clearFocus(); +} +ecoinsendaffrimdialog::~ecoinsendaffrimdialog() +{ + delete ui; +} +void ecoinsendaffrimdialog::setModel(WalletModel *_model) +{ + this->model = _model; +} +void ecoinsendaffrimdialog::setMsg(QString name,QString num) +{ + + m_name = name; + m_num = num; + std::string strname = name.toStdString(); + int acc = model->GetAccuracyBySymbol(strname); + QString exp("1000000000000000000|([0-9]{0,19}[\.][0-9]{0,"); + exp+=(QString::number(acc)); + exp = exp + ("})"); + QRegExp double_rx10000(exp); + QValidator *validator = new QRegExpValidator(double_rx10000, ui->numEdit ); + ui->numEdit->setValidator( validator ); + ui->tokenlabel->setText(name); + ui->addEdit->setText(""); + ui->numEdit->setText(""); + ui->tiplabel->setText(""); + ui->numEdit->clearFocus(); + +} + +void ecoinsendaffrimdialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) +{ + QPalette pa; + pa.setColor(QPalette::WindowText,Qt::black); + + LOG_WRITE(LOG_INFO,"processSendCoinsReturn","sendCoinsReturn.status",\ + QString::number(sendCoinsReturn.status).toStdString().c_str(),\ + "m_error",m_error.c_str()); + + switch(sendCoinsReturn.status) + { + case WalletModel::InvalidAddress: + ui->tiplabel->setText(tr("The recipient address is not valid. Please recheck.")); + break; + case WalletModel::InvalidAmount: + ui->tiplabel->setText(tr("The amount to pay must be larger than 0.")); + break; + case WalletModel::AmountExceedsBalance: + ui->tiplabel->setText(tr("The amount exceeds your balance.")); + break; + case WalletModel::AmountWithFeeExceedsBalance: + ui->tiplabel->setText(tr("The total exceeds your balance.")); + break; + case WalletModel::DuplicateAddress: + ui->tiplabel->setText(tr("Duplicate address found: addresses should only be used once each.")); + break; + case WalletModel::TransactionCreationFailed: + if(m_error == "The tokenvalue is too big,you have not enough tokencoins.") + ui->tiplabel->setText(tr("The tokenvalue is too big,you have not enough tokencoins.")); + else if(m_error == "The Tokenvalue is too big,you have not enough Tokencoins.") + ui->tiplabel->setText(tr("The Tokenvalue is too big,you have not enough Tokencoins.")); + else + ui->tiplabel->setText(tr("Transaction creation failed!") + " " + m_error.c_str()); + break; + case WalletModel::TransactionCommitFailed: + if(m_error == "Token-reg-starttime-is-up-yet") + ui->tiplabel->setText(tr("Token-reg-starttime-is-up-yet")); + else + ui->tiplabel->setText(tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed)); + break; + case WalletModel::AbsurdFee: + ui->tiplabel->setText(tr("A fee higher")); + break; + case WalletModel::PaymentRequestExpired: + ui->tiplabel->setText(tr("Payment request expired.")); + break; + case WalletModel::OK: + default: + return; + } +} + + +void ecoinsendaffrimdialog::on_sendecoinButton_pressed() +{ + m_error = ""; + if(!model || !model->getOptionsModel()) + return; + if("" == ui->addEdit->text() || + "" == ui->numEdit->text()) + { + ui->tiplabel->setText(tr("input info")); + return; + } + QString num = ui->numEdit->text(); + QString pointword = "."; + if(num.indexOf(pointword) == num.size()-1){ + ui->tiplabel->setText(tr("Please enter the correct amount")); + return; + } + + + QList recipients; + SendCoinsRecipient recipient; + bool valid = true; + + if (recipient.paymentRequest.IsInitialized()) + return ; + + + QString strNumEdit = ui->numEdit->text(); + recipient.address = ui->addEdit->text(); + QString add = ui->addEdit->text(); + QString lab =this->model->getAddressTableModel()->labelForAddress(add); + recipient.label =lab; + recipient.amount = 0; + recipient.message = ""; + recipient.fSubtractFeeFromAmount = false; + recipients.append(recipient); + fNewRecipientAllowed = false; + + // If wallet is still locked, unlock was failed or cancelled, mark context as invalid + if(!model->CheckPassword()) + { + ui->tiplabel->setText(tr("password error")); + return ; + } + WalletModel::UnlockContext ctx(model, true, true); + + WalletModelTransaction currentTransaction(recipients); + WalletModel::SendCoinsReturn prepareStatus; + + CCoinControl ctrl; + + if (model->getOptionsModel()->getCoinControlFeatures()) + { + // ctrl = *CoinControlDialog::coinControl; + } + else + { + ctrl.nConfirmTarget = 1; + } + + + std::string Txid = m_name.toStdString(); + int acc = model->GetAccuracyBySymbol(Txid); + double temp10 = 10; + int pointplace = strNumEdit.indexOf("."); + printf("pointplace %d \n",pointplace); + if(pointplace>0) + { + int afterpointnum = strNumEdit.size() - pointplace-1; + strNumEdit.replace(QString("."), QString("")); + acc =acc-afterpointnum; + } + while (acc>0) { + strNumEdit+="0"; + acc--; + } + + stringstream strValue; + strValue << strNumEdit.toStdString().c_str(); + uint64_t intNumEdit; + strValue >> intNumEdit; + LOG_WRITE(LOG_INFO,"SendCoinsAffrimWidget::strNumEdit",\ + strNumEdit.toStdString().c_str(),\ + QString::number(intNumEdit).toStdString().c_str()); + + if(intNumEdit<=0){ + ui->tiplabel->setText(tr("num error")); + return ; + } + + prepareStatus = model->prepareeCoinsSendCreateTransaction(Txid,0,intNumEdit,currentTransaction,m_error, &ctrl); + processSendCoinsReturn(prepareStatus, + BitcoinUnits::formatWithUnit(eTag, currentTransaction.getTransactionFee())); + + if(prepareStatus.status != WalletModel::OK) { + fNewRecipientAllowed = true; + return; + } + + CAmount txFee = currentTransaction.getTransactionFee(); + CWalletTx *newTx = currentTransaction.getTransaction(); + + // Format confirmation message + QStringList formatted; + Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients()) + { + + QString amount = "" + BitcoinUnits::formatHtmlWithUnit(0, rcp.amount); + + amount.append(""); + QString address = "" + rcp.address; + address.append(""); + QString recipientElement; + QString q = QString("%1").arg(rcp.amount); + if (!rcp.paymentRequest.IsInitialized()) + { + if(rcp.label.length() > 0) // label with address + { + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); + recipientElement.append(QString(" (%1)").arg(address)); + } + else // just address + { + recipientElement = tr("%1 to %2").arg(amount, address); + } + } + else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request + { + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); + } + else + { + recipientElement = tr("%1 to %2").arg(amount, address); + } + + formatted.append(recipientElement); + } + + QString questionString = tr("Are you sure you want to send?"); + questionString.append("

%1"); + + if(txFee > 0) + { + questionString.append("
"); + questionString.append(BitcoinUnits::formatHtmlWithUnit(0, txFee)); + questionString.append(" "); + questionString.append(tr("added as transaction fee")); + questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)"); + } + questionString.append("
"); + CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; + QStringList alternativeUnits; + Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) + { + alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(0, totalAmount)); + } + + questionString.append(tr("Total Amount %1") + .arg(BitcoinUnits::formatHtmlWithUnit(0, totalAmount))); + + questionString.append(QString("
(=%2)
") + .arg(alternativeUnits.join(" " + tr("or") + "
"))); + + WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); + m_error = model->m_sendcoinerror; + processSendCoinsReturn(sendStatus); + + if (sendStatus.status == WalletModel::OK) + { + ui->addEdit->setText(""); + Q_EMIT SendeCoinSuccess(m_name); + } + fNewRecipientAllowed = true; + +} diff --git a/.svn/pristine/a7/a7f95691dcb4c736f3e796af79f2fe0560517b4d.svn-base b/.svn/pristine/a7/a7f95691dcb4c736f3e796af79f2fe0560517b4d.svn-base new file mode 100644 index 0000000..6dc4806 --- /dev/null +++ b/.svn/pristine/a7/a7f95691dcb4c736f3e796af79f2fe0560517b4d.svn-base @@ -0,0 +1,51 @@ +#ifndef TALLYACCOUNT_H +#define TALLYACCOUNT_H + +#include +#include "walletmodel.h" +#include "upgradewidget.h" +#include +#include +class WalletModel; +namespace Ui { +class TallyAccount; +} + +class TallyAccount : public QWidget +{ + Q_OBJECT + +public: + explicit TallyAccount(QWidget *parent = 0); + ~TallyAccount(); + void setinfo(WalletModel::keepupaccountInfo info); + void SetnewInfo(); + WalletModel::keepupaccountInfo getinfo(); + void setModel(WalletModel *_model); + void setfinishinfo(); + void resettime(); + void GetTotalAmount(); + void getPasswordIfTallyAccountIng(); + void showPwdErrAndStop(); +private Q_SLOTS: + void on_pushButton_outaccount_pressed(); + void updateIpcList(); + +private: + Ui::TallyAccount *ui; + WalletModel *walletmodel; + WalletModel::keepupaccountInfo info_; + std::vector m_bookkeeplist; + QVBoxLayout *pvboxlayoutall; + void addItemContent(int row, int column, QString content); + int m_time; +Q_SIGNALS: + void next(QString add,CAmount num); +protected: + void timerEvent( QTimerEvent *event ); + int m_nTimerId; + + +}; + +#endif // TALLYACCOUNT_H diff --git a/.svn/pristine/cf/cf3e87ec2e32ce4a4540a648af65b2e2f6c55c55.svn-base b/.svn/pristine/cf/cf3e87ec2e32ce4a4540a648af65b2e2f6c55c55.svn-base new file mode 100644 index 0000000..45b1ddb --- /dev/null +++ b/.svn/pristine/cf/cf3e87ec2e32ce4a4540a648af65b2e2f6c55c55.svn-base @@ -0,0 +1,9426 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/wallet.h" + +#include "base58.h" +#include "checkpoints.h" +#include "chain.h" +#include "wallet/coincontrol.h" +#include "consensus/consensus.h" +#include "consensus/validation.h" +#include "key.h" +#include "keystore.h" +#include "validation.h" +#include "net.h" +#include "policy/policy.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "script/script.h" +#include "script/sign.h" +#include "timedata.h" +#include "txmempool.h" +#include "util.h" +#include "ui_interface.h" +#include "utilmoneystr.h" +#include "univalue.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "univalue/include/univalue.h" +#include "rpc/server.h" +#include "dpoc/DpocInfo.h" +#include "dpoc/TimeService.h" +#include "dpoc/ConsensusAccountPool.h" + +using namespace std; + +CWallet* pwalletMain = NULL; +/** Transaction fee set by the user */ +CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); +unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; +bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; +bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; +bool fWalletRbf = DEFAULT_WALLET_RBF; + +const char * DEFAULT_WALLET_DAT = "wallet.dat"; +const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; + +/** + * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) + * Override with -mintxfee + */ +CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); +/** + * If fee estimation does not have enough data to provide estimates, use this fee instead. + * Has no effect if not using fee estimation + * Override with -fallbackfee + */ +CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); + +const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); + +/** @defgroup mapWallet + * + * @{ + */ + +struct CompareValueOnly +{ + bool operator()(const pair >& t1, + const pair >& t2) const + { + return t1.first < t2.first; + } +}; + +std::string COutput::ToString() const +{ + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); +} + +bool COutput::CanBeSentToOhter() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return false; + return (tx->tx->vout[i].txType == TXOUT_IPCOWNER); +} + +bool COutput::CanBeAuthorizedToOther() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return false; + return (tx->tx->vout[i].ipcLabel.reAuthorize == 1); +} + +bool COutput::CanBeUniqueAuthorizedToOther() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return false; + if (tx->tx->vout[i].txType != TXOUT_IPCOWNER) + return false; + if (tx->tx->vout[i].ipcLabel.uniqueAuthorize == 0) + return true; +} + +int COutput::GetType() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].txType; +} + +int COutput::GetIPCExtendType() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].ipcLabel.ExtendType; +} + +uint32_t COutput::GetIPCStartTime() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + return tx->tx->vout[i].ipcLabel.startTime; +} + +uint32_t COutput::GetIPCStopTime() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + return tx->tx->vout[i].ipcLabel.stopTime; +} + +uint8_t COutput::GetIPCreAuthorize() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].ipcLabel.reAuthorize; +} + +uint8_t COutput::GetIPCUniqAuthorize()const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].ipcLabel.uniqueAuthorize; +} + +std::string COutput::GetIPCHash() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + return tx->tx->vout[i].ipcLabel.hash.GetHex(); +} + +std::string COutput::GetIPCTitle() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + return tx->tx->vout[i].ipcLabel.labelTitle; +} + +std::string COutput::GetIPCLabel() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + return tx->tx->vout[i].txLabel; +} + +std::string COutput::GetTokenSymbol() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + int txtype = GetType(); + if (txtype == TXOUT_TOKENREG) + { + return tx->tx->vout[i].tokenRegLabel.getTokenSymbol(); + } + else if (txtype == TXOUT_TOKEN) + { + return tx->tx->vout[i].tokenLabel.getTokenSymbol(); + } + return ""; +} +uint8_t COutput::GetTokenAccuracy() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 10; + int txtype = GetType(); + if (txtype == TXOUT_TOKENREG) + { + return tx->tx->vout[i].tokenRegLabel.accuracy; + } + else if (txtype == TXOUT_TOKEN) + { + return tx->tx->vout[i].tokenLabel.accuracy; + } + return 0; +} + +uint64_t COutput::GetTokenvalue() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + int txtype = GetType(); + if (txtype == TXOUT_TOKENREG) + { + return tx->tx->vout[i].tokenRegLabel.totalCount; + } + else if (txtype == TXOUT_TOKEN) + { + return tx->tx->vout[i].tokenLabel.value; + } + return uint64_t(0); +} +int64_t COutput::GetAssetEntryTime() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + int ntxtype = GetType(); + std::string stripchash = GetIPCHash(); + int64_t ipcstarttime = tx->tx->vout[i].ipcLabel.startTime; + if (ntxtype != TXOUT_IPCOWNER && ntxtype != TXOUT_IPCAUTHORIZATION) + { + return tx->GetTimeOfTokenInChain()>ipcstarttime ? tx->GetTimeOfTokenInChain() : ipcstarttime; + } + if (pwalletMain->mapWallet.count(tx->tx->GetHash()) == 0) + return 0; + CWalletTx curtx = pwalletMain->mapWallet[tx->tx->GetHash()]; + CTransactionRef curptx = curtx.tx; + bool isFindParenttx = false; + int nindex = 0; + while (true) + { + if (NULL == curptx) + return 0; + + nindex = 0; + BOOST_FOREACH(const CTxIn& txin, curptx->vin) + { + nindex++; + isminetype isme= pwalletMain->IsMine(txin); + if (isme == ISMINE_NO) + { + isFindParenttx = true; + break; + } + if (pwalletMain->mapWallet.count(txin.prevout.hash) == 0) + continue; + CWalletTx& prev = pwalletMain->mapWallet[txin.prevout.hash]; + if (NULL == prev.tx) + return 0; + if (prev.tx->vout[txin.prevout.n].txType == ntxtype && stripchash == prev.tx->vout[txin.prevout.n].ipcLabel.hash.GetHex()) + { + ipcstarttime = prev.tx->vout[txin.prevout.n].ipcLabel.startTime; + curtx = prev; + curptx = curtx.tx; + break;; + } + else if (nindex == curptx->vin.size()) + { + isFindParenttx = true; + break; + } + + + } + if (isFindParenttx) + { + return curtx.GetTimeOfTokenInChain()>ipcstarttime ? curtx.GetTimeOfTokenInChain() : ipcstarttime; + } + + } + + return 0; +} + +const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const +{ + LOCK(cs_wallet); + std::map::const_iterator it = mapWallet.find(hash); + if (it == mapWallet.end()) + return NULL; + return &(it->second); +} + +CPubKey CWallet::GenerateNewKey() +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + + CKey secret; + + // Create new metadata + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + + // use HD key derivation if HD was enabled during wallet creation + if (IsHDEnabled()) { + DeriveNewChildKey(metadata, secret); + } else { + secret.MakeNewKey(fCompressed); + } + + // Compressed public keys were introduced in version 0.6.0 + if (fCompressed) + SetMinVersion(FEATURE_COMPRPUBKEY); + + CPubKey pubkey = secret.GetPubKey(); + assert(secret.VerifyPubKey(pubkey)); + + mapKeyMetadata[pubkey.GetID()] = metadata; + UpdateTimeFirstKey(nCreationTime); + + if (!AddKeyPubKey(secret, pubkey)) + throw std::runtime_error(std::string(__func__) + ": AddKey failed"); + return pubkey; +} + +void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret) +{ + // for now we use a fixed keypath scheme of m/0'/0'/k + CKey key; //master key seed (256bit) + CExtKey masterKey; //hd master key + CExtKey accountKey; //key at m/0' + CExtKey externalChainChildKey; //key at m/0'/0' + CExtKey childKey; //key at m/0'/0'/' + + // try to get the master key + if (!GetKey(hdChain.masterKeyID, key)) + throw std::runtime_error(std::string(__func__) + ": Master key not found"); + + masterKey.SetMaster(key.begin(), key.size()); + + // derive m/0' + // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) + masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); + + // derive m/0'/0' + accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); + + // derive child key at next index, skip keys already known to the wallet + do { + // always derive hardened keys + // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range + // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 + externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); + metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'"; + metadata.hdMasterKeyID = hdChain.masterKeyID; + // increment childkey index + hdChain.nExternalChainCounter++; + } while (HaveKey(childKey.key.GetPubKey().GetID())); + secret = childKey.key; + + // update the chain model in the database + if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) + throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); +} + +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) + return false; + + // check if we need to remove from watch-only + CScript script; + script = GetScriptForDestination(pubkey.GetID()); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); + script = GetScriptForRawPubKey(pubkey); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); + + if (!fFileBacked) + return true; + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteKey(pubkey, + secret.GetPrivKey(), + mapKeyMetadata[pubkey.GetID()]); + } + return true; +} + +bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, + const vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + if (!fFileBacked) + return true; + { + LOCK(cs_wallet); + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedKey(vchPubKey, + vchCryptedSecret, + mapKeyMetadata[vchPubKey.GetID()]); + else + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, + vchCryptedSecret, + mapKeyMetadata[vchPubKey.GetID()]); + } + return false; +} + +bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta) +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + UpdateTimeFirstKey(meta.nCreateTime); + mapKeyMetadata[keyID] = meta; + return true; +} + +bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) +{ + return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); +} + +void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) +{ + AssertLockHeld(cs_wallet); + if (nCreateTime <= 1) { + // Cannot determine birthday information, so set the wallet birthday to + // the beginning of time. + nTimeFirstKey = 1; + } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) { + nTimeFirstKey = nCreateTime; + } +} + +bool CWallet::AddCScript(const CScript& redeemScript) +{ + if (!CCryptoKeyStore::AddCScript(redeemScript)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); +} + +bool CWallet::LoadCScript(const CScript& redeemScript) +{ + /* A sanity check was added in pull #3843 to avoid adding redeemScripts + * that never can be redeemed. However, old wallets may still contain + * these. Do not add them to the wallet and warn. */ + if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) + { + std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString(); + LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", + __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); + return true; + } + + return CCryptoKeyStore::AddCScript(redeemScript); +} + +bool CWallet::AddWatchOnly(const CScript& dest) +{ + if (!CCryptoKeyStore::AddWatchOnly(dest)) + return false; + const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)]; + UpdateTimeFirstKey(meta.nCreateTime); + NotifyWatchonlyChanged(true); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta); +} + +bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime) +{ + mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime; + return AddWatchOnly(dest); +} + +bool CWallet::RemoveWatchOnly(const CScript &dest) +{ + AssertLockHeld(cs_wallet); + if (!CCryptoKeyStore::RemoveWatchOnly(dest)) + return false; + if (!HaveWatchOnly()) + NotifyWatchonlyChanged(false); + if (fFileBacked) + if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) + return false; + + return true; +} + +bool CWallet::LoadWatchOnly(const CScript &dest) +{ + return CCryptoKeyStore::AddWatchOnly(dest); +} + +bool CWallet::Unlock(const SecureString& strWalletPassphrase) +{ + CCrypter crypter; + CKeyingMaterial vMasterKey; + + { + LOCK(cs_wallet); + BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + continue; // try another master key + LogPrintf("[crypter.Decrypt]..vMasterKey.size() = %d \n",vMasterKey.size()); + if (CCryptoKeyStore::Unlock(vMasterKey)) + { + curstrWalletPassphrase = strWalletPassphrase; + return true; + } + + } + } + return false; +} + +bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) +{ + bool fWasLocked = IsLocked(); + + { + LOCK(cs_wallet); + Lock(); + + CCrypter crypter; + CKeyingMaterial vMasterKey; + BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + { + int64_t nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + + if (pMasterKey.second.nDeriveIterations < 25000) + pMasterKey.second.nDeriveIterations = 25000; + + LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) + return false; + CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); + if (fWasLocked) + Lock(); + curstrWalletPassphrase = strNewWalletPassphrase; + return true; + } + } + } + + return false; +} + +void CWallet::SetBestChain(const CBlockLocator& loc) +{ + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + walletdb.WriteBestBlock(loc); +} + +bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) +{ + LOCK(cs_wallet); // nWalletVersion + if (nWalletVersion >= nVersion) + return true; + + // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way + if (fExplicit && nVersion > nWalletMaxVersion) + nVersion = FEATURE_LATEST; + + nWalletVersion = nVersion; + + if (nVersion > nWalletMaxVersion) + nWalletMaxVersion = nVersion; + + if (fFileBacked) + { + CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); + if (nWalletVersion > 40000) + pwalletdb->WriteMinVersion(nWalletVersion); + if (!pwalletdbIn) + delete pwalletdb; + } + + return true; +} + +bool CWallet::SetMaxVersion(int nVersion) +{ + + LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion + // cannot downgrade below current version + if (nWalletVersion > nVersion) + return false; + + nWalletMaxVersion = nVersion; + + return true; +} + +set CWallet::GetConflicts(const uint256& txid) const +{ + set result; + AssertLockHeld(cs_wallet); + + std::map::const_iterator it = mapWallet.find(txid); + if (it == mapWallet.end()) + return result; + const CWalletTx& wtx = it->second; + + std::pair range; + + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (mapTxSpends.count(txin.prevout) <= 1) + continue; // No conflict if zero or one spends + range = mapTxSpends.equal_range(txin.prevout); + for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) + result.insert(_it->second); + } + return result; +} + +bool CWallet::HasWalletSpend(const uint256& txid) const +{ + AssertLockHeld(cs_wallet); + auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); + return (iter != mapTxSpends.end() && iter->first.hash == txid); +} + +void CWallet::Flush(bool shutdown) +{ + bitdb.Flush(shutdown); +} + +bool CWallet::Verify() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); + std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); + + LogPrintf("Using wallet %s\n", walletFile); + uiInterface.InitMessage(_("Verifying wallet...")); + + // Wallet file must be a plain filename without a directory + if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile)) + return InitError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string())); + + if (!bitdb.Open(GetDataDir())) + { + // try moving the database env out of the way + boost::filesystem::path pathDatabase = GetDataDir() / "database"; + boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); + try { + boost::filesystem::rename(pathDatabase, pathDatabaseBak); + LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); + } catch (const boost::filesystem::filesystem_error&) { + // failure is ok (well, not really, but it's not worse than what we started with) + } + + // try again + if (!bitdb.Open(GetDataDir())) { + // if it still fails, it probably means we can't even create the database env + return InitError(strprintf(_("Error initializing wallet database environment %s!"), GetDataDir())); + } + } + + if (GetBoolArg("-salvagewallet", false)) + { + // Recover readable keypairs: + if (!CWalletDB::Recover(bitdb, walletFile, true)) + return false; + } + + if (boost::filesystem::exists(GetDataDir() / walletFile)) + { + CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover); + if (r == CDBEnv::RECOVER_OK) + { + InitWarning(strprintf(_("Warning: Wallet file corrupt, data salvaged!" + " Original %s saved as %s in %s; if" + " your balance or transactions are incorrect you should" + " restore from a backup."), + walletFile, "wallet.{timestamp}.bak", GetDataDir())); + } + if (r == CDBEnv::RECOVER_FAIL){ + LogPrintf("Using wallet %s InitError r == CDBEnv::RECOVER_FAIL\n", walletFile); + // return InitError(strprintf(_("%s corrupt, salvage failed"), walletFile)); + } + } + + return true; +} + +void CWallet::SyncMetaData(pair range) +{ + // We want all the wallet transactions in range to have the same metadata as + // the oldest (smallest nOrderPos). + // So: find smallest nOrderPos: + + int nMinOrderPos = std::numeric_limits::max(); + const CWalletTx* copyFrom = NULL; + for (TxSpends::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + int n = mapWallet[hash].nOrderPos; + if (n < nMinOrderPos) + { + nMinOrderPos = n; + copyFrom = &mapWallet[hash]; + } + } + // Now copy data from copyFrom to rest: + for (TxSpends::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + CWalletTx* copyTo = &mapWallet[hash]; + if (copyFrom == copyTo) continue; + if (!copyFrom->IsEquivalentTo(*copyTo)) continue; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + // fTimeReceivedIsTxTime not copied on purpose + // nTimeReceived not copied on purpose + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + // nOrderPos not copied on purpose + // cached members not copied on purpose + } +} + +/** + * Outpoint is spent if any non-conflicted transaction + * spends it: + */ +bool CWallet::IsSpent(const uint256& hash, unsigned int n) const +{ + + const COutPoint outpoint(hash, n); + pair range; + range = mapTxSpends.equal_range(outpoint); + + + for (TxSpends::const_iterator it = range.first; it != range.second; ++it) + { + const uint256& wtxid = it->second; + std::map::const_iterator mit = mapWallet.find(wtxid); + if (mit != mapWallet.end()) { + int depth = mit->second.GetDepthInMainChain(); + if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) + return true; // Spent + } + } + return false; +} + +void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) +{ + mapTxSpends.insert(make_pair(outpoint, wtxid)); + + pair range; + range = mapTxSpends.equal_range(outpoint); + SyncMetaData(range); +} + + +void CWallet::AddToSpends(const uint256& wtxid) +{ + + assert(mapWallet.count(wtxid)); + CWalletTx& thisTx = mapWallet[wtxid]; + if (thisTx.IsCoinBase()) // Coinbases don't spend anything! + return; + BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin) + AddToSpends(txin.prevout, wtxid); +} + +void CWallet::RemoveFromSpends(const COutPoint& outpoint, const uint256& wtxid) +{ + mapTxSpends.erase(outpoint); +} + + +void CWallet::RemoveFromSpends(const uint256& wtxid) +{ + int count = mapWallet.count(wtxid); + assert(count); + CWalletTx& thisTx = mapWallet[wtxid]; + if (thisTx.IsCoinBase()) // Coinbases don't spend anything! + return; + + BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin) + RemoveFromSpends(txin.prevout, wtxid); +} + + +bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) +{ + if (IsCrypted()) + return false; + LogPrintf("[EncryptWallet]:in ......................\n"); + CKeyingMaterial vMasterKey; + + vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); + GetStrongRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + + CMasterKey kMasterKey; + + kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); + GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + + CCrypter crypter; + int64_t nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + + if (kMasterKey.nDeriveIterations < 25000) + kMasterKey.nDeriveIterations = 25000; + + LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) + return false; + LogPrintf("[EncryptWallet]:fFileBacked = %d \n",fFileBacked); + { + LOCK(cs_wallet); + mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; + if (fFileBacked) + { + assert(!pwalletdbEncryption); + pwalletdbEncryption = new CWalletDB(strWalletFile); + if (!pwalletdbEncryption->TxnBegin()) { + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + return false; + } + pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); + } + + if (!EncryptKeys(vMasterKey)) + { + if (fFileBacked) { + pwalletdbEncryption->TxnAbort(); + delete pwalletdbEncryption; + } + // We now probably have half of our keys encrypted in memory, and half not... + // die and let the user reload the unencrypted wallet. + assert(false); + } + + // Encryption was introduced in version 0.4.0 + SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); + + if (fFileBacked) + { + if (!pwalletdbEncryption->TxnCommit()) { + delete pwalletdbEncryption; + // We now have keys encrypted in memory, but not on disk... + // die to avoid confusion and let the user reload the unencrypted wallet. + assert(false); + } + + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + } + + Lock(); + Unlock(strWalletPassphrase); + + // if we are using HD, replace the HD master key (seed) with a new one + if (IsHDEnabled()) { + CKey key; + CPubKey masterPubKey = GenerateNewHDMasterKey(); + if (!SetHDMasterKey(masterPubKey)) + return false; + } + + NewKeyPool(); + Lock(); + + // Need to completely rewrite the wallet file; if we don't, bdb might keep + // bits of the unencrypted private key in slack space in the database file. + CDB::Rewrite(strWalletFile); + + } + NotifyStatusChanged(this); + return true; +} + +DBErrors CWallet::ReorderTransactions() +{ + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + + // Old wallets didn't have any defined order for transactions + // Probably a bad idea to change the output of this + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + walletdb.ListAccountCreditDebit("", acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + nOrderPosNext = 0; + std::vector nOrderPosOffsets; + for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + + if (nOrderPos == -1) + { + nOrderPos = nOrderPosNext++; + nOrderPosOffsets.push_back(nOrderPos); + + if (pwtx) + { + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + else + if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + else + { + int64_t nOrderPosOff = 0; + BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) + { + if (nOrderPos >= nOffsetStart) + ++nOrderPosOff; + } + nOrderPos += nOrderPosOff; + nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); + + if (!nOrderPosOff) + continue; + + // Since we're changing the order, write it back + if (pwtx) + { + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + else + if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + } + walletdb.WriteOrderPosNext(nOrderPosNext); + + return DB_LOAD_OK; +} + +int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) +{ + AssertLockHeld(cs_wallet); // nOrderPosNext + int64_t nRet = nOrderPosNext++; + if (pwalletdb) { + pwalletdb->WriteOrderPosNext(nOrderPosNext); + } else { + CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); + } + return nRet; +} + +bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) +{ + CWalletDB walletdb(strWalletFile); + if (!walletdb.TxnBegin()) + return false; + + int64_t nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.nOrderPos = IncOrderPosNext(&walletdb); + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + AddAccountingEntry(debit, &walletdb); + + // Credit + CAccountingEntry credit; + credit.nOrderPos = IncOrderPosNext(&walletdb); + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + AddAccountingEntry(credit, &walletdb); + + if (!walletdb.TxnCommit()) + return false; + + return true; +} + +bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew) +{ + CWalletDB walletdb(strWalletFile); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + if (!bForceNew) { + if (!account.vchPubKey.IsValid()) + bForceNew = true; + else { + // Check if the current key has been used + CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); + for (map::iterator it = mapWallet.begin(); + it != mapWallet.end() && account.vchPubKey.IsValid(); + ++it) + BOOST_FOREACH(const CTxOut& txout, (*it).second.tx->vout) + if (txout.scriptPubKey == scriptPubKey) { + bForceNew = true; + break; + } + } + } + + // Generate a new key + if (bForceNew) { + if (!GetKeyFromPool(account.vchPubKey)) + return false; + + SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); + walletdb.WriteAccount(strAccount, account); + } + + pubKey = account.vchPubKey; + + return true; +} + +void CWallet::MarkDirty() +{ + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + item.second.MarkDirty(); + } +} + +bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) +{ + LOCK(cs_wallet); + + auto mi = mapWallet.find(originalHash); + + // There is a bug if MarkReplaced is not called on an existing wallet transaction. + assert(mi != mapWallet.end()); + + CWalletTx& wtx = (*mi).second; + + // Ensure for now that we're not overwriting data + assert(wtx.mapValue.count("replaced_by_txid") == 0); + + wtx.mapValue["replaced_by_txid"] = newHash.ToString(); + + CWalletDB walletdb(strWalletFile, "r+"); + + bool success = true; + if (!walletdb.WriteTx(wtx)) { + LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString()); + success = false; + } + + NotifyTransactionChanged(this, originalHash, CT_UPDATED); + + return success; +} + +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) +{ + LOCK(cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); + + uint256 hash = wtxIn.GetHash(); + + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + wtx.BindWallet(this); + bool fInsertedNew = ret.second; + if (fInsertedNew) + { + wtx.nTimeReceived = GetAdjustedTime(); + wtx.nOrderPos = IncOrderPosNext(&walletdb); + wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + + wtx.nTimeSmart = wtx.nTimeReceived; + if (!wtxIn.hashUnset()) + { + if (mapBlockIndex.count(wtxIn.hashBlock)) + { + int64_t latestNow = wtx.nTimeReceived; + int64_t latestEntry = 0; + { + // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future + int64_t latestTolerated = latestNow + 300; + const TxItems & txOrdered = wtxOrdered; + for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx == &wtx) + continue; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t nSmartTime; + if (pwtx) + { + nSmartTime = pwtx->nTimeSmart; + if (!nSmartTime) + nSmartTime = pwtx->nTimeReceived; + } + else + nSmartTime = pacentry->nTime; + if (nSmartTime <= latestTolerated) + { + latestEntry = nSmartTime; + if (nSmartTime > latestNow) + latestNow = nSmartTime; + break; + } + } + } + + int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); + wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); + } + else + LogPrintf("AddToWallet(): found %s in block %s not in index\n", + wtxIn.GetHash().ToString(), + wtxIn.hashBlock.ToString()); + } + AddToSpends(hash); + } + + bool fUpdated = false; + if (!fInsertedNew) + { + + // Merge + if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + // If no longer abandoned, update + if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) + { + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + } + + //// debug print + LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!walletdb.WriteTx(wtx)) + { + return false; + } + + + // Break debit/credit balance caches: + wtx.MarkDirty(); + + // Notify UI of new or updated transaction + NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + return true; +} + +bool CWallet::RemoveFromWallet(const CWalletTx& wtxIn, bool fFlushOnClose) +{ + LOCK(cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); + + uint256 hash = wtxIn.GetHash(); + + RemoveFromSpends(hash); + + bool fUpdated = false; + //// debug print + LogPrintf("RemoveFromWallet %s %s\n", wtxIn.GetHash().ToString(), "removed"); + + int ret = mapWallet.erase(hash); + + // Write to disk + if (!walletdb.EraseTx(hash)) + return false; + + // Notify UI of new or updated transaction + NotifyTransactionChanged(this, hash, CT_DELETED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if (!strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + return true; + +} +bool CWallet::LoadToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + + mapWallet[hash] = wtxIn; + CWalletTx& wtx = mapWallet[hash]; + wtx.BindWallet(this); + wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + AddToSpends(hash); + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { + if (mapWallet.count(txin.prevout.hash)) { + CWalletTx& prevtx = mapWallet[txin.prevout.hash]; + if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { + MarkConflicted(prevtx.hashBlock, wtx.GetHash()); + } + } + } + + return true; +} + +/** + * Add a transaction to the wallet, or update it. pIndex and posInBlock should + * be set when the transaction was known to be included in a block. When + * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not + * updated in AddToWallet, but notifications happen and cached balances are + * marked dirty. + * If fUpdate is true, existing transactions will be updated. + * TODO: One exception to this is that the abandoned state is cleared under the + * assumption that any further notification of a transaction that was considered + * abandoned is an indication that it is not safe to be considered abandoned. + * Abandoned state should probably be more carefuly tracked via different + * posInBlock signals or by checking mempool presence when necessary. + */ +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) +{ + { + AssertLockHeld(cs_wallet); + + if (posInBlock != -1) { + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + std::pair range = mapTxSpends.equal_range(txin.prevout); + while (range.first != range.second) { + if (range.first->second != tx.GetHash()) { + LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); + MarkConflicted(pIndex->GetBlockHash(), range.first->second); + } + range.first++; + } + } + } + + bool fExisted = mapWallet.count(tx.GetHash()) != 0; + if (fExisted && !fUpdate) + { + std::cout << "fExisted :" << fExisted << " ,fUpdate : " << fUpdate << std::endl; + return false; + } + if (fExisted || IsMine(tx) || IsFromMe(tx) || IsFromMe(tx,1)) + { + CWalletTx wtx(this, MakeTransactionRef(tx)); + + // Get merkle branch if transaction was found in a block + if (posInBlock != -1) + wtx.SetMerkleBranch(pIndex, posInBlock); + return AddToWallet(wtx, false); + } + } + return false; +} + +bool CWallet::AbandonTransaction(const uint256& hashTx) +{ + LOCK2(cs_main, cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+"); + + std::set todo; + std::set done; + + // Can't mark abandoned if confirmed or in mempool + assert(mapWallet.count(hashTx)); + CWalletTx& origtx = mapWallet[hashTx]; + if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { + return false; + } + + todo.insert(hashTx); + + while (!todo.empty()) { + uint256 now = *todo.begin(); + todo.erase(now); + done.insert(now); + assert(mapWallet.count(now)); + CWalletTx& wtx = mapWallet[now]; + int currentconfirm = wtx.GetDepthInMainChain(); + // If the orig tx was not in block, none of its spends can be + assert(currentconfirm <= 0); + // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon} + if (currentconfirm == 0 && !wtx.isAbandoned()) { + // If the orig tx was not in block/mempool, none of its spends can be in mempool + assert(!wtx.InMempool()); + wtx.nIndex = -1; + wtx.setAbandoned(); + wtx.MarkDirty(); + walletdb.WriteTx(wtx); + NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED); + // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too + TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(hashTx, 0)); + while (iter != mapTxSpends.end() && iter->first.hash == now) { + if (!done.count(iter->second)) { + todo.insert(iter->second); + } + iter++; + } + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be recomputed + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } + } + } + + return true; +} + +void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) +{ + LOCK2(cs_main, cs_wallet); + + int conflictconfirms = 0; + if (mapBlockIndex.count(hashBlock)) { + CBlockIndex* pindex = mapBlockIndex[hashBlock]; + if (chainActive.Contains(pindex)) { + conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); + } + } + // If number of conflict confirms cannot be determined, this means + // that the block is still unknown or not yet part of the main chain, + // for example when loading the wallet during a reindex. Do nothing in that + // case. + if (conflictconfirms >= 0) + return; + + // Do not flush the wallet here for performance reasons + CWalletDB walletdb(strWalletFile, "r+", false); + + std::set todo; + std::set done; + + todo.insert(hashTx); + + while (!todo.empty()) { + uint256 now = *todo.begin(); + todo.erase(now); + done.insert(now); + assert(mapWallet.count(now)); + CWalletTx& wtx = mapWallet[now]; + int currentconfirm = wtx.GetDepthInMainChain(); + if (conflictconfirms < currentconfirm) { + // Block is 'more conflicted' than current confirm; update. + // Mark transaction as conflicted with this block. + wtx.nIndex = -1; + wtx.hashBlock = hashBlock; + wtx.MarkDirty(); + walletdb.WriteTx(wtx); + // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too + TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); + while (iter != mapTxSpends.end() && iter->first.hash == now) { + if (!done.count(iter->second)) { + todo.insert(iter->second); + } + iter++; + } + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be recomputed + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } + } + } +} + +void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) +{ + LOCK2(cs_main, cs_wallet); + + if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true)) + return; // Not one of ours + + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be + // recomputed, also: + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } +} + + +isminetype CWallet::IsMine(const CTxIn &txin) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.tx->vout.size()) + return IsMine(prev.tx->vout[txin.prevout.n]); + } + } + return ISMINE_NO; +} + + + +// Note that this function doesn't distinguish between a 0-valued input, +// and a not-"is mine" (according to the filter) input. +CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.tx->vout.size()) + if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) + return prev.tx->vout[txin.prevout.n].nValue; + } + } + return 0; +} + +isminetype CWallet::IsMine(const CTxOut& txout) const +{ + return ::IsMine(*this, txout.scriptPubKey); +} + + + +CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const +{ + if (!MoneyRange(txout.nValue)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + return ((IsMine(txout) & filter) ? txout.nValue : 0); +} + +bool CWallet::IsChange(const CTxOut& txout) const +{ + // TODO: fix handling of 'change' outputs. The assumption is that any + // payment to a script that is ours, but is not in the address book + // is change. That assumption is likely to break when we implement multisignature + // wallets that return change back into a multi-signature-protected address; + // a better way of identifying which outputs are 'the send' and which are + // 'the change' will need to be implemented (maybe extend CWalletTx to remember + // which output, if any, was change). + if (::IsMine(*this, txout.scriptPubKey)) + { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + return true; + + LOCK(cs_wallet); + if (!mapAddressBook.count(address)) + return true; + } + return false; +} + +CAmount CWallet::GetChange(const CTxOut& txout) const +{ + if (!MoneyRange(txout.nValue)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + return (IsChange(txout) ? txout.nValue : 0); +} + +bool CWallet::IsMine(const CTransaction& tx) const +{ + BOOST_FOREACH(const CTxOut& txout, tx.vout) + if (IsMine(txout)) + return true; + return false; +} + + + +bool CWallet::IsFromMe(const CTransaction& tx) const +{ + return (GetDebit(tx, ISMINE_ALL) > 0); +} + +bool CWallet::IsFromMe(const CTransaction& tx ,int type) const +{ + BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (IsMine(txin)) + return true; + return false; +} + +CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const +{ + CAmount nDebit = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nDebit += GetDebit(txin, filter); + if (!MoneyRange(nDebit)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nDebit; +} + +bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const +{ + LOCK(cs_wallet); + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + auto mi = mapWallet.find(txin.prevout.hash); + if (mi == mapWallet.end()) + return false; // any unknown inputs can't be from us + + const CWalletTx& prev = (*mi).second; + + if (txin.prevout.n >= prev.tx->vout.size()) + return false; // invalid input! + + if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) + return false; + } + return true; +} + +CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const +{ + CAmount nCredit = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nCredit += GetCredit(txout, filter); + if (!MoneyRange(nCredit)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nCredit; +} + +CAmount CWallet::GetChange(const CTransaction& tx) const +{ + CAmount nChange = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nChange = txout.nValue; + if (!MoneyRange(nChange)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nChange; +} + +CPubKey CWallet::GenerateNewHDMasterKey() +{ + CKey key; + key.MakeNewKey(true); + + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + + // calculate the pubkey + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + + // set the hd keypath to "m" -> Master, refers the masterkeyid to itself + metadata.hdKeypath = "m"; + metadata.hdMasterKeyID = pubkey.GetID(); + + { + LOCK(cs_wallet); + + // mem store the metadata + mapKeyMetadata[pubkey.GetID()] = metadata; + + // write the key&metadata to the database + if (!AddKeyPubKey(key, pubkey)) + throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); + } + + return pubkey; +} + +bool CWallet::SetHDMasterKey(const CPubKey& pubkey) +{ + LOCK(cs_wallet); + + // ensure this wallet.dat can only be opened by clients supporting HD + SetMinVersion(FEATURE_HD); + + // store the keyid (hash160) together with + // the child index counter in the database + // as a hdchain object + CHDChain newHdChain; + newHdChain.masterKeyID = pubkey.GetID(); + SetHDChain(newHdChain, false); + + return true; +} + +bool CWallet::SetHDChain(const CHDChain& chain, bool memonly) +{ + LOCK(cs_wallet); + if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain)) + throw runtime_error(std::string(__func__) + ": writing chain failed"); + + hdChain = chain; + return true; +} + +bool CWallet::IsHDEnabled() +{ + return !hdChain.masterKeyID.IsNull(); +} + +int64_t CWalletTx::GetTxTime() const +{ + int64_t n = nTimeSmart; + return n ? n : nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + { + LOCK(pwallet->cs_wallet); + if (IsCoinBase()) + { + // Generated block + if (!hashUnset()) + { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); + if (mi != pwallet->mapRequestCount.end()) + { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && !hashUnset()) + { + map::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock); + if (_mi != pwallet->mapRequestCount.end()) + nRequests = (*_mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(list& listReceived, + list& listSent, CAmount& nFee, string& strSentAccount, const isminefilter& filter) const +{ + nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + // Compute fee: + CAmount nDebit = GetDebit(filter); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + CAmount nValueOut = tx->GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. + for (unsigned int i = 0; i < tx->vout.size(); ++i) + { + const CTxOut& txout = tx->vout[i]; + isminetype fIsMine = pwallet->IsMine(txout); + // Only need to handle txouts if AT LEAST one of these is true: + // 1) they debit from us (sent) + // 2) the output is to us (received) + if (nDebit > 0) + { + // Don't report 'change' txouts + if (pwallet->IsChange(txout)) + continue; + } + else if (!(fIsMine & filter)) + continue; + + // In either case, we need to get the destination address + CTxDestination address; + + if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable()) + { + LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString()); + address = CNoDestination(); + } + + COutputEntry output = {address, txout.nValue, (int)i}; + + // If we are debited by the transaction, add the output as a "sent" entry + if (nDebit > 0) + listSent.push_back(output); + + // If we are receiving the output, add it as a "received" entry + if (fIsMine & filter) + listReceived.push_back(output); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived, + CAmount& nSent, CAmount& nFee, const isminefilter& filter) const +{ + nReceived = nSent = nFee = 0; + + CAmount allFee; + string strSentAccount; + list listReceived; + list listSent; + GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); + + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const COutputEntry& s, listSent) + nSent += s.amount; + nFee = allFee; + } + { + LOCK(pwallet->cs_wallet); + BOOST_FOREACH(const COutputEntry& r, listReceived) + { + if (pwallet->mapAddressBook.count(r.destination)) + { + map::const_iterator mi = pwallet->mapAddressBook.find(r.destination); + if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount) + nReceived += r.amount; + } + else if (strAccount.empty()) + { + nReceived += r.amount; + } + } + } +} + +/** + * Scan the block chain (starting in pindexStart) for transactions + * from or to us. If fUpdate is true, found transactions that already + * exist in the wallet will be updated. + * + * Returns pointer to the first block in the last contiguous range that was + * successfully scanned. + * + */ +CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + CBlockIndex* ret = nullptr; + int64_t nNow = GetTime(); + const CChainParams& chainParams = Params(); + + CBlockIndex* pindex = pindexStart; + { + LOCK2(cs_main, cs_wallet); + + // no need to read and scan block, if block was created before + // our wallet birthday (as adjusted for block time variability) + while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) + pindex = chainActive.Next(pindex); + + ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup + double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); + double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); + while (pindex) + { + if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) + ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); + + CBlock block; + if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { + for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { + AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate); + } + if (!ret) { + ret = pindex; + } + } else { + ret = nullptr; + } + pindex = chainActive.Next(pindex); + if (GetTime() >= nNow + 60) { + nNow = GetTime(); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); + } + } + ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI + } + return ret; +} + +void CWallet::ReacceptWalletTransactions() +{ + // If transactions aren't being broadcasted, don't let them into local mempool either + if (!fBroadcastTransactions) + return; + LOCK2(cs_main, cs_wallet); + std::map mapSorted; + + // Sort pending wallet transactions based on their initial wallet insertion order + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + const uint256& wtxid = item.first; + CWalletTx& wtx = item.second; + assert(wtx.GetHash() == wtxid); + + int nDepth = wtx.GetDepthInMainChain(); + + if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { + mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); + } + } + LogPrintf("mapSorted.size() = %d \n",mapSorted.size()); + // Try to add wallet transactions to memory pool + BOOST_FOREACH(PAIRTYPE(const int64_t, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *(item.second); + + LOCK(mempool.cs); + CValidationState state; + wtx.AcceptToMemoryPool(maxTxFee, state); + } + LogPrintf("Done[mapSorted] \n"); +} + +bool CWalletTx::RelayWalletTransaction(CConnman* connman) +{ + assert(pwallet->GetBroadcastTransactions()); + if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0) + { + CValidationState state; + /* GetDepthInMainChain already catches known conflicts. */ + if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) { + LogPrintf("Relaying wtx %s\n", GetHash().ToString()); + if (connman) { + CInv inv(MSG_TX, GetHash()); + connman->ForEachNode([&inv](CNode* pnode) + { + pnode->PushInventory(inv); + }); + return true; + } + } + } + return false; +} + +set CWalletTx::GetConflicts() const +{ + set result; + if (pwallet != NULL) + { + uint256 myHash = GetHash(); + result = pwallet->GetConflicts(myHash); + result.erase(myHash); + } + return result; +} + +CAmount CWalletTx::GetDebit(const isminefilter& filter) const +{ + if (tx->vin.empty()) + return 0; + + CAmount debit = 0; + if(filter & ISMINE_SPENDABLE) + { + if (fDebitCached) + debit += nDebitCached; + else + { + nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); + fDebitCached = true; + debit += nDebitCached; + } + } + if(filter & ISMINE_WATCH_ONLY) + { + if(fWatchDebitCached) + debit += nWatchDebitCached; + else + { + nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); + fWatchDebitCached = true; + debit += nWatchDebitCached; + } + } + return debit; +} + +CAmount CWalletTx::GetCredit(const isminefilter& filter) const +{ + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + CAmount credit = 0; + if (filter & ISMINE_SPENDABLE) + { + // GetBalance can assume transactions in mapWallet won't change + if (fCreditCached) + credit += nCreditCached; + else + { + nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fCreditCached = true; + credit += nCreditCached; + } + } + if (filter & ISMINE_WATCH_ONLY) + { + if (fWatchCreditCached) + credit += nWatchCreditCached; + else + { + nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fWatchCreditCached = true; + credit += nWatchCreditCached; + } + } + return credit; +} + +CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const +{ + if (pwallet == 0) + return 0; + bool reAvailableCreditCached = false; + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached ) + return nAvailableCreditCached; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < tx->vout.size(); i++) + { + if (!pwallet->IsSpent(hashTx, i)) + { + const CTxOut &txout = tx->vout[i]; + //Remove the COINS that have been locked in. add by xxy 20171030 + if (txout.txType == TXOUT_CAMPAIGN && + txout.devoteLabel.ExtendType == TYPE_CONSENSUS_REGISTER) + { + reAvailableCreditCached = true; //The utxo that applies to join the type in tx will need to be re-checked next time, and the state will change! + if (!CConsensusAccountPool::Instance().IsAviableUTXO(hashTx)) + { + continue; + } + + } + //end + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + //add by xxy + if (reAvailableCreditCached) + fAvailableCreditCached = false; + //end + return nCredit; +} + +//add by xxy 20171219 +CAmount CWalletTx::GetLockCredit(bool fUseCache) const +{ + if (pwallet == 0) + return 0; + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return 0; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < tx->vout.size(); i++) + { + if (!pwallet->IsSpent(hashTx, i)) + { + const CTxOut &txout = tx->vout[i]; + if (txout.txType == TXOUT_CAMPAIGN && + txout.devoteLabel.ExtendType == TYPE_CONSENSUS_REGISTER) + { + if (!CConsensusAccountPool::Instance().IsAviableUTXO(hashTx)) + { + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + continue; + } + + } + //end + + + } + } + fAvailableCreditCached = false; + + return nCredit; +} +//end +CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureWatchCreditCached) + return nImmatureWatchCreditCached; + nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fImmatureWatchCreditCached = true; + return nImmatureWatchCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const +{ + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableWatchCreditCached) + return nAvailableWatchCreditCached; + + CAmount nCredit = 0; + for (unsigned int i = 0; i < tx->vout.size(); i++) + { + if (!pwallet->IsSpent(GetHash(), i)) + { + const CTxOut &txout = tx->vout[i]; + nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableWatchCreditCached = nCredit; + fAvailableWatchCreditCached = true; + return nCredit; +} + +CAmount CWalletTx::GetChange() const +{ + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; +} + +bool CWalletTx::InMempool() const +{ + LOCK(mempool.cs); + if (mempool.exists(GetHash())) { + return true; + } + return false; +} + +bool CWalletTx::IsTrusted() const +{ + + int nDepth = GetDepthInMainChain(); + if (nDepth >= 1) + return true; + if (nDepth < 0) + return false; + if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit + return false; + + // Don't trust unconfirmed transactions from us unless they are in the mempool. + if (!InMempool()) + return false; + + // Trusted if all inputs are from us and are in the mempool: + BOOST_FOREACH(const CTxIn& txin, tx->vin) + { + // Transactions not sent by us: not trusted + const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); + if (parent == NULL) + return false; + const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) + return false; + } + return true; +} + +bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const +{ + CMutableTransaction tx1 = *this->tx; + CMutableTransaction tx2 = *_tx.tx; + for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript(); + for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript(); + return CTransaction(tx1) == CTransaction(tx2); +} + +std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) +{ + std::vector result; + + LOCK(cs_wallet); + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast if newer than nTime: + if (wtx.nTimeReceived > nTime) + continue; + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + if (wtx.RelayWalletTransaction(connman)) + result.push_back(wtx.GetHash()); + } + return result; +} + +void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + if (GetTime() < nNextResend || !fBroadcastTransactions) + return; + bool fFirst = (nNextResend == 0); + nNextResend = GetTime() + GetRand(30 * 60); + if (fFirst) + return; + + // Only do it if there's been a new block since last time + if (nBestBlockTime < nLastResend) + return; + nLastResend = GetTime(); + + // Rebroadcast unconfirmed txes older than 5 minutes before the last + // block was found: + std::vector relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman); + if (!relayed.empty()) + LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); +} + + + + + + + + +CAmount CWallet::GetDeposit() const +{ + CAmount nTotal = 0; + std::string strPublicKey; + uint160 pkhash; + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetLockCredit(); + + } + } + + return nTotal; +} +int CWallet::GetDepthofJoinTX() +{ + int depth = 0; + std::string strPublicKey; + uint256 hash; + uint160 pkhash; + if (CDpocInfo::Instance().GetLocalAccount(strPublicKey)) + { + pkhash.SetHex(strPublicKey); + hash = CConsensusAccountPool::Instance().GetTXhashBypkhash(pkhash); + if (hash.IsNull()) + return depth; + if (mapWallet.count(hash)>0) + { + const CWalletTx* pcoin = &mapWallet[hash]; + return pcoin->GetDepthInMainChain(); + } + + } + return depth; + +} +CAmount CWallet::GetBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->GetDepthInMainChain() >= nTxConfirmTarget && pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableCredit(); + + } + } + + return nTotal; +} +CAmount CWallet::GetUnconfirmedBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + // if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) + if (pcoin->GetDepthInMainChain() < nTxConfirmTarget || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())) + nTotal += pcoin->GetAvailableCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetImmatureBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetWatchOnlyBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + + return nTotal; +} + +CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetImmatureWatchOnlyBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); + } + } + return nTotal; +} + +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + const CWalletTx* pcoin = &(*it).second; + +// if (!CheckIPCFinalTx(*pcoin)) +// continue; + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } +} +//add by xxy +void CWallet::AvailableNormalCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + const CWalletTx* pcoin = &(*it).second; + + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + + if (nDepth < nTxConfirmTarget) + continue; + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))&& + (pcoin->tx->vout[i].txType == TXOUT_NORMAL || (pcoin->tx->vout[i].txType == TXOUT_CAMPAIGN && pcoin->tx->vout[i].devoteLabel.ExtendType == TYPE_CONSENSUS_REGISTER && + CConsensusAccountPool::Instance().IsAviableUTXO(pcoin->tx->GetHash())))) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } + +} +void CWallet::AvailableIPCCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + + const CWalletTx* pcoin = &(*it).second; + + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue >= 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))&& + (pcoin->tx->vout[i].txType == TXOUT_IPCOWNER || pcoin->tx->vout[i].txType == TXOUT_IPCAUTHORIZATION)) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } +} +void CWallet::ListTokenBalance(std::map& TokenList) +{ + TokenList.clear(); + UpdateTokenBalanceList(); + std::map::iterator it; + for (it = TokenValueMap.begin(); it != TokenValueMap.end();it++) + { + TokenList.insert(std::make_pair(it->first,it->second)); + } + return; +} +uint8_t CWallet::GetAccuracyBySymbol(std::string& tokensymbol) +{ + if (tokenDataMap.count(tokensymbol) == 0) + return 10; + return tokenDataMap[tokensymbol].accuracy; +} +uint32_t CWallet::GetIssueDateBySymbol(std::string& tokensymbol) +{ + if (tokenDataMap.count(tokensymbol) == 0) + return 0; + return tokenDataMap[tokensymbol].issueDate; +} +typedef std::multimap::iterator sit; +bool CWallet::GetSymbolbalance(std::string& tokensymbol, uint64_t& value) +{ + value = 0; + std::vector vAvailableTokenCoins; + const CCoinControl *coinControl = NULL; + AvailableTokenCoins(vAvailableTokenCoins, true, coinControl); + uint8_t txtype = -1; + std::string strsymbol = ""; + uint64_t nvalue = 0; + bool isFind = false; + for (const auto& coin : vAvailableTokenCoins) + { + if (coin.tx->GetDepthInMainChain()tx->vout[coin.i].txType; + if (txtype == 4) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenRegLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenRegLabel.totalCount; + } + else if (txtype == 5) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenLabel.value; + } + if (strsymbol == tokensymbol) //Symbol symbol is consistent + { + isFind = true; + value += nvalue; + if (txtype == 4) //There could be only one token registered + return isFind; + } + if (tokensymbol != strsymbol) + continue; + } + + return isFind; +} +bool CWallet::IsHaveTheTokensymbol(std::string&tokensymbol) +{ + std::string str = ""; + for (unsigned int i = 0; i < TokensymbolList.size(); i++) + { + str = TokensymbolList[i]; + if (tokensymbol == str) + { + return true; + } + } + return false; +} +void CWallet::UpdateTokenBalanceList( ) +{ + + std::vector vAvailableTokenCoins; + std::string strsymbol = ""; + std::string cursymbol = ""; + uint8_t txtype = -1; + uint64_t balance = 0; + uint64_t nvalue = 0; + const CCoinControl *coinControl = NULL; + AvailableTokenCoins(vAvailableTokenCoins,true, coinControl); + for (const auto& coin : vAvailableTokenCoins) + { + txtype = coin.tx->tx->vout[coin.i].txType; + if (txtype == 4) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenRegLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenRegLabel.totalCount; + } + else if (txtype == 5) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenLabel.value; + } + if (!IsHaveTheTokensymbol(strsymbol)) //If not, add it + { + TokensymbolList.push_back(strsymbol); + } + + + } + TokenValueMap.clear(); + if (TokensymbolList.size()) + { + for (unsigned int i = 0; i < TokensymbolList.size(); i++) + { + cursymbol = TokensymbolList[i]; + balance = 0; //For a symbol, the balance is 0 + for (const auto& coin : vAvailableTokenCoins) + { + txtype = coin.tx->tx->vout[coin.i].txType; + if (txtype == 4) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenRegLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenRegLabel.totalCount; + } + else if (txtype == 5) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenLabel.value; + } + if (cursymbol != strsymbol) + continue; + balance += nvalue; + } + TokenValueMap.insert(std::make_pair(cursymbol, balance)); + } + } + return; +} +void CWallet::AvailableTokenCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + const CWalletTx* pcoin = &(*it).second; + + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue >= 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))&& + (pcoin->tx->vout[i].txType == TXOUT_TOKEN || pcoin->tx->vout[i].txType == TXOUT_TOKENREG))// + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } +} +//end +static void ApproximateBestSubset(vector > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue, + vector& vfBest, CAmount& nBest, int iterations = 1000) +{ + vector vfIncluded; + + vfBest.assign(vValue.size(), true); + nBest = nTotalLower; + + FastRandomContext insecure_rand; + + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + CAmount nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (unsigned int i = 0; i < vValue.size(); i++) + { + //The solver here uses a randomized algorithm, + //the randomness serves no real security purpose but is just + //needed to prevent degenerate behavior and it is important + //that the rng is fast. We do not use a constant random sequence, + //because there may be some privacy improvement by making + //the selection random. + if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } +} + +bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, vector vCoins, + set >& setCoinsRet, CAmount& nValueRet) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits::max(); + coinLowestLarger.second.first = NULL; + vector > > vValue; + CAmount nTotalLower = 0; + + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(const COutput &output, vCoins) + { + if (!output.fSpendable) + continue; + + const CWalletTx *pcoin = output.tx; + + if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) + continue; + + if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors)) + continue; + + int i = output.i; + CAmount n = pcoin->tx->vout[i].nValue; + + pair > coin = make_pair(n,make_pair(pcoin, i)); + + if (n == nTargetValue) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } + else if (n < nTargetValue + MIN_CHANGE) + { + vValue.push_back(coin); + nTotalLower += n; + } + else if (n < coinLowestLarger.first) + { + coinLowestLarger = coin; + } + } + + if (nTotalLower == nTargetValue) + { + for (unsigned int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } + + if (nTotalLower < nTargetValue) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + // Solve subset sum by stochastic approximation + std::sort(vValue.begin(), vValue.end(), CompareValueOnly()); + std::reverse(vValue.begin(), vValue.end()); + vector vfBest; + CAmount nBest; + + ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) + ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest); + + // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, + // or the next bigger coin is closer), return the bigger coin + if (coinLowestLarger.second.first && + ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest)) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + LogPrint("selectcoins", "SelectCoins() best subset: "); + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first)); + LogPrint("selectcoins", "total %s\n", FormatMoney(nBest)); + } + + return true; +} + +bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const +{ + vector vCoins(vAvailableCoins); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + if (!out.fSpendable) + continue; + nValueRet += out.tx->tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + // calculate value from preset inputs and store them + set > setPresetCoins; + CAmount nValueFromPresetInputs = 0; + + std::vector vPresetInputs; + if (coinControl) + coinControl->ListSelected(vPresetInputs); + BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) + { + map::const_iterator it = mapWallet.find(outpoint.hash); + if (it != mapWallet.end()) + { + const CWalletTx* pcoin = &it->second; + // Clearly invalid input, fail + if (pcoin->tx->vout.size() <= outpoint.n) + return false; + nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; + setPresetCoins.insert(make_pair(pcoin, outpoint.n)); + } else + return false; // TODO: Allow non-wallet inputs + } + + // remove preset inputs from vCoins + for (vector::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) + { + if (setPresetCoins.count(make_pair(it->tx, it->i))) + it = vCoins.erase(it); + else + ++it; + } + + size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); + bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + + bool res = nTargetValue <= nValueFromPresetInputs || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits::max(), vCoins, setCoinsRet, nValueRet)); + + // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset + setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); + + // add preset inputs to the total value selected + nValueRet += nValueFromPresetInputs; + + return res; +} +//add by xxy +bool CWallet::SelectNormalCoins(const vector& vAvailableCoins, const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const +{ + vector vCoins(vAvailableCoins); + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + if (!out.fSpendable) + continue; + nValueRet += out.tx->tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + // calculate value from preset inputs and store them + set > setPresetCoins; + CAmount nValueFromPresetInputs = 0; + + std::vector vPresetInputs; + + if (coinControl) + coinControl->ListSelected(vPresetInputs); + BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) + { + map::const_iterator it = mapWallet.find(outpoint.hash); + if (it != mapWallet.end()) + { + const CWalletTx* pcoin = &it->second; + // Clearly invalid input, fail + if (pcoin->tx->vout.size() <= outpoint.n) + return false; + nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; + setPresetCoins.insert(make_pair(pcoin, outpoint.n)); + } + else + return false; // TODO: Allow non-wallet inputs + } + + // remove preset inputs from vCoins + + for (vector::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) + { + if (setPresetCoins.count(make_pair(it->tx, it->i))) + it = vCoins.erase(it); + else + ++it; + } + size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); + bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + + bool res = nTargetValue <= nValueFromPresetInputs || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength / 3), vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength / 2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits::max(), vCoins, setCoinsRet, nValueRet)); + + // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset + setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); + + // add preset inputs to the total value selected + nValueRet += nValueFromPresetInputs; + + return res; +} + +bool CWallet::SelectTokenCoins(const std::vector& vAvailableCoins, std::set >& setCoinsRet, std::string & symbol, uint64_t& nTokenValue, const CCoinControl *coinControl) const +{ + vector vCoins(vAvailableCoins); + uint8_t txtype = -1; + uint64_t tempValue = 0; + + //Go through it and see if you can find only one tokencoins to trade and find and return + tempValue = nTokenValue; + BOOST_FOREACH(const COutput& out, vCoins) + { + if (!out.fSpendable) + continue; + //Eight confirmations can be spent + if (out.nDepth < nTxConfirmTarget) + continue; + // + txtype = out.tx->tx->vout[out.i].txType; + if (txtype == 4) + { + if (symbol != out.tx->tx->vout[out.i].tokenRegLabel.getTokenSymbol()) + continue; + if (out.tx->tx->vout[out.i].tokenRegLabel.totalCount >= nTokenValue) + { + setCoinsRet.insert(make_pair(out.tx, out.i)); + return true; + } + return false; + } + if (txtype == 5) + { + if (symbol != out.tx->tx->vout[out.i].tokenLabel.getTokenSymbol()) + continue; + if (out.tx->tx->vout[out.i].tokenLabel.value >= nTokenValue) + { + //clear before + setCoinsRet.clear(); + setCoinsRet.insert(make_pair(out.tx, out.i)); + return true; + } + else if (out.tx->tx->vout[out.i].tokenLabel.value < tempValue) + { + setCoinsRet.insert(make_pair(out.tx, out.i)); + tempValue = tempValue - out.tx->tx->vout[out.i].tokenLabel.value; + } + else + { + setCoinsRet.insert(make_pair(out.tx, out.i)); + return true; + } + } + } + + return false; + +} +//end +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange) +{ + vector vecSend; + + // Turn the txout set into a CRecipient vector + for (size_t idx = 0; idx < tx.vout.size(); idx++) + { + const CTxOut& txOut = tx.vout[idx]; + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; + vecSend.push_back(recipient); + } + + CCoinControl coinControl; + coinControl.destChange = destChange; + coinControl.fAllowOtherInputs = true; + coinControl.fAllowWatchOnly = includeWatching; + coinControl.fOverrideFeeRate = overrideEstimatedFeeRate; + coinControl.nFeeRate = specificFeeRate; + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + coinControl.Select(txin.prevout); + + CReserveKey reservekey(this); + CWalletTx wtx; + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false)) + return false; + + if (nChangePosInOut != -1) + tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); + + // Copy output sizes from new transaction; they may have had the fee subtracted from them + for (unsigned int idx = 0; idx < tx.vout.size(); idx++) + tx.vout[idx].nValue = wtx.tx->vout[idx].nValue; + + // Add new txins (keeping original txin scriptSig/order) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (!coinControl.IsSelected(txin.prevout)) + { + tx.vin.push_back(txin); + + if (lockUnspents) + { + LOCK2(cs_main, cs_wallet); + LockCoin(txin.prevout); + } + } + } + + // optionally keep the change output key + if (keepReserveKey) + reservekey.KeepKey(); + + return true; +} +bool CWallet::address2pkhash(std::string& address, uint160& pkhash) +{ + //uint160 pkhash = 0; + CBitcoinAddress Caddress(address); + if (!Caddress.IsValid()) + return false; + const CTxDestination &desaddress = Caddress.Get(); + CKeyID id = *get(&desaddress); + pkhash = id; + return true; +} +bool CWallet::JoinCampaign(const CTxDestination &address, CAmount deposi, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) + +{ + + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + + //First verify that the bottom layer is already in charge, and if the bottom is in the bookkeeping, it does not create a transaction + std::string currentAccountHex; + + CKeyID id = *get(&address); + uint160 devoterhash = id; + if (CConsensusAccountPool::Instance().verifyPkInCandidateListByIndex(chainActive.Tip(), id)) + { + std::cout << " 111111111111111111 "<< std::endl; + std::string curaddress = CBitcoinAddress(CKeyID(devoterhash)).ToString(); + strFailReason = "Address " + curaddress + " already join the campaign before,can't join now"; + return false; + } + if (CDpocInfo::Instance().GetLocalAccount(currentAccountHex)) + { + uint160 devoterhash; + devoterhash.SetHex(currentAccountHex); + std::string curaddress = CBitcoinAddress(CKeyID(devoterhash)).ToString(); + strFailReason = "Address " + curaddress + " already join the campaign"; + return false; + } + + + //Construct system payee - change to destination address + //CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(Params().system_account_address).Get()); + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(address).Get()); + vector vecSend; + CRecipient recipient = { scriptPubKey, deposi, false}; + vecSend.push_back(recipient); + + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + //Construct a txout for trading + DevoteLabel joinLabel; + joinLabel.ExtendType = TYPE_CONSENSUS_REGISTER; + + joinLabel.hash = id; + + CTxOut txout(recipient.nAmount, recipient.scriptPubKey , joinLabel); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + //Add an output that supports exit bookkeeping 1 ipc + CAmount nNewoutValue = 1*COIN; + CTxOut newout(nNewoutValue, recipient.scriptPubKey); + txNew.vout.push_back(newout); + nValueToSelect += nNewoutValue; + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at random position: + //nChangePosInOut = GetRandInt(txNew.vout.size() + 1); + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::ExitCampaign(CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + + //Get the pubkey that is currently in charge at the bottom and return directly if it is empty + std::string currentCampaignAccountPubkeyStr; + int ret = CDpocInfo::Instance().GetLocalAccount(currentCampaignAccountPubkeyStr); + if (ret < 0) + { + std::cout << "Not join the campaign yet\n"; + strFailReason = _("Not join the campaign yet"); + return false; + } + CKeyID id; + id.SetHex(currentCampaignAccountPubkeyStr); + CTxDestination address(id); + + //Construct system payee + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(Params().system_account_address).Get()); + vector vecSend; + CRecipient recipient = { scriptPubKey, 0, false }; + vecSend.push_back(recipient); + + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector tmpAvailableCoins; + std::vector vAvailableCoins; + AvailableNormalCoins(tmpAvailableCoins, true, coinControl); + + //Filtering is only the source UTXO that specifies the exit address + BOOST_FOREACH(const COutput& out, tmpAvailableCoins) { + + CTxDestination utxoaddress; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, utxoaddress); + + //if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + if (!fValidAddress) + continue; + if (utxoaddress != address) + continue; + + vAvailableCoins.push_back(out); + } + if (vAvailableCoins.size() == 0) + { + strFailReason = _("The Address which you want to ExitCampaign must have some money!"); + return false; + } + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + //Construct a txout from the transaction + DevoteLabel exitLabel; + exitLabel.ExtendType = TYPE_CONSENSUS_QUITE; + CKeyID id = *get(&address); + exitLabel.hash = id; + + printf("pubkey hash to exit:%s\n", exitLabel.hash.GetHex().c_str()); + + CTxOut txout(recipient.nAmount, exitLabel); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::PunishRequest(const CTxDestination &address, const std::string evdience, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + + + //Construct system payee + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(Params().system_account_address).Get()); + vector vecSend; + CRecipient recipient = { scriptPubKey, 0, false }; + vecSend.push_back(recipient); + + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + //Construct the penalty application transaction + CKeyID id = *get(&address); + CTxOut txout(id, evdience); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + for (const auto& pcoin : setCoins) + { + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + for (const auto& pcoin : setCoins) + { + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin()+nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin()+nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin()+nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//add by xxy +bool CWallet::CreateNormalTransaction(const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE*2); + { + set > setCoins; + + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//StrReglabel: know the IPCLabel of the register; VecSend: recipient information +bool CWallet::CreateIPCRegTransaction(std::string& strReglabel, const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + if (strReglabel.size() == 0) + { + strFailReason = _("IPCRegTransaction must have IPCLabel"); + return false; + } + UniValue JsonIpclabel; + bool isNUll = JsonIpclabel.read(strReglabel); + IPCLabel label; + UniValue o = find_value(JsonIpclabel, "ExtendType"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's ExtendType err"); + return false; + } + label.ExtendType = (uint8_t)o.get_int(); + label.startTime = timeService.GetCurrentTimeSeconds(); + label.stopTime = 0; + label.reAuthorize = 1; + label.uniqueAuthorize = 0; + o = find_value(JsonIpclabel, "hashLen"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's hashLen err"); + return false; + } + label.hashLen = (uint8_t)o.get_int(); + + o = find_value(JsonIpclabel, "hash"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's hash err"); + return false; + } + std::string ipchash = o.get_str(); + if (ipchash.length() != 32) + { + strFailReason = _("this ipchash length is Wrongful "); + return false; + } + label.hash.SetHex(ipchash); + + o = find_value(JsonIpclabel, "labelTitle"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's labelTitle err"); + return false; + } + label.labelTitle = o.get_str(); + o = find_value(JsonIpclabel, "txLabel"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's txLabel err"); + return false; + } + + std::string TxLabel = o.get_str(); + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + //bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + + + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, TXOUT_IPCOWNER, label, TxLabel); + + txNew.vout.push_back(txout); + break; + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//Knowledge transfer transaction txid: transaction id for input; Index: the corresponding Index in the pre output; VecSend: recipient information +bool CWallet::CreateIPCSendTransaction(std::string& txid, int Index, const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + UniValue uTxid = UniValue(txid); + uint256 hash = ParseHashV(uTxid, "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + + IPCLabel ipcSendLabel; + ipcSendLabel = tx->vout[Index].ipcLabel; + ipcSendLabel.startTime = timeService.GetCurrentTimeSeconds(); + std::string TxLabel = ""; + + { + //Check the incoming txid index for your unspent knowledge + std::vector vAvailableIPCCoins; + AvailableIPCCoins(vAvailableIPCCoins, true, coinControl); + bool isFind = false; + std::string cointxid = ""; + BOOST_FOREACH(const COutput& coin, vAvailableIPCCoins) + { + cointxid = coin.tx->tx->GetHash().GetHex(); + if (cointxid == txid && coin.i == Index && coin.tx->GetDepthInMainChain() >= DEFAULT_TX_CONFIRM_TARGET) + isFind = true; + } + if (!isFind) + { + strFailReason = _("This coins you can't spent now!"); + return false; + } + + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + // bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, TXOUT_IPCOWNER, ipcSendLabel, TxLabel); + + txNew.vout.push_back(txout); + } + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + txNew.vin.push_back(CTxIn(tx->GetHash(), Index, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + //Add fake signature, calculate service charge + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + int iNewVinsize = txNew.vin.size(); + if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + { + strFailReason = _("Signing Addvin of transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, iNewVinsize - 1, sigdata); + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added ipc type input also needs to be signed + int itxNewVinsize = txNew.vin.size(); + + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, itxNewVinsize - 1, tx->vout[Index].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, itxNewVinsize-1, sigdata); + } + + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +//To authorize and re-authorize transactions; Txid: the transaction id of the input; Index: the corresponding Index in the pre output; VecSend: recipient information; IPCAuthorizeLabel: IP licensed or reauthorized IPClabel +bool CWallet::CreateIPCAuthorizationTransaction(std::string& txid, int Index, const vector& vecSend, std::string& IPCAuthorizeLabel, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + UniValue uTxid = UniValue(txid); + uint256 hash = ParseHashV(uTxid, "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + + if (IPCAuthorizeLabel.size() == 0) + { + strFailReason = _("IPCAuthorizeTransaction must have IPCLabel"); + return false; + } + UniValue JsonIpclabel; + bool isRightRead = JsonIpclabel.read(IPCAuthorizeLabel); + IPCLabel label; + + UniValue o = find_value(JsonIpclabel, "startTime"); + if (o.isNull()||o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's startTime err"); + return false; + } + label.startTime = o.get_int32(); + o = find_value(JsonIpclabel, "stopTime"); + if (o.isNull()||o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's stopTime err"); + return false; + } + label.stopTime = o.get_int32(); + o = find_value(JsonIpclabel, "reAuthorize"); + if (o.isNull()|| o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's reAuthorize err"); + return false; + } + label.reAuthorize = (uint8_t)o.get_int(); + + o = find_value(JsonIpclabel, "uniqueAuthorize"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's uniqueAuthorize err"); + return false; + } + label.uniqueAuthorize = (uint8_t)o.get_int(); + label.uniqueAuthorize = 0; + bool IsUniqueAuthorize = (bool)(label.uniqueAuthorize); + label.ExtendType = tx->vout[Index].ipcLabel.ExtendType; + label.hashLen = tx->vout[Index].ipcLabel.hashLen; + label.hash = tx->vout[Index].ipcLabel.hash; + label.labelTitle = tx->vout[Index].ipcLabel.labelTitle; + std::string TxLabel = ""; + + { + //Check the incoming txid index for your unspent knowledge + std::vector vAvailableIPCCoins; + AvailableIPCCoins(vAvailableIPCCoins, true, coinControl); + bool isFind = false; + std::string cointxid = ""; + BOOST_FOREACH(const COutput& coin, vAvailableIPCCoins) + { + cointxid = coin.tx->tx->GetHash().GetHex(); + if (cointxid == txid && coin.i == Index) + isFind = true; + } + if (!isFind) + { + strFailReason = _("This coins you have spent already!"); + return false; + } + + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + // bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, TXOUT_IPCAUTHORIZATION, label, TxLabel); + + txNew.vout.push_back(txout); + //Add the output of the change of ownership + + CTxOut txinpretxout ; + + if (!IsUniqueAuthorize) //Non-exclusive authorization + { + txinpretxout = tx->vout[Index]; + } + else + { + txinpretxout = tx->vout[Index]; + txinpretxout.txLabel = ""; + } + txNew.vout.push_back(txinpretxout); + + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + //end***************************************************************************************************************** + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + //Join an input + txNew.vin.push_back(CTxIn(tx->GetHash(), Index, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + //Add fake signature, calculate service charge + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + int iNewVinsize = txNew.vin.size(); + if(!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + { + strFailReason = _("Signing Addvin of transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, iNewVinsize-1, sigdata); + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added ipc type input also needs to be signed + int itxNewVinsize = txNew.vin.size(); + + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, itxNewVinsize - 1, tx->vout[Index].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, itxNewVinsize - 1, sigdata); + } + nIn++; + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//To create a token registration transaction; StrReglabel: TokenRegLabel; VecSend: recipient information +bool CWallet::CreateTokenRegTransaction(std::string& strReglabel, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl , bool sign ) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + break; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + if (strReglabel.size() == 0) + { + strFailReason = _("IPCRegTransaction must have IPCTokenLabel"); + return false; + } + UniValue JsonIpclabel; + bool isNUll = JsonIpclabel.read(strReglabel); + + TokenRegLabel label; + UniValue o = find_value(JsonIpclabel, "TokenSymbol"); + if (o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's TokenSymbol err"); + return false; + } + std::string strTokenSymbol = o.get_str(); + if (strTokenSymbol == "") + { + strFailReason = _(" IPCTokenLabel's TokenSymbol is NULL"); + return false; + } + if (strTokenSymbol.length()>8) + { + strFailReason = _(" IPCTokenLabel's TokenSymbol is longer than 8"); + return false; + } + strncpy((char*)(label.TokenSymbol), strTokenSymbol.c_str(), strTokenSymbol.length()); + o = find_value(JsonIpclabel, "value"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's value err"); + return false; + } + label.value = o.get_int64(); + o = find_value(JsonIpclabel, "hash"); + if ( o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's hash err"); + return false; + } + label.hash.SetHex(o.get_str()); + o = find_value(JsonIpclabel, "label"); + if (o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's label err"); + return false; + } + std::string strlabel = o.get_str(); + if (strlabel.length() > 16) + { + strFailReason = _(" IPCTokenLabel's Label is longer than 16"); + return false; + } + strncpy((char*)(label.label), strlabel.c_str(), strlabel.length()); + o = find_value(JsonIpclabel, "issueDate"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's issueDate err"); + return false; + } + label.issueDate = o.get_int32(); + o = find_value(JsonIpclabel, "totalCount"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's totalCount err"); + return false; + } + label.totalCount = o.get_int64(); + o = find_value(JsonIpclabel, "accuracy"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's accuracy err"); + return false; + } + label.accuracy = o.get_int(); + o = find_value(JsonIpclabel, "txLabel"); + if ( o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's txLabel err"); + return false; + } + std::string TxLabel = o.get_str(); + CAmount nDefalutFee = 100 * COIN; + nValue += nDefalutFee; + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, label, TxLabel); + txNew.vout.push_back(txout); + break; + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//To create a token trade; Tokensymbol: token symbol; TokenValue: token trading details; VecSend: recipient information +bool CWallet::CreateTokenTransaction(std::string& tokensymbol, uint64_t TokenValue, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + uint64_t curTokenBalance = 0; + + GetSymbolbalance(tokensymbol, curTokenBalance); + + if (TokenValue > curTokenBalance) + { + strFailReason = _("The Tokenvalue is too big,you have not enough Tokencoins."); + return false; + } + + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + break; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + std::string txLabel = ""; + TokenLabel tokenlabel; + memcpy((char*)(tokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(tokenlabel.TokenSymbol)); + tokenlabel.value = TokenValue; + if (tokenDataMap.count(tokenlabel.getTokenSymbol()) == 0) + { + strFailReason = _("Can't found 'accuracy' of the Token"); + return false; + } + tokenlabel.accuracy = tokenDataMap[tokenlabel.getTokenSymbol()].accuracy; + { + + set > setCoins; + set > setTokenCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + std::vector vAvailableTokenCoins; + AvailableTokenCoins(vAvailableTokenCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, tokenlabel,txLabel); + txNew.vout.push_back(txout); + break; + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + //Join an input + //Find the appropriate utxo input in the current sybmol balance + int icurinsize = txNew.vin.size(); + setTokenCoins.clear(); + if (!SelectTokenCoins(vAvailableTokenCoins, setTokenCoins, tokensymbol, TokenValue)) + { + strFailReason = _("Can't select enough TokenCoins!"); + return false; + } + bool getVinscriptPubKey = false; + uint64_t TotalvalueVin = 0; + CScript newscriptPubKey; + for (const auto& coin : setTokenCoins) + { + if (coin.first->tx->vout[coin.second].txType == 4) + TotalvalueVin += coin.first->tx->vout[coin.second].tokenRegLabel.totalCount; + else + TotalvalueVin += coin.first->tx->vout[coin.second].tokenLabel.value; + + if (!getVinscriptPubKey) + { + newscriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + getVinscriptPubKey = true; + } + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + } + if (!DummySignTx(txNew, setTokenCoins, icurinsize)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + //After finding enough input, you need to compute the add token change + TokenLabel stokenlabel; + for (const auto& recipient : vecSend) + { + memcpy((char*)(stokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(stokenlabel.TokenSymbol)); + stokenlabel.value = TotalvalueVin - TokenValue; + stokenlabel.accuracy = tokenlabel.accuracy; + + if (stokenlabel.value > 0) + { + CTxOut txsout(recipient.nAmount, newscriptPubKey, stokenlabel, txLabel); + int ntokenchangepos = txNew.vout.size() - 1; + vector::iterator tposition = txNew.vout.begin() + ntokenchangepos; + if (ntokenchangepos > 0) + txNew.vout.insert(tposition, txsout); + else + txNew.vout.push_back(txsout); + } + break; + } + + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added Token type input also needs to be signed + for (const auto& coin : setTokenCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::CreateTokenTransactionM(std::string& tokensymbol, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + uint64_t curTokenBalance = 0; + uint64_t TokenValue = 0; + GetSymbolbalance(tokensymbol, curTokenBalance); + + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0 || TokenValue < 0 ||recipient.nvalue < 0) + { + strFailReason = _("Transaction amounts/tokenvalue must not be negative"); + return false; + } + nValue += recipient.nAmount; + TokenValue += recipient.nvalue; + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + if (TokenValue > curTokenBalance) + { + strFailReason = _("The Tokenvalue is too big,you have not enough Tokencoins."); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + std::string txLabel = ""; + TokenLabel tokenlabel; + memcpy((char*)(tokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(tokenlabel.TokenSymbol)); + tokenlabel.value = 0; + if (tokenDataMap.count(tokenlabel.getTokenSymbol()) == 0) + { + strFailReason = _("Can't found 'accuracy' of the Token"); + return false; + } + tokenlabel.accuracy = tokenDataMap[tokenlabel.getTokenSymbol()].accuracy; + { + + set > setCoins; + set > setTokenCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + std::vector vAvailableTokenCoins; + AvailableTokenCoins(vAvailableTokenCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + TokenLabel nowtokenlabel = tokenlabel; + nowtokenlabel.value = recipient.nvalue; + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, nowtokenlabel, txLabel); + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + //Join an input + //Find the appropriate utxo input in the current sybmol balance + int icurinsize = txNew.vin.size(); + setTokenCoins.clear(); + if (!SelectTokenCoins(vAvailableTokenCoins, setTokenCoins, tokensymbol, TokenValue)) + { + strFailReason = _("Can't select enough TokenCoins!"); + return false; + } + bool getVinscriptPubKey = false; + uint64_t TotalvalueVin = 0; + CScript newscriptPubKey; + for (const auto& coin : setTokenCoins) + { + if (coin.first->tx->vout[coin.second].txType == 4) + TotalvalueVin += coin.first->tx->vout[coin.second].tokenRegLabel.totalCount; + else + TotalvalueVin += coin.first->tx->vout[coin.second].tokenLabel.value; + + if (!getVinscriptPubKey) + { + newscriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + getVinscriptPubKey = true; + } + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + } + if (!DummySignTx(txNew, setTokenCoins, icurinsize)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + //After finding enough input, you need to compute the add token change + TokenLabel stokenlabel; + + { + memcpy((char*)(stokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(stokenlabel.TokenSymbol)); + stokenlabel.value = TotalvalueVin - TokenValue; + stokenlabel.accuracy = tokenlabel.accuracy; + + if (stokenlabel.value > 0) + { + CAmount nstokenamount = 0; + CTxOut txsout(nstokenamount, newscriptPubKey, stokenlabel, txLabel); + int ntokenchangepos = txNew.vout.size() - 1; + vector::iterator tposition = txNew.vout.begin() + ntokenchangepos; + if (ntokenchangepos > 0) + txNew.vout.insert(tposition, txsout); + else + txNew.vout.push_back(txsout); + } + + } + + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added Token type input also needs to be signed + for (const auto& coin : setTokenCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//Submit a bid for the election +bool CWallet::CommitJoinCampaignTransaction(const CTxDestination &address, CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state) +{ + { + LOCK2(cs_main, cs_wallet); + LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); + + + if (fBroadcastTransactions) + { + // Broadcast + if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { + LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); + // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. + return false; + } + else { + + { + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Notify that old coins are spent + BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin) + { + CWalletTx &coin = mapWallet[txin.prevout.hash]; + coin.BindWallet(this); + NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + } + } + + // Track how many getdata requests our transaction gets + mapRequestCount[wtxNew.GetHash()] = 0; + + wtxNew.RelayWalletTransaction(connman); + } + } + } + + //After the transaction is successfully sent, the underlying system interface is invoked to set the public key + CKeyID id = *get(&address); + std::string sethexstring = id.GetHex(); + int nRet = CDpocInfo::Instance().SetLocalAccount(sethexstring); + if (0 == nRet) + { + CDpocInfo::Instance().setJoinCampaign(true); + CDpocInfo::Instance().setLocalAccoutVar(sethexstring); + } + + LogPrintf("[CWallet::CommitJoinCampaignTransaction] Sets the underlying public key return value %d\n", nRet); + + //Set the consensus status to be normal 0 + std::string strStatus("0"); + bool bRet = CDpocInfo::Instance().SetConsensusStatus(strStatus, sethexstring); + LogPrintf("[CWallet::CommitJoinCampaignTransaction] Set the consensus return value %d\n", bRet); + + return true; +} + +//Gets the current node bookkeeping reward +bool CWallet::GetCurrentRewards(std::vector& timelist, std::vector& valuelist) +{ + timelist.clear(); + valuelist.clear(); + std::vector listInfo; + int nSum; + bool ret = CDpocInfo::Instance().GetBlockInfo(listInfo, nSum); + if (!ret) return ret; + + BOOST_FOREACH(const BlockInfo &info, listInfo) + { + timelist.push_back(info.strTime); + valuelist.push_back(info.strFee); + } + return true; +} + +//end +/** + * Call after CreateTransaction unless you want to abort + */ +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state) +{ + { + LOCK2(cs_main, cs_wallet); + LogPrintf("CommitTransaction: %s\n", wtxNew.GetHash().GetHex()); + + + if (fBroadcastTransactions) + { + // Broadcast + if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { + LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); + // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. + + return false; + } else { + + { + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Notify that old coins are spent + BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin) + { + CWalletTx &coin = mapWallet[txin.prevout.hash]; + coin.BindWallet(this); + NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + } + } + + // Track how many getdata requests our transaction gets + mapRequestCount[wtxNew.GetHash()] = 0; + + wtxNew.RelayWalletTransaction(connman); + } + } + } + return true; +} + +void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list& entries) { + CWalletDB walletdb(strWalletFile); + return walletdb.ListAccountCreditDebit(strAccount, entries); +} + +bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry) +{ + CWalletDB walletdb(strWalletFile); + + return AddAccountingEntry(acentry, &walletdb); +} + +bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwalletdb) +{ + if (!pwalletdb->WriteAccountingEntry_Backend(acentry)) + return false; + + laccentries.push_back(acentry); + CAccountingEntry & entry = laccentries.back(); + wtxOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); + + return true; +} + +CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) +{ + return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); +} + +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) +{ + // payTxFee is the user-set global for desired feerate + return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes)); +} + +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee) +{ + CAmount nFeeNeeded = targetFee; + // User didn't set: use -txconfirmtarget to estimate... + if (nFeeNeeded == 0) { + int estimateFoundTarget = nConfirmTarget; + nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes); + // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee + if (nFeeNeeded == 0) + nFeeNeeded = fallbackFee.GetFee(nTxBytes); + } + // prevent user from paying a fee below minRelayTxFee or minTxFee + nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); + // But always obey the maximum + if (nFeeNeeded > maxTxFee) + nFeeNeeded = maxTxFee; + return nFeeNeeded; +} + + + + +DBErrors CWallet::LoadWallet(bool& fFirstRunRet) +{ + if (!fFileBacked) + return DB_LOAD_OK; + fFirstRunRet = false; + DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); + if (nLoadWalletRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // that requires a new key. + } + } + + if (nLoadWalletRet != DB_LOAD_OK) + return nLoadWalletRet; + fFirstRunRet = !vchDefaultKey.IsValid(); + + uiInterface.LoadWallet(this); + + return DB_LOAD_OK; +} + +DBErrors CWallet::ZapSelectTx(vector& vHashIn, vector& vHashOut) +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(this, vHashIn, vHashOut); + if (nZapSelectTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // that requires a new key. + } + } + + if (nZapSelectTxRet != DB_LOAD_OK) + return nZapSelectTxRet; + + MarkDirty(); + + return DB_LOAD_OK; + +} + +DBErrors CWallet::ZapWalletTx(std::vector& vWtx) +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx); + if (nZapWalletTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // that requires a new key. + } + } + + if (nZapWalletTxRet != DB_LOAD_OK) + return nZapWalletTxRet; + + return DB_LOAD_OK; +} + + +bool CWallet::SetAddressBook(const CTxDestination& address, const string& strName, const string& strPurpose) +{ + bool fUpdated = false; + { + LOCK(cs_wallet); // mapAddressBook + std::map::iterator mi = mapAddressBook.find(address); + fUpdated = mi != mapAddressBook.end(); + mapAddressBook[address].name = strName; + if (!strPurpose.empty()) /* update purpose only if requested */ + mapAddressBook[address].purpose = strPurpose; + } + NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, + strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); + if (!fFileBacked) + return false; + if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) + return false; + return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); +} + +bool CWallet::DelAddressBook(const CTxDestination& address) +{ + { + LOCK(cs_wallet); // mapAddressBook + + if(fFileBacked) + { + // Delete destdata tuples associated with address + std::string strAddress = CBitcoinAddress(address).ToString(); + BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) + { + CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); + } + } + mapAddressBook.erase(address); + } + + NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); + + if (!fFileBacked) + return false; + CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString()); + return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); +} + +bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) +{ + if (fFileBacked) + { + if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) + return false; + } + vchDefaultKey = vchPubKey; + return true; +} + +/** + * Mark old keypool keys as used, + * and generate all new keys + */ +bool CWallet::NewKeyPool() +{ + { + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + BOOST_FOREACH(int64_t nIndex, setKeyPool) + walletdb.ErasePool(nIndex); + setKeyPool.clear(); + + if (IsLocked()) + return false; + + int64_t nKeys = max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t)0); + for (int i = 0; i < nKeys; i++) + { + int64_t nIndex = i+1; + walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); + setKeyPool.insert(nIndex); + } + LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); + } + return true; +} + +bool CWallet::TopUpKeyPool(unsigned int kpSize) +{ + { + LOCK(cs_wallet); + + if (IsLocked()) + return false; + + CWalletDB walletdb(strWalletFile); + + // Top up key pool + unsigned int nTargetSize; + if (kpSize > 0) + nTargetSize = kpSize; + else + nTargetSize = max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0); + + while (setKeyPool.size() < (nTargetSize + 1)) + { + int64_t nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + throw runtime_error(std::string(__func__) + ": writing generated key failed"); + setKeyPool.insert(nEnd); + LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); + } + } + return true; +} + +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey = CPubKey(); + { + LOCK(cs_wallet); + + if (!IsLocked()) + TopUpKeyPool(); + + // Get the oldest key + if(setKeyPool.empty()) + return; + + CWalletDB walletdb(strWalletFile); + + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error(std::string(__func__) + ": read failed"); + if (!HaveKey(keypool.vchPubKey.GetID())) + throw runtime_error(std::string(__func__) + ": unknown key in key pool"); + assert(keypool.vchPubKey.IsValid()); + LogPrintf("keypool reserve %d\n", nIndex); + } +} + +void CWallet::KeepKey(int64_t nIndex) +{ + // Remove from key pool + if (fFileBacked) + { + CWalletDB walletdb(strWalletFile); + walletdb.ErasePool(nIndex); + } + LogPrintf("keypool keep %d\n", nIndex); +} + +void CWallet::ReturnKey(int64_t nIndex) +{ + // Return to key pool + { + LOCK(cs_wallet); + setKeyPool.insert(nIndex); + } + LogPrintf("keypool return %d\n", nIndex); +} + +bool CWallet::GetKeyFromPool(CPubKey& result) +{ + int64_t nIndex = 0; + CKeyPool keypool; + { + LOCK(cs_wallet); + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + { + if (IsLocked()) return false; + result = GenerateNewKey(); + return true; + } + KeepKey(nIndex); + result = keypool.vchPubKey; + } + return true; +} + +int64_t CWallet::GetOldestKeyPoolTime() +{ + LOCK(cs_wallet); + + // if the keypool is empty, return + if (setKeyPool.empty()) + return GetTime(); + + // load oldest key from keypool, get time and return + CKeyPool keypool; + CWalletDB walletdb(strWalletFile); + int64_t nIndex = *(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); + assert(keypool.vchPubKey.IsValid()); + return keypool.nTime; +} + +std::map CWallet::GetAddressBalances() +{ + map balances; + + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (!pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) + continue; + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) + { + CTxDestination addr; + if (!IsMine(pcoin->tx->vout[i])) + continue; + if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) + continue; + + CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; + + if (!balances.count(addr)) + balances[addr] = 0; + balances[addr] += n; + } + } + } + + return balances; +} + +set< set > CWallet::GetAddressGroupings() +{ + AssertLockHeld(cs_wallet); // mapWallet + set< set > groupings; + set grouping; + + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (pcoin->tx->vin.size() > 0) + { + bool any_mine = false; + // group all input addresses with each other + BOOST_FOREACH(CTxIn txin, pcoin->tx->vin) + { + CTxDestination address; + if(!IsMine(txin)) /* If this input isn't mine, ignore it */ + continue; + if(!ExtractDestination(mapWallet[txin.prevout.hash].tx->vout[txin.prevout.n].scriptPubKey, address)) + continue; + grouping.insert(address); + any_mine = true; + } + + // group change with input addresses + if (any_mine) + { + BOOST_FOREACH(CTxOut txout, pcoin->tx->vout) + if (IsChange(txout)) + { + CTxDestination txoutAddr; + if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) + continue; + grouping.insert(txoutAddr); + } + } + if (grouping.size() > 0) + { + groupings.insert(grouping); + grouping.clear(); + } + } + + // group lone addrs by themselves + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) + if (IsMine(pcoin->tx->vout[i])) + { + CTxDestination address; + if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, address)) + continue; + grouping.insert(address); + groupings.insert(grouping); + grouping.clear(); + } + } + + set< set* > uniqueGroupings; // a set of pointers to groups of addresses + map< CTxDestination, set* > setmap; // map addresses to the unique group containing it + BOOST_FOREACH(set _grouping, groupings) + { + // make a set of all the groups hit by this new group + set< set* > hits; + map< CTxDestination, set* >::iterator it; + BOOST_FOREACH(CTxDestination address, _grouping) + if ((it = setmap.find(address)) != setmap.end()) + hits.insert((*it).second); + + // merge all hit groups into a new single group and delete old groups + set* merged = new set(_grouping); + BOOST_FOREACH(set* hit, hits) + { + merged->insert(hit->begin(), hit->end()); + uniqueGroupings.erase(hit); + delete hit; + } + uniqueGroupings.insert(merged); + + // update setmap + BOOST_FOREACH(CTxDestination element, *merged) + setmap[element] = merged; + } + + set< set > ret; + BOOST_FOREACH(set* uniqueGrouping, uniqueGroupings) + { + ret.insert(*uniqueGrouping); + delete uniqueGrouping; + } + + return ret; +} + +CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CWalletDB walletdb(strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); +} + +CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CAmount nBalance = 0; + + // Tally wallet transactions + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!CheckIPCFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + continue; + + CAmount nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance -= nSent + nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + + return nBalance; +} + +std::set CWallet::GetAccountAddresses(const std::string& strAccount) const +{ + LOCK(cs_wallet); + set result; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, mapAddressBook) + { + const CTxDestination& address = item.first; + const string& strName = item.second.name; + if (strName == strAccount) + result.insert(address); + } + return result; +} + +bool CReserveKey::GetReservedKey(CPubKey& pubkey) +{ + if (nIndex == -1) + { + CKeyPool keypool; + pwallet->ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex != -1) + vchPubKey = keypool.vchPubKey; + else { + return false; + } + } + assert(vchPubKey.IsValid()); + pubkey = vchPubKey; + return true; +} + +void CReserveKey::KeepKey() +{ + if (nIndex != -1) + pwallet->KeepKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CReserveKey::ReturnKey() +{ + if (nIndex != -1) + pwallet->ReturnKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CWallet::GetAllReserveKeys(set& setAddress) const +{ + setAddress.clear(); + + CWalletDB walletdb(strWalletFile); + + LOCK2(cs_main, cs_wallet); + BOOST_FOREACH(const int64_t& id, setKeyPool) + { + CKeyPool keypool; + if (!walletdb.ReadPool(id, keypool)) + throw runtime_error(std::string(__func__) + ": read failed"); + assert(keypool.vchPubKey.IsValid()); + CKeyID keyID = keypool.vchPubKey.GetID(); + if (!HaveKey(keyID)) + throw runtime_error(std::string(__func__) + ": unknown key in key pool"); + setAddress.insert(keyID); + } +} + +void CWallet::UpdatedTransaction(const uint256 &hashTx) +{ + { + LOCK(cs_wallet); + // Only notify UI if this transaction is in this wallet + map::const_iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + NotifyTransactionChanged(this, hashTx, CT_UPDATED); + } +} + +void CWallet::GetScriptForMining(boost::shared_ptr &script) +{ + boost::shared_ptr rKey(new CReserveKey(this)); + CPubKey pubkey; + if (!rKey->GetReservedKey(pubkey)) + return; + + script = rKey; + script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; +} + +void CWallet::LockCoin(const COutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.insert(output); +} + +void CWallet::UnlockCoin(const COutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.erase(output); +} + +void CWallet::UnlockAllCoins() +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.clear(); +} + +bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const +{ + AssertLockHeld(cs_wallet); // setLockedCoins + COutPoint outpt(hash, n); + + return (setLockedCoins.count(outpt) > 0); +} + +void CWallet::ListLockedCoins(std::vector& vOutpts) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + for (std::set::iterator it = setLockedCoins.begin(); + it != setLockedCoins.end(); it++) { + COutPoint outpt = (*it); + vOutpts.push_back(outpt); + } +} + +/** @} */ // end of Actions + +class CAffectedKeysVisitor : public boost::static_visitor { +private: + const CKeyStore &keystore; + std::vector &vKeys; + +public: + CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + + void Process(const CScript &script) { + txnouttype type; + std::vector vDest; + int nRequired; + if (ExtractDestinations(script, type, vDest, nRequired)) { + BOOST_FOREACH(const CTxDestination &dest, vDest) + boost::apply_visitor(*this, dest); + } + } + + void operator()(const CKeyID &keyId) { + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CScriptID &scriptId) { + CScript script; + if (keystore.GetCScript(scriptId, script)) + Process(script); + } + + void operator()(const CNoDestination &none) {} +}; + +void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { + AssertLockHeld(cs_wallet); // mapKeyMetadata + mapKeyBirth.clear(); + + // get birth times for keys with metadata + for (const auto& entry : mapKeyMetadata) { + if (entry.second.nCreateTime) { + mapKeyBirth[entry.first] = entry.second.nCreateTime; + } + } + + // map in which we'll infer heights of other keys + CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin + std::map mapKeyFirstBlock; + std::set setKeys; + GetKeys(setKeys); + BOOST_FOREACH(const CKeyID &keyid, setKeys) { + if (mapKeyBirth.count(keyid) == 0) + mapKeyFirstBlock[keyid] = pindexMax; + } + setKeys.clear(); + + // if there are no such keys, we're done + if (mapKeyFirstBlock.empty()) + return; + + // find first block that affects those keys, if there are any left + std::vector vAffected; + for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { + // iterate over all wallet transactions... + const CWalletTx &wtx = (*it).second; + BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); + if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { + // ... which are already in a block + int nHeight = blit->second->nHeight; + BOOST_FOREACH(const CTxOut &txout, wtx.tx->vout) { + // iterate over all their outputs + CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); + BOOST_FOREACH(const CKeyID &keyid, vAffected) { + // ... and all their affected keys + std::map::iterator rit = mapKeyFirstBlock.find(keyid); + if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) + rit->second = blit->second; + } + vAffected.clear(); + } + } + } + + // Extract block timestamps for those keys + for (std::map::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) + mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off +} + +bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) +{ + if (boost::get(&dest)) + return false; + + mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value); +} + +bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) +{ + if (!mapAddressBook[dest].destdata.erase(key)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key); +} + +bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) +{ + mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); + return true; +} + +bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const +{ + std::map::const_iterator i = mapAddressBook.find(dest); + if(i != mapAddressBook.end()) + { + CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key); + if(j != i->second.destdata.end()) + { + if(value) + *value = j->second; + return true; + } + } + return false; +} + +std::string CWallet::GetWalletHelpString(bool showDebug) +{ + std::string strUsage = HelpMessageGroup(_("Wallet options:")); + strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); + strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); + strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); + strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); + strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); + if (showDebug) + strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); + strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); + strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); + strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); + strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); + strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); + strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); + + if (showDebug) + { + strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); + + strUsage += HelpMessageOpt("-dblogsize=", strprintf("Flush wallet database activity from memory to disk log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); + strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); + strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); + } + + return strUsage; +} + +extern std::string DecodeDumpString(const std::string &str); +extern int64_t DecodeDumpTime(const std::string &str); + +bool CWallet::LoadWalletFromFile(const std::string filepath){ + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (pwalletMain->IsLocked()) + { + printf("Error: Please enter the wallet passphrase with walletpassphrase first."); + return false; + } + + std::ifstream file; + file.open(filepath, std::ios::in | std::ios::ate); + if (!file.is_open()) + { + printf("Cannot open wallet dump file"); + return false; + } + + int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); + + bool fGood = true; + + int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); + file.seekg(0, file.beg); + + pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + while (file.good()) { + pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + std::string line; + std::getline(file, line); + if (line.empty() || line[0] == '#') + continue; + + std::vector vstr; + boost::split(vstr, line, boost::is_any_of(" ")); + if (vstr.size() < 2) + continue; + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(vstr[0])) + continue; + CKey key = vchSecret.GetKey(); + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + CKeyID keyid = pubkey.GetID(); + if (pwalletMain->HaveKey(keyid)) { + LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString()); + continue; + } + int64_t nTime = DecodeDumpTime(vstr[1]); + std::string strLabel; + bool fLabel = true; + for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { + if (boost::algorithm::starts_with(vstr[nStr], "#")) + break; + if (vstr[nStr] == "change=1") + fLabel = false; + if (vstr[nStr] == "reserve=1") + fLabel = false; + if (boost::algorithm::starts_with(vstr[nStr], "label=")) { + strLabel = DecodeDumpString(vstr[nStr].substr(6)); + fLabel = true; + } + } + LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); + if (!pwalletMain->AddKeyPubKey(key, pubkey)) { + fGood = false; + continue; + } + pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime; + if (fLabel) + pwalletMain->SetAddressBook(keyid, strLabel, "receive"); + nTimeBegin = std::min(nTimeBegin, nTime); + } + file.close(); + pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI + + CBlockIndex *pindex = chainActive.Tip(); + while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200) + pindex = pindex->pprev; + + pwalletMain->UpdateTimeFirstKey(nTimeBegin); + + LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); + pwalletMain->ScanForWalletTransactions(pindex); + pwalletMain->MarkDirty(); + + if (!fGood) + { + printf("Error adding some keys to wallet"); + return false; + } + + return true; +} + +extern std::string EncodeDumpString(const std::string &str); +extern std::string EncodeDumpTime(int64_t nTime); +bool CWallet::ExportWalletToFile(const std::string filepath){ + + LOCK2(cs_main, pwalletMain->cs_wallet); + +// EnsureWalletIsUnlocked(); + if (pwalletMain->IsLocked()) + { + printf("Error: Please enter the wallet passphrase with walletpassphrase first."); + return false; + } + ofstream file; + file.open(filepath.c_str()); + if (!file.is_open()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); + + std::map mapKeyBirth; + std::set setKeyPool; + pwalletMain->GetKeyBirthTimes(mapKeyBirth); + pwalletMain->GetAllReserveKeys(setKeyPool); + + // sort time/key pairs + std::vector > vKeyBirth; + for (const auto& entry : mapKeyBirth) { + if (const CKeyID* keyID = boost::get(&entry.first)) { // set and test + vKeyBirth.push_back(std::make_pair(entry.second, *keyID)); + } + } + mapKeyBirth.clear(); + std::sort(vKeyBirth.begin(), vKeyBirth.end()); + + // produce output + file << strprintf("# Wallet dump created by Ipchain %s\n", CLIENT_BUILD); + file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); + file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); + file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); + file << "\n"; + + // add the base58check encoded extended master if the wallet uses HD + CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID; + if (!masterKeyID.IsNull()) + { + CKey key; + if (pwalletMain->GetKey(masterKeyID, key)) + { + CExtKey masterKey; + masterKey.SetMaster(key.begin(), key.size()); + + CBitcoinExtKey b58extkey; + b58extkey.SetKey(masterKey); + + file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n"; + } + } + for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { + const CKeyID &keyid = it->second; + std::string strTime = EncodeDumpTime(it->first); + std::string strAddr = CBitcoinAddress(keyid).ToString(); + CKey key; + if (pwalletMain->GetKey(keyid, key)) { + file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); + if (pwalletMain->mapAddressBook.count(keyid)) { + file << strprintf("label=%s", EncodeDumpString(pwalletMain->mapAddressBook[keyid].name)); + } + else if (keyid == masterKeyID) { + file << "hdmaster=1"; + } + else if (setKeyPool.count(keyid)) { + file << "reserve=1"; + } + else if (pwalletMain->mapKeyMetadata[keyid].hdKeypath == "m") { + file << "inactivehdmaster=1"; + } + else { + file << "change=1"; + } + file << strprintf(" # addr=%s%s\n", strAddr, (pwalletMain->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath=" + pwalletMain->mapKeyMetadata[keyid].hdKeypath : "")); + } + } + file << "\n"; + file << "# End of dump\n"; + file.close(); + return true; +} + +CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) +{ + // needed to restore wallet transaction meta data after -zapwallettxes + std::vector vWtx; + + if (GetBoolArg("-zapwallettxes", false)) { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + + CWallet *tempWallet = new CWallet(walletFile); + DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); + if (nZapWalletRet != DB_LOAD_OK) { + InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); + return NULL; + } + + delete tempWallet; + tempWallet = NULL; + } + + uiInterface.InitMessage(_("Loading wallet...")); + + int64_t nStart = GetTimeMillis(); + bool fFirstRun = true; + CWallet *walletInstance = new CWallet(walletFile); + DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); + if (nLoadWalletRet != DB_LOAD_OK) + { + if (nLoadWalletRet == DB_CORRUPT) { + InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); + return NULL; + } + else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) + { + InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data" + " or address book entries might be missing or incorrect."), + walletFile)); + } + else if (nLoadWalletRet == DB_TOO_NEW) { + InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME))); + return NULL; + } + else if (nLoadWalletRet == DB_NEED_REWRITE) + { + InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); + return NULL; + } + else { + InitError(strprintf(_("Error loading %s"), walletFile)); + return NULL; + } + } + + if (GetBoolArg("-upgradewallet", fFirstRun)) + { + int nMaxVersion = GetArg("-upgradewallet", 0); + if (nMaxVersion == 0) // the -upgradewallet without argument case + { + LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); + nMaxVersion = CLIENT_VERSION; + walletInstance->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately + } + else + LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); + if (nMaxVersion < walletInstance->GetVersion()) + { + InitError(_("Cannot downgrade wallet")); + return NULL; + } + walletInstance->SetMaxVersion(nMaxVersion); + } + + if (fFirstRun) + { + // Create new keyUser and set as default key + if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { + // generate a new master key + CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); + if (!walletInstance->SetHDMasterKey(masterPubKey)) + throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + } + CPubKey newDefaultKey; + if (walletInstance->GetKeyFromPool(newDefaultKey)) { + walletInstance->SetDefaultKey(newDefaultKey); + if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) { + InitError(_("Cannot write default address") += "\n"); + return NULL; + } + } + + walletInstance->SetBestChain(chainActive.GetLocator()); + } + else if (IsArgSet("-usehd")) { + bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + if (walletInstance->IsHDEnabled() && !useHD) { + InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile)); + return NULL; + } + if (!walletInstance->IsHDEnabled() && useHD) { + InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile)); + return NULL; + } + } + + LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); + + RegisterValidationInterface(walletInstance); + + CBlockIndex *pindexRescan = chainActive.Tip(); + if (GetBoolArg("-rescan", false)) + pindexRescan = chainActive.Genesis(); + else + { + CWalletDB walletdb(walletFile); + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = FindForkInGlobalIndex(chainActive, locator); + else + pindexRescan = chainActive.Genesis(); + } + if (chainActive.Tip() && chainActive.Tip() != pindexRescan) + { + //We can't rescan beyond non-pruned blocks, stop and throw an error + //this might happen if a user uses a old wallet within a pruned node + // or if he ran -disablewallet for a longer time, then decided to re-enable + if (fPruneMode) + { + CBlockIndex *block = chainActive.Tip(); + while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block) + block = block->pprev; + + if (pindexRescan != block) { + InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)")); + return NULL; + } + } + + uiInterface.InitMessage(_("Rescanning...")); + LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); + nStart = GetTimeMillis(); + walletInstance->ScanForWalletTransactions(pindexRescan, true); + LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); + walletInstance->SetBestChain(chainActive.GetLocator()); + CWalletDB::IncrementUpdateCounter(); + + // Restore wallet transaction metadata after -zapwallettxes=1 + if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") + { + CWalletDB walletdb(walletFile); + + BOOST_FOREACH(const CWalletTx& wtxOld, vWtx) + { + uint256 hash = wtxOld.GetHash(); + std::map::iterator mi = walletInstance->mapWallet.find(hash); + if (mi != walletInstance->mapWallet.end()) + { + const CWalletTx* copyFrom = &wtxOld; + CWalletTx* copyTo = &mi->second; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + copyTo->nTimeReceived = copyFrom->nTimeReceived; + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + copyTo->nOrderPos = copyFrom->nOrderPos; + walletdb.WriteTx(*copyTo); + } + } + } + } + walletInstance->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); + + { + LOCK(walletInstance->cs_wallet); + LogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize()); + LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size()); + LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size()); + } + + return walletInstance; +} + +bool CWallet::InitLoadWallet() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + pwalletMain = NULL; + LogPrintf("Wallet disabled!\n"); + return true; + } + + std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); + + CWallet * const pwallet = CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + pwalletMain = pwallet; + + return true; +} + +std::atomic CWallet::fFlushThreadRunning(false); + +void CWallet::postInitProcess(boost::thread_group& threadGroup) +{ + // Add wallet transactions that aren't already in a block to mempool + // Do this here as mempool requires genesis block to be loaded + ReacceptWalletTransactions(); + + // Run a thread to flush wallet periodically + if (!CWallet::fFlushThreadRunning.exchange(true)) { + threadGroup.create_thread(ThreadFlushWalletDB); + } +} + +bool CWallet::ParameterInteraction() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); + } + + if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) { + // Rewrite just private keys: rescan to find transactions + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + + // -zapwallettx implies a rescan + if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); + } + + if (GetBoolArg("-sysperms", false)) + return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); + if (GetArg("-prune", 0) && GetBoolArg("-rescan", false)) + return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); + + if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-minrelaytxfee") + " " + + _("The wallet will avoid paying less than the minimum relay fee.")); + + if (IsArgSet("-mintxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", ""))); + if (n > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on every transaction.")); + CWallet::minTxFee = CFeeRate(n); + } + if (IsArgSet("-fallbackfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), GetArg("-fallbackfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee estimates are not available.")); + CWallet::fallbackFee = CFeeRate(nFeePerK); + } + if (IsArgSet("-paytxfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) + return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you send a transaction.")); + + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), + GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (IsArgSet("-maxtxfee")) + { + CAmount nMaxFee = 0; + if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) + return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", ""))); + if (nMaxFee > HIGH_MAX_TX_FEE) + InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + maxTxFee = nMaxFee; + if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + } + } + nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); + fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + + if (fSendFreeTransactions && GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0) + return InitError("Creation of free transactions with their relay disabled is not supported."); + + return true; +} + +bool CWallet::BackupWallet(const std::string& strDest) +{ + if (!fFileBacked) + return false; + while (true) + { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0) + { + // Flush log data to the dat file + bitdb.CloseDb(strWalletFile); + bitdb.CheckpointLSN(strWalletFile); + bitdb.mapFileUseCount.erase(strWalletFile); + + // Copy wallet file + boost::filesystem::path pathSrc = GetDataDir() / strWalletFile; + boost::filesystem::path pathDest(strDest); + if (boost::filesystem::is_directory(pathDest)) + pathDest /= strWalletFile; + + try { +#if BOOST_VERSION >= 104000 + boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); +#else + boost::filesystem::copy_file(pathSrc, pathDest); +#endif + LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string()); + return true; + } catch (const boost::filesystem::filesystem_error& e) { + LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what()); + return false; + } + } + } + MilliSleep(100); + } + return false; +} + + +CKeyPool::CKeyPool() +{ + nTime = GetTime(); +} + +CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn) +{ + nTime = GetTime(); + vchPubKey = vchPubKeyIn; +} + +CWalletKey::CWalletKey(int64_t nExpires) +{ + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; +} + +void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) +{ + // Update the tx's hashBlock + hashBlock = pindex->GetBlockHash(); + + // set the position of the transaction in the block + nIndex = posInBlock; +} + +int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const +{ + if (hashUnset()) + return 0; + + AssertLockHeld(cs_main); + + // Find the block it claims to be in + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + pindexRet = pindex; + return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); +} + +int64_t CMerkleTx::GetTimeOfTokenInChain() const +{ + /*const CBlockIndex* &pindexRet;*/ + if (hashUnset()) + return 0; + + AssertLockHeld(cs_main); + + // Find the block it claims to be in + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + return pindex->GetBlockTime(); +} + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) +{ + return ::AcceptToMemoryPool(mempool, state, tx, false, NULL, NULL, false, nAbsurdFee); //The fourth parameter is modified to false +} diff --git a/.svn/pristine/d2/d26bb67e8eda68f01055b5f88841aacdc82c2205.svn-base b/.svn/pristine/d2/d26bb67e8eda68f01055b5f88841aacdc82c2205.svn-base new file mode 100644 index 0000000..21b1c68 --- /dev/null +++ b/.svn/pristine/d2/d26bb67e8eda68f01055b5f88841aacdc82c2205.svn-base @@ -0,0 +1,616 @@ + +#include "../util.h" +#include "TimeService.h" +#include +#include "../timedata.h" +#include +#include +#include +#include +#include + +bool g_bStdCout = false; +CTimeService timeService; +boost::mutex mutexTime; +boost::condition_variable cvTime; + +CNtpClient::CNtpClient() + :NTP_PORT(123) +{ +} + +CNtpClient::CNtpClient(const std::string& strServerIp) + :NTP_PORT(123),_serverIp(strServerIp) +{ +} + +CNtpClient::~CNtpClient() +{ +} + +void CNtpClient::reverseByteOrder(uint64_t &in) +{ + uint64_t rs = 0; + int len = sizeof(uint64_t); + for (int i = 0; i < len; i++) + { + std::memset(reinterpret_cast(&rs) + len - 1 - i + , static_cast ((in & 0xFFLL << (i * 8)) >> i * 8) + , 1); + } + in = rs; +} +std::vector CNtpClient::domain2ip(const char *pDomain, int nPort) +{ + std::vector vecStrIp; + if (NULL == pDomain) + { + LogPrintf("[CNtpClient::domain2ip]Convert domain name to IP address error,input is null\n"); + return vecStrIp; + } + + try + { + boost::asio::io_service ios; + boost::asio::ip::tcp::resolver slv(ios); + boost::asio::ip::tcp::resolver::query qry(pDomain, boost::lexical_cast(nPort)); + boost::asio::ip::tcp::resolver::iterator it = slv.resolve(qry); + boost::asio::ip::tcp::resolver::iterator iterEnd; + + for (; it != iterEnd; it++) + { + vecStrIp.push_back((*it).endpoint().address().to_string()); + } + return vecStrIp; + } + catch (boost::system::system_error e) + { + LogPrintf("[CNtpClient::domain2ip]Convert domain name to IP address error,error message:%s\n", e.code()); + return vecStrIp; + } + catch (std::exception& e) + { + LogPrintf("[CNtpClient::domain2ip]Convert domain name to IP address error,error message:%s\n", e.what()); + return vecStrIp; + } +} + +int64_t CNtpClient::getNetTime(std::string strAddress, int64_t &nOldLocalTime, int64_t &nNewLocalTime) +{ + std::vector vecIP; + + //Decide if it's a domain name + if (isalpha((strAddress.c_str())[0])) + { + LogPrintf("[CNtpClient::getNetTime]domain name:%s \n", strAddress); + vecIP = domain2ip(strAddress.c_str(), 0); + } + else + { + LogPrintf("[CNtpClient::getNetTime]IPAddress: %s\n", strAddress); + vecIP.push_back(strAddress); + } + + int64_t nRet = 0; + if (0 == vecIP.size()) + { + LogPrintf("[CNtpClient::getNetTime]address error\n"); + return nRet; + } + + for (int nIndex = 0; nIndex < vecIP.size(); nIndex++) + { + nOldLocalTime = GetTimeMicros(); + nRet = getOneNetTime(vecIP[nIndex]); + nNewLocalTime = GetTimeMicros(); + if (0 < nRet) + { + LogPrintf("[CNtpClient::getNetTime] NetTime: %d\n", nRet); + break; + } + } + if (0 == nRet) + { + nOldLocalTime = 0; + nNewLocalTime = 0; + LogPrintf("[CNtpClient::getNetTime] NetTime: 0\n"); + } + + return nRet; +} + +int64_t CNtpClient::getOneNetTime(std::string strIP) +{ + int64_t nRetTime = 0; + try + { + boost::asio::io_service io_Ntp; + boost::asio::ip::udp::socket _socket(io_Ntp); + _socket.open(boost::asio::ip::udp::v4()); + boost::asio::ip::udp::endpoint ep(boost::asio::ip::address_v4::from_string(strIP), NTP_PORT); + + CNtpPacket request; + std::stringstream ss; + std::string strSendBuf; + ss << request; + ss >> strSendBuf; + + std::size_t nSendlenth = _socket.send_to(boost::asio::buffer(strSendBuf), ep); + + if (nSendlenth != strSendBuf.size()) + { + if (_socket.is_open()) + { + _socket.close(); + } + return 0; + } + + std::array arrRecv; + + //Wait 5 seconds to receive data + CAsyncRecv asyncRecv(&io_Ntp, &_socket); + boost::system::error_code ec; + std::size_t len = asyncRecv.receive(boost::asio::buffer(arrRecv), boost::posix_time::seconds(5), ec); + if (ec) + { + LogPrintf("[CNtpClient::getOneNetTime] ErrorMessage:%s\n", ec.message()); + if (_socket.is_open()) + { + _socket.close(); + } + return 0; + } + + uint8_t* pBytes = arrRecv.data(); + + /****get the last 8 bytes(Transmit Timestamp) from received packet. + std::memcpy(&last, pBytes + len - 8, sizeof(last)); + ****create a NtpPacket*/ + CNtpPacket resonpse; + std::stringstream strRss; + strRss.write(reinterpret_cast(pBytes), len); + strRss >> resonpse; + + uint64_t uLast = resonpse._rep._trt; + + reverseByteOrder(uLast); + + int64_t nSeconds = (uLast & 0xFFFFFFFF00000000) >> 32; + int64_t nMico = uLast & 0x00000000FFFFFFFF; + + /*32-bit said so far in 1900 before all of the number of seconds the integer part, + 32 is the number of milliseconds after 4294.967296 (32/10 = 2 ^ ^ 6) times*/ + double dMico = nMico; + dMico = dMico / 4294.967296; + + int64_t nTime1900 = (boost::posix_time::ptime(boost::gregorian::date(1900, 1, 1)) - + boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_microseconds(); + + nRetTime = nSeconds * 1000000 + dMico; + nRetTime = nRetTime + nTime1900; + + if (_socket.is_open()) + { + _socket.close(); + } + + LogPrintf("[CNtpClient::getOneNetTime] NetTime:%d\n", nRetTime); + return nRetTime; + } + catch (boost::system::system_error e) + { + LogPrintf("[CNtpClient::getOneNetTime] ErrorMessage:%s\n", e.code()); + return 0; + } + catch (std::exception& e) + { + LogPrintf("[CNtpClient::getOneNetTime] ErrorMessage:%s\n", e.what()); + return 0; + } + catch (...) + { + LogPrintf("[CNtpClient::getOneNetTime] ErrorMessage\n"); + return 0; + } +} + +CTimeService::CTimeService():m_nNetTimeOffset(0), m_bNewStart(true) +{ + m_nLastInitTime = GetTimeMicros(); +} + +CTimeService::~CTimeService() +{ +} + +void CTimeService::GetNetTimeError() +{ + //5 minutes (6000000l microsecond) retry + m_nLastInitTime = GetCurrentTimeMicros() - TIME_REFRESH_TIME; + m_nLastInitTime = m_nLastInitTime + TIME_5MINUTE; +} + +void CTimeService::GetNetTimeSuccess() +{ + m_nLastInitTime = GetCurrentTimeMicros(); +} + +int64_t CTimeService::GetNetTime(int64_t &nOldLocalTime, int64_t &nNewLocalTime) +{ + //Read the time server address from the configuration file + //to read the address of each time server until there is a fetch value + std::multimap mapConfigInfo; + ReadTimeServerFile(mapConfigInfo); + + int64_t nNetTime = 0; + std::multimap::iterator iter_configMap = mapConfigInfo.begin(); + for (; iter_configMap != mapConfigInfo.end(); ++iter_configMap) + { + if ("timeserver" == iter_configMap->first) + { + CNtpClient ntp; + nNetTime = ntp.getNetTime(iter_configMap->second, nOldLocalTime, nNewLocalTime); + + if (0 < nNetTime) + { + break; + } + else + { + nOldLocalTime = 0; + nNewLocalTime = 0; + } + } + } + + LogPrintf("[CTimeService::GetNetTime] NetTime:%d\n", nNetTime); + return nNetTime; +} + +void CTimeService::CalculateNetOffsetTime(const int64_t &nNetTime,const int64_t &nOldLocalTime,const int64_t &nNewLocalTime) +{ + { + writeLock wtlock(rwmutex); + m_nNetTimeOffset = nNetTime - (nNewLocalTime + nOldLocalTime) / 2; + + LogPrintf("[CTimeService::CalculateNetOffsetTime] NetTimeOffset:%d, nNetTime:%d, nNewLocalTime:%d, nOldLocalTime:%d,GetTimeMicros():%d\n", + m_nNetTimeOffset, nNetTime, nNewLocalTime, nOldLocalTime, GetTimeMicros()); + } + + GetNetTimeSuccess(); + + //The new start node flag bit is false + m_bNewStart = false; + return; +} + +void CTimeService::GetNetOffsetTime() +{ + //get network time + int64_t nOldLocalTime = 0; + int64_t nNewLocalTime = 0; + int64_t nNetTime = GetNetTime(nOldLocalTime, nNewLocalTime); + + if(0 < nNetTime) + { + //Calculate network offset time + CalculateNetOffsetTime(nNetTime, nOldLocalTime, nNewLocalTime); + return; + } + else + { + LogPrintf("[CTimeService::GetNetOffsetTime] GetNetTime faile,Get NetTime Every 5 Minutes."); + + //New start node, not even all servers in the configuration file, + //Reconnect the server every five minutes. + //Before the server time, the new node's session, packaging, and other logic are not started + if (m_bNewStart) + { + int64_t nLastGetTime = GetTimeMicros(); + while (true) + { + boost::this_thread::interruption_point(); + + if (TIME_5MINUTE < GetTimeMicros() - nLastGetTime) + { + nOldLocalTime = 0; + nNewLocalTime = 0; + int64_t nRetNetTime = GetNetTime(nOldLocalTime, nNewLocalTime); + if (0 < nRetNetTime) + { + CalculateNetOffsetTime(nRetNetTime,nOldLocalTime, nNewLocalTime); + break; + } + else + { + nLastGetTime = GetTimeMicros(); + } + } + + //MilliSleep(5001); + std::chrono::milliseconds dura(5001); + std::this_thread::sleep_for(dura); + } + return; + } + else//Non-new start node, m_nNetTimeOffset remains unchanged, and reconnect to the server every 5 minutes + { + GetNetTimeError(); + return; + } + } +} + +void CTimeService::monitorTimeChange() +{ + try + { + //Hard-coded configuration file + WriteTimeServerFile(); + + GetNetOffsetTime(); + LogPrintf("[CTimeService::monitorTimeChange]Init_GetNetOffsetTime,NetTimeOffset:%d----CurrentTimeMicros:%d\n", + m_nNetTimeOffset, GetCurrentTimeMicros()); + + int64_t n64LastTime = GetCurrentTimeMicros(); + while (true) + { + boost::this_thread::interruption_point(); + + //Adjust the network time dynamically + int64_t n64NewTime = GetCurrentTimeMicros(); + if ((n64NewTime - n64LastTime > TIME_1HALFSECOND) || (n64LastTime - n64NewTime > TIME_1HALFSECOND)) + { + //Server time jump, deviation >500 ms, according to network time adjustment. + GetNetOffsetTime(); + n64NewTime = GetCurrentTimeMicros(); + LogPrintf("[CTimeService::monitorTimeChange]Server time jumps ,NetTimeOffset:%d----CurrentTimeMicros:%d\n", + m_nNetTimeOffset, GetCurrentTimeMicros()); + } + else if ((GetCurrentTimeMicros() - m_nLastInitTime) > TIME_REFRESH_TIME) + { + //Update network time every 100 minutes + GetNetOffsetTime(); + n64NewTime = GetCurrentTimeMicros(); + LogPrintf("[CTimeService::monitorTimeChange]Every 100 Minutes,NetTimeOffset:%d----CurrentTimeMicros:%d\n", + m_nNetTimeOffset,GetCurrentTimeMicros()); + } + + n64LastTime = n64NewTime; + + //MilliSleep(1001); + std::chrono::milliseconds dura(1000); + std::this_thread::sleep_for(dura); + } + } + catch (boost::thread_interrupted & errcod) + { + LogPrintf("[CTimeService::monitorTimeChange] end by boost::thread thrdMiningService Interrupt exception was thrown\n"); + return; + } + catch (...) + { + LogPrintf("[CTimeService::monitorTimeChange] end by throw\n"); + return; + } +} + +int64_t CTimeService::GetCurrentTimeMicros() +{ + try + { + readLock rdlock(rwmutex); + return GetTimeMicros() + m_nNetTimeOffset; + } + catch (...) + { + LogPrintf("[CTimeService::GetCurrentTimeMicros] end by throw\n"); + return -1; + } +} + +int64_t CTimeService::GetCurrentTimeMillis() +{ + return GetCurrentTimeMicros()/1000; +} + +int64_t CTimeService::GetCurrentTimeSeconds() +{ + return GetCurrentTimeMillis()/1000; +} + +void CTimeService::start() +{ + thrdTimeService = boost::thread(&TraceThread >, "time", std::function(std::bind(&CTimeService::monitorTimeChange, this))); +} + +void CTimeService::stop() +{ + thrdTimeService.interrupt(); + thrdTimeService.join(); +} + +int64_t CTimeService::abs64(int64_t n) +{ + return (n >= 0 ? n : -n); +} + +bool CTimeService::ReadTimeServerFileToString(std::string &strAddress) +{ + std::string strFilePath; + boost::filesystem::path dirPath = GetDataDir(); + strFilePath += dirPath.string(); + strFilePath += "/timeserver.conf"; + + std::ifstream configFile; + if (!configFile.is_open()) + { + configFile.open(strFilePath.c_str()); + } + + if (configFile.is_open()) + { + configFile.seekg(0, std::ios::beg); + + while (!configFile.eof() && configFile.peek() != EOF) + { + std::string str_line; + std::getline(configFile, str_line); + + strAddress += str_line; + strAddress += "\n"; + } + + configFile.close(); + + return true; + } + else + { + return false; + } +} + + +bool CTimeService::ReadTimeServerFile(std::multimap& mapConfigInfo) +{ + std::string strFilePath; + boost::filesystem::path dirPath = GetDataDir(); + strFilePath += dirPath.string(); + strFilePath += "/timeserver.conf"; + + std::ifstream configFile; + if (!configFile.is_open()) + { + configFile.open(strFilePath.c_str()); + } + + if (configFile.is_open()) + { + configFile.seekg(0, std::ios::beg); + + while (!configFile.eof() && configFile.peek() != EOF) + { + std::string str_line; + std::getline(configFile, str_line); + + //Filter out the comment message, + //which is to filter out the line if the first character is # + if (str_line.compare(0, 1, "#") == 0) + { + continue; + } + std::size_t pos = str_line.find('='); + std::string str_key = str_line.substr(0, pos); + std::string str_value = str_line.substr(pos + 1); + mapConfigInfo.insert(std::pair(str_key, str_value)); + } + + configFile.close(); + + return true; + } + else + { + return false; + } +} + +bool CTimeService::WriteTimeServerFile() +{ + std::string strFilePath; + boost::filesystem::path dirPath = GetDataDir(); + strFilePath += dirPath.string(); + strFilePath += "/timeserver.conf"; + + std::ofstream configFile; + + std::string strAddress = "#Ali's NTP time server\n" \ + "timeserver=ntp1.aliyun.com\n" \ + "timeserver=cn.pool.ntp.org\n" \ + "timeserver=ntp2.aliyun.com\n" \ + "timeserver=ntp3.aliyun.com\n" \ + "timeserver=ntp4.aliyun.com\n" \ + "timeserver=ntp5.aliyun.com\n" \ + "timeserver=ntp6.aliyun.com\n" \ + "timeserver=ntp7.aliyun.com\n" \ + "timeserver=time1.aliyun.com\n" \ + "timeserver=time2.aliyun.com\n" \ + "timeserver=time3.aliyun.com\n" \ + "timeserver=time4.aliyun.com\n" \ + "timeserver=time5.aliyun.com\n" \ + "timeserver=time6.aliyun.com\n" \ + "timeserver=time7.aliyun.com\n" \ + "#Shanghai\n" \ + "timeserver=ntp.api.bz\n" \ + "timeserver=ntp.gwadar.cn\n" \ + "#North America\n" \ + "timeserver=ntp1.cmc.ec.gc.ca\n" \ + "timeserver=ntp.ucsd.edu\n" \ + "timeserver=ntp-1.mcs.anl.gov\n" \ + "#Russia\n" \ + "timeserver=ntp.psn.ru\n" \ + "#Korea\n" \ + "timeserver=time.nuri.net\n" \ + "#Australia\n" \ + "timeserver=ntp.adelaide.edu.au\n" \ + "#Europe\n" \ + "timeserver=ntp.obspm.fr\n" \ + "timeserver=ntp.univ-lyon1.fr\n" \ + "#England\n" \ + "timeserver=ntp0.uk.uu.net\n" \ + "timeserver=ntp1.uk.uu.netn\n"; + + if (boost::filesystem::exists(strFilePath)) + { + std::string strGetAddress; + ReadTimeServerFileToString(strGetAddress); + + if (strAddress.size() <= strGetAddress.size()) + { + LogPrintf("[CTimeService::WriteTimeServerFile] timeserver.conf is existed,\ + And the information is greater than the default configuration information"); + return true; + } + } + + configFile.open(strFilePath, std::ios::out | std::ios::trunc); + if (configFile.is_open()) + { + configFile << strAddress; + configFile.flush(); + configFile.close(); + + return true; + } + + return false; +} + +bool CTimeService::ChangeFilePath(std::string &strPath) +{ + if (strPath.empty()) + { + LogPrintf("[CDpocInfo::ChangeFilePath] InPut is NULL"); + return false; + } + + LogPrintf("[CTimeService::ChangeFilePath] newPath==oldPath"); + return true; +} + +bool CTimeService::IsHasNetTimeOffset() +{ + LogPrintf("[CTimeService::IsHasNetTimeOffset] begin\n"); + readLock rdlock(rwmutex); + if (0 == m_nNetTimeOffset) + { + LogPrintf("[CTimeService::IsHasNetTimeOffset] end by false\n"); + return false; + } + + LogPrintf("[CTimeService::IsHasNetTimeOffset] end by true\n"); + return true; +} + diff --git a/.svn/pristine/d9/d92e7e6930ea772dc377b2178b3738895b18ec8b.svn-base b/.svn/pristine/d9/d92e7e6930ea772dc377b2178b3738895b18ec8b.svn-base new file mode 100644 index 0000000..91c568d --- /dev/null +++ b/.svn/pristine/d9/d92e7e6930ea772dc377b2178b3738895b18ec8b.svn-base @@ -0,0 +1,383 @@ +// Copyright (c) 2011-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "intro.h" +#include "ui_intro.h" +#include "guiutil.h" +#include "util.h" +#include +#include +#include +#include +#include +#include +#include "log/log.h" +#include "log/stateinfo.h" +#include +#include "logondlg.h" +#include "dpoc/DpocInfo.h" + +std::string Intro::m_datapath = ""; +std::string Intro::m_clienttype = ""; +bool Intro::m_IsRecover = false; +QString m_notestpath; +static const uint64_t GB_BYTES = 1000000000LL; +/* Minimum free space (in GB) needed for data directory */ +static const uint64_t BLOCK_CHAIN_SIZE = 120; +/* Minimum free space (in GB) needed for data directory when pruned; Does not include prune target */ +static const uint64_t CHAIN_STATE_SIZE = 2; +/* Total required space (in GB) depending on user choice (prune, not prune) */ +static uint64_t requiredSpace; + +class FreespaceChecker : public QObject +{ + Q_OBJECT + +public: + FreespaceChecker(Intro *intro); + + enum Status { + ST_OK, + ST_ERROR + }; + +public Q_SLOTS: + void check(); + +Q_SIGNALS: + void reply(int status, const QString &message, quint64 available); + +private: + Intro *intro; +}; + +#include "intro.moc" + +FreespaceChecker::FreespaceChecker(Intro *_intro) +{ + this->intro = _intro; +} + +void FreespaceChecker::check() +{ + namespace fs = boost::filesystem; + QString dataDirStr = intro->getPathToCheck(); + fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr); + uint64_t freeBytesAvailable = 0; + int replyStatus = ST_OK; + QString replyMessage = tr("A new data directory will be created."); + + /* Find first parent that exists, so that fs::space does not fail */ + fs::path parentDir = dataDir; + fs::path parentDirOld = fs::path(); + while(parentDir.has_parent_path() && !fs::exists(parentDir)) + { + parentDir = parentDir.parent_path(); + /* Check if we make any progress, break if not to prevent an infinite loop here */ + if (parentDirOld == parentDir) + break; + parentDirOld = parentDir; + } + try { + freeBytesAvailable = fs::space(parentDir).available; + if(fs::exists(dataDir)) + { + if(fs::is_directory(dataDir)) + { + QString separator = "" + QDir::toNativeSeparators("/") + tr("name") + ""; + replyStatus = ST_OK; + replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator); + } else { + replyStatus = ST_ERROR; + replyMessage = tr("Path already exists, and is not a directory."); + } + } + } catch (const fs::filesystem_error&) + { + /* Parent directory does not exist or is not accessible */ + replyStatus = ST_ERROR; + replyMessage = tr("Cannot create data directory here."); + } + Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable); +} + + +Intro::Intro(QWidget *parent) : + QDialog(parent), + ui(new Ui::Intro), + thread(0), + signalled(false) +{ + ui->setupUi(this); + ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(tr(PACKAGE_NAME))); + ui->storageLabel->setText(ui->storageLabel->text().arg(tr(PACKAGE_NAME))); + uint64_t pruneTarget = std::max(0, GetArg("-prune", 0)); + requiredSpace = BLOCK_CHAIN_SIZE; + if (pruneTarget) { + uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES); + if (prunedGBs <= requiredSpace) { + requiredSpace = prunedGBs; + } + } + requiredSpace += CHAIN_STATE_SIZE; + ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(tr(PACKAGE_NAME)).arg(requiredSpace)); + startThread(); +} + +Intro::~Intro() +{ + delete ui; + /* Ensure thread is finished before it is deleted */ + Q_EMIT stopThread(); + thread->wait(); +} + +QString Intro::getDataDirectory() +{ + return ui->dataDirectory->text(); +} + +void Intro::setDataDirectory(const QString &dataDir) +{ + ui->dataDirectory->setText(dataDir); + if(dataDir == getDefaultDataDirectory()) + { + ui->dataDirDefault->setChecked(true); + ui->dataDirectory->setEnabled(false); + ui->ellipsisButton->setEnabled(false); + } else { + ui->dataDirCustom->setChecked(true); + ui->dataDirectory->setEnabled(true); + ui->ellipsisButton->setEnabled(true); + } +} + +QString Intro::getDefaultDataDirectory() +{ + return GUIUtil::boostPathToQString(GetDefaultDataDir()); +} +bool isFileExist(QString fullFileName) +{ + QFileInfo fileInfo(fullFileName); + if(fileInfo.isFile()) + { + return true; + } + return false; +} +bool isDirExist(QString fullPath) +{ + QDir dir(fullPath); + if(dir.exists()) + { + return true; + } + else + { + bool ok = dir.mkdir(fullPath); + return ok; + } +} + + +bool Intro::pickDataDirectory(logondlg* plogondlg ,std::string clienttype) +{ + m_clienttype = clienttype; + namespace fs = boost::filesystem; + QSettings settings; + QString dataDir ; + if(!GetArg("-datadir", "").empty()){ + dataDir = QString::fromStdString(GetArg("-datadir", "")); + // return true; + }else{ + dataDir = settings.value("strDataDir").toString(); + if(dataDir == "") + dataDir = getDefaultDataDirectory(); + } + + if(dataDir.isEmpty()) + dataDir = settings.value("strDataDir", dataDir).toString(); + m_notestpath = dataDir; + if(clienttype == "test") + dataDir+="/testnet3"; + + + LOG_SETPATH(q2s(dataDir)); + STATE_SETPATH(q2s(dataDir)); + m_datapath = q2s(dataDir); + + if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || + GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || + settings.value("fReset", false).toBool() || + GetBoolArg("-resetguisettings", false)|| + !isFileExist(dataDir+"/wallet.dat")) + { + isDirExist(dataDir); + + + plogondlg->setdatapath(m_notestpath); + int back = plogondlg->exec(); + + if(back==0){ + return false; + } + + dataDir = plogondlg->getdatapath(); + + if("IPChain" != dataDir.right(7)){ + dataDir += "/IPChain"; + isDirExist(dataDir); + } + + m_notestpath = dataDir; + QString temp = dataDir; + if(clienttype == "test") + temp+="/testnet3"; + LOG_SETPATH(q2s(temp)); + STATE_SETPATH(q2s(temp)); + m_datapath = q2s(temp); + LOG_WRITE(LOG_INFO,"fs::exists in if 1",dataDir.toStdString().c_str()); + /* If current default data directory does not exist, let the user choose one */ + Intro intro; + intro.setDataDirectory(dataDir); + intro.setWindowIcon(QIcon(":icons/bitcoin")); + + dataDir = intro.getDataDirectory(); + LOG_WRITE(LOG_INFO,"fs::exists in if 2",dataDir.toStdString().c_str()); + if(dataDir != getDefaultDataDirectory()) + SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); + try { + TryCreateDirectory(GUIUtil::qstringToBoostPath(dataDir)); + } catch (const fs::filesystem_error&) { + QMessageBox::critical(0, tr(PACKAGE_NAME), + tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir)); + } + settings.setValue("strDataDir", m_notestpath); + settings.setValue("fReset", false); + //intro.setIsRecover(true); + m_IsRecover = true; + + LOG_WRITE(LOG_INFO,"truertruetruetruetruetruetrue"); + }else{ + if(fs::exists(GUIUtil::qstringToBoostPath(dataDir))) + { + //Intro intro; + //intro.setIsRecover(false); + m_IsRecover = false; + LOG_WRITE(LOG_INFO,"fs::exists"); + LOG_WRITE(LOG_INFO,"falsefalsefalsefalsefalse"); + }else + { + LOG_WRITE(LOG_INFO,"!fs::exists"); + } + } + + if(dataDir != getDefaultDataDirectory()) + SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(m_notestpath).string()); // use OS locale for path setting + return true; +} +void Intro::setIsRecover(bool IsRecover) +{ + +} +bool Intro::getIsRecover() +{ +} +void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable) +{ + switch(status) + { + case FreespaceChecker::ST_OK: + ui->errorMessage->setText(message); + ui->errorMessage->setStyleSheet(""); + break; + case FreespaceChecker::ST_ERROR: + ui->errorMessage->setText(tr("Error") + ": " + message); + ui->errorMessage->setStyleSheet("QLabel { color: #800000 }"); + break; + } + /* Indicate number of bytes available */ + if(status == FreespaceChecker::ST_ERROR) + { + ui->freeSpace->setText(""); + } else { + QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES); + if(bytesAvailable < requiredSpace * GB_BYTES) + { + freeString += " " + tr("(of %n GB needed)", "", requiredSpace); + ui->freeSpace->setStyleSheet("QLabel { color: #800000 }"); + } else { + ui->freeSpace->setStyleSheet(""); + } + ui->freeSpace->setText(freeString + "."); + } + /* Don't allow confirm in ERROR state */ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR); +} + +void Intro::on_dataDirectory_textChanged(const QString &dataDirStr) +{ + /* Disable OK button until check result comes in */ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + checkPath(dataDirStr); +} + +void Intro::on_ellipsisButton_clicked() +{ + QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text())); + if(!dir.isEmpty()) + ui->dataDirectory->setText(dir); +} + +void Intro::on_dataDirDefault_clicked() +{ + setDataDirectory(getDefaultDataDirectory()); +} + +void Intro::on_dataDirCustom_clicked() +{ + ui->dataDirectory->setEnabled(true); + ui->ellipsisButton->setEnabled(true); +} + +void Intro::startThread() +{ + thread = new QThread(this); + FreespaceChecker *executor = new FreespaceChecker(this); + executor->moveToThread(thread); + + connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64))); + connect(this, SIGNAL(requestCheck()), executor, SLOT(check())); + /* make sure executor object is deleted in its own thread */ + connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater())); + connect(this, SIGNAL(stopThread()), thread, SLOT(quit())); + + thread->start(); +} + +void Intro::checkPath(const QString &dataDir) +{ + mutex.lock(); + pathToCheck = dataDir; + if(!signalled) + { + signalled = true; + Q_EMIT requestCheck(); + } + mutex.unlock(); +} + +QString Intro::getPathToCheck() +{ + QString retval; + mutex.lock(); + retval = pathToCheck; + signalled = false; /* new request can be queued now */ + mutex.unlock(); + return retval; +} diff --git a/.svn/pristine/d9/d98d5b8d85747119f316896f9a2314574e477666.svn-base b/.svn/pristine/d9/d98d5b8d85747119f316896f9a2314574e477666.svn-base new file mode 100644 index 0000000..d1d93e4 --- /dev/null +++ b/.svn/pristine/d9/d98d5b8d85747119f316896f9a2314574e477666.svn-base @@ -0,0 +1,229 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/client.h" +#include "rpc/protocol.h" +#include "util.h" + +#include +#include + +#include // for to_lower() +#include + +using namespace std; + +class CRPCConvertParam +{ +public: + std::string methodName; //!< method whose params want conversion + int paramIdx; //!< 0-based idx of param to convert + std::string paramName; //!< parameter name +}; + +/** + * Specifiy a (method, idx, name) here if the argument is a non-string RPC + * argument and needs to be converted from JSON. + * + * @note Parameter indexes start from 0. + */ +static const CRPCConvertParam vRPCConvertParams[] = +{ + { "setmocktime", 0, "timestamp" }, + { "generate", 0, "nblocks" }, + { "generate", 1, "maxtries" }, + + { "generatedpoc", 0, "nblocks" }, + { "generatedpoc", 1, "nPeriodCount" }, + { "generatedpoc", 2, "nPeriodStartTime" }, + { "generatedpoc", 3, "nTimePeriod" }, + + { "getdpoclist", 0, "num" }, + + { "generatetoaddress", 0, "nblocks" }, + { "generatetoaddress", 2, "maxtries" }, + { "getnetworkhashps", 0, "nblocks" }, + { "getnetworkhashps", 1, "height" }, + { "sendtoaddress", 1, "amount" }, + { "sendtoaddress", 4, "subtractfeefromamount" }, + { "settxfee", 0, "amount" }, + { "getreceivedbyaddress", 1, "minconf" }, + { "getreceivedbyaccount", 1, "minconf" }, + { "listreceivedbyaddress", 0, "minconf" }, + { "listreceivedbyaddress", 1, "include_empty" }, + { "listreceivedbyaddress", 2, "include_watchonly" }, + { "listreceivedbyaccount", 0, "minconf" }, + { "listreceivedbyaccount", 1, "include_empty" }, + { "listreceivedbyaccount", 2, "include_watchonly" }, + { "getbalance", 1, "minconf" }, + { "getbalance", 2, "include_watchonly" }, + { "getblockhash", 0, "height" }, + { "waitforblockheight", 0, "height" }, + { "waitforblockheight", 1, "timeout" }, + { "waitforblock", 1, "timeout" }, + { "waitfornewblock", 0, "timeout" }, + { "move", 2, "amount" }, + { "move", 3, "minconf" }, + { "sendfrom", 2, "amount" }, + { "sendfrom", 3, "minconf" }, + { "listtransactions", 1, "count" }, + { "listtransactions", 2, "skip" }, + { "listtransactions", 3, "include_watchonly" }, + { "listaccounts", 0, "minconf" }, + { "listaccounts", 1, "include_watchonly" }, + { "walletpassphrase", 1, "timeout" }, + { "getblocktemplate", 0, "template_request" }, + { "listsinceblock", 1, "target_confirmations" }, + { "listsinceblock", 2, "include_watchonly" }, + { "sendmany", 1, "amounts" }, + { "sendmany", 2, "minconf" }, + { "sendmany", 4, "subtractfeefrom" }, + { "sendtokenmany", 2, "amounts" }, + { "addmultisigaddress", 0, "nrequired" }, + { "addmultisigaddress", 1, "keys" }, + { "createmultisig", 0, "nrequired" }, + { "createmultisig", 1, "keys" }, + { "listunspent", 0, "minconf" }, + { "listunspent", 1, "maxconf" }, + { "listunspent", 2, "addresses" }, + { "getblock", 1, "verbose" }, + { "getblockheader", 1, "verbose" }, + { "gettransaction", 1, "include_watchonly" }, + { "getrawtransaction", 1, "verbose" }, + { "createrawtransaction", 0, "inputs" }, + { "createrawtransaction", 1, "outputs" }, + { "createrawtransaction", 2, "locktime" }, + { "signrawtransaction", 1, "prevtxs" }, + { "signrawtransaction", 2, "privkeys" }, + { "sendrawtransaction", 1, "allowhighfees" }, + { "fundrawtransaction", 1, "options" }, + { "gettxout", 1, "n" }, + { "gettxout", 2, "include_mempool" }, + { "gettxoutproof", 0, "txids" }, + { "lockunspent", 0, "unlock" }, + { "lockunspent", 1, "transactions" }, + { "importprivkey", 2, "rescan" }, + { "importaddress", 2, "rescan" }, + { "importaddress", 3, "p2sh" }, + { "importpubkey", 2, "rescan" }, + { "importmulti", 0, "requests" }, + { "importmulti", 1, "options" }, + { "verifychain", 0, "checklevel" }, + { "verifychain", 1, "nblocks" }, + { "pruneblockchain", 0, "height" }, + { "keypoolrefill", 0, "newsize" }, + { "getrawmempool", 0, "verbose" }, + { "estimatefee", 0, "nblocks" }, + { "estimatepriority", 0, "nblocks" }, + { "estimatesmartfee", 0, "nblocks" }, + { "estimatesmartpriority", 0, "nblocks" }, + { "prioritisetransaction", 1, "priority_delta" }, + { "prioritisetransaction", 2, "fee_delta" }, + { "setban", 2, "bantime" }, + { "setban", 3, "absolute" }, + { "setnetworkactive", 0, "state" }, + { "getmempoolancestors", 1, "verbose" }, + { "getmempooldescendants", 1, "verbose" }, + { "bumpfee", 1, "options" }, + // Echo with conversion (For testing only) + { "echojson", 0, "arg0" }, + { "echojson", 1, "arg1" }, + { "echojson", 2, "arg2" }, + { "echojson", 3, "arg3" }, + { "echojson", 4, "arg4" }, + { "echojson", 5, "arg5" }, + { "echojson", 6, "arg6" }, + { "echojson", 7, "arg7" }, + { "echojson", 8, "arg8" }, + { "echojson", 9, "arg9" }, +}; + +class CRPCConvertTable +{ +private: + std::set> members; + std::set> membersByName; + +public: + CRPCConvertTable(); + + bool convert(const std::string& method, int idx) { + return (members.count(std::make_pair(method, idx)) > 0); + } + bool convert(const std::string& method, const std::string& name) { + return (membersByName.count(std::make_pair(method, name)) > 0); + } +}; + +CRPCConvertTable::CRPCConvertTable() +{ + const unsigned int n_elem = + (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); + + for (unsigned int i = 0; i < n_elem; i++) { + members.insert(std::make_pair(vRPCConvertParams[i].methodName, + vRPCConvertParams[i].paramIdx)); + membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName, + vRPCConvertParams[i].paramName)); + } +} + +static CRPCConvertTable rpcCvtTable; + +/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) + * as well as objects and arrays. + */ +UniValue ParseNonRFCJSONValue(const std::string& strVal) +{ + UniValue jVal; + if (!jVal.read(std::string("[")+strVal+std::string("]")) || + !jVal.isArray() || jVal.size()!=1) + throw runtime_error(string("Error parsing JSON:")+strVal); + return jVal[0]; +} + +UniValue RPCConvertValues(const std::string &strMethod, const std::vector &strParams) +{ + UniValue params(UniValue::VARR); + + for (unsigned int idx = 0; idx < strParams.size(); idx++) { + const std::string& strVal = strParams[idx]; + + if (!rpcCvtTable.convert(strMethod, idx)) { + // insert string value directly + params.push_back(strVal); + } else { + // parse string as JSON, insert bool/number/object/etc. value + params.push_back(ParseNonRFCJSONValue(strVal)); + } + } + + return params; +} + +UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector &strParams) +{ + UniValue params(UniValue::VOBJ); + + for (const std::string &s: strParams) { + size_t pos = s.find("="); + if (pos == std::string::npos) { + throw(std::runtime_error("No '=' in named argument '"+s+"', this needs to be present for every argument (even if it is empty)")); + } + + std::string name = s.substr(0, pos); + std::string value = s.substr(pos+1); + + if (!rpcCvtTable.convert(strMethod, name)) { + // insert string value directly + params.pushKV(name, value); + } else { + // parse string as JSON, insert bool/number/object/etc. value + params.pushKV(name, ParseNonRFCJSONValue(value)); + } + } + + return params; +} diff --git a/.svn/pristine/da/dadc4fd30d56f3a8e7e9ef5bb647b087eb28ad50.svn-base b/.svn/pristine/da/dadc4fd30d56f3a8e7e9ef5bb647b087eb28ad50.svn-base new file mode 100644 index 0000000..c6f9b47 --- /dev/null +++ b/.svn/pristine/da/dadc4fd30d56f3a8e7e9ef5bb647b087eb28ad50.svn-base @@ -0,0 +1,569 @@ +# Copyright (c) 2013-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# qt/libzbarqt.a +bin_PROGRAMS += qt/ipchain-qt +EXTRA_LIBRARIES += qt/libbitcoinqt.a + + +# bitcoin qt core # +QT_TS = \ + qt/locale/bitcoin_af.ts \ + qt/locale/bitcoin_af_ZA.ts \ + qt/locale/bitcoin_ar.ts \ + qt/locale/bitcoin_be_BY.ts \ + qt/locale/bitcoin_bg_BG.ts \ + qt/locale/bitcoin_bg.ts \ + qt/locale/bitcoin_ca_ES.ts \ + qt/locale/bitcoin_ca.ts \ + qt/locale/bitcoin_ca@valencia.ts \ + qt/locale/bitcoin_cs.ts \ + qt/locale/bitcoin_cy.ts \ + qt/locale/bitcoin_da.ts \ + qt/locale/bitcoin_de.ts \ + qt/locale/bitcoin_el_GR.ts \ + qt/locale/bitcoin_el.ts \ + qt/locale/bitcoin_en_GB.ts \ + qt/locale/bitcoin_en.ts \ + qt/locale/bitcoin_eo.ts \ + qt/locale/bitcoin_es_AR.ts \ + qt/locale/bitcoin_es_CL.ts \ + qt/locale/bitcoin_es_CO.ts \ + qt/locale/bitcoin_es_DO.ts \ + qt/locale/bitcoin_es_ES.ts \ + qt/locale/bitcoin_es_MX.ts \ + qt/locale/bitcoin_es.ts \ + qt/locale/bitcoin_es_UY.ts \ + qt/locale/bitcoin_es_VE.ts \ + qt/locale/bitcoin_et_EE.ts \ + qt/locale/bitcoin_et.ts \ + qt/locale/bitcoin_eu_ES.ts \ + qt/locale/bitcoin_fa_IR.ts \ + qt/locale/bitcoin_fa.ts \ + qt/locale/bitcoin_fi.ts \ + qt/locale/bitcoin_fr_CA.ts \ + qt/locale/bitcoin_fr_FR.ts \ + qt/locale/bitcoin_fr.ts \ + qt/locale/bitcoin_gl.ts \ + qt/locale/bitcoin_he.ts \ + qt/locale/bitcoin_hi_IN.ts \ + qt/locale/bitcoin_hr.ts \ + qt/locale/bitcoin_hu.ts \ + qt/locale/bitcoin_id_ID.ts \ + qt/locale/bitcoin_it_IT.ts \ + qt/locale/bitcoin_it.ts \ + qt/locale/bitcoin_ja.ts \ + qt/locale/bitcoin_ka.ts \ + qt/locale/bitcoin_kk_KZ.ts \ + qt/locale/bitcoin_ko_KR.ts \ + qt/locale/bitcoin_ku_IQ.ts \ + qt/locale/bitcoin_ky.ts \ + qt/locale/bitcoin_la.ts \ + qt/locale/bitcoin_lt.ts \ + qt/locale/bitcoin_lv_LV.ts \ + qt/locale/bitcoin_mk_MK.ts \ + qt/locale/bitcoin_mn.ts \ + qt/locale/bitcoin_ms_MY.ts \ + qt/locale/bitcoin_nb.ts \ + qt/locale/bitcoin_ne.ts \ + qt/locale/bitcoin_nl.ts \ + qt/locale/bitcoin_pam.ts \ + qt/locale/bitcoin_pl.ts \ + qt/locale/bitcoin_pt_BR.ts \ + qt/locale/bitcoin_pt_PT.ts \ + qt/locale/bitcoin_ro_RO.ts \ + qt/locale/bitcoin_ro.ts \ + qt/locale/bitcoin_ru_RU.ts \ + qt/locale/bitcoin_ru.ts \ + qt/locale/bitcoin_sk.ts \ + qt/locale/bitcoin_sl_SI.ts \ + qt/locale/bitcoin_sq.ts \ + qt/locale/bitcoin_sr@latin.ts \ + qt/locale/bitcoin_sr.ts \ + qt/locale/bitcoin_sv.ts \ + qt/locale/bitcoin_ta.ts \ + qt/locale/bitcoin_th_TH.ts \ + qt/locale/bitcoin_tr_TR.ts \ + qt/locale/bitcoin_tr.ts \ + qt/locale/bitcoin_uk.ts \ + qt/locale/bitcoin_ur_PK.ts \ + qt/locale/bitcoin_uz@Cyrl.ts \ + qt/locale/bitcoin_vi.ts \ + qt/locale/bitcoin_vi_VN.ts \ + qt/locale/bitcoin_zh_CN.ts \ + qt/locale/bitcoin_zh_HK.ts \ + qt/locale/bitcoin_zh.ts \ + qt/locale/bitcoin_zh_TW.ts + +QT_FORMS_UI = \ + qt/forms/cmessagebox.ui \ + qt/forms/intro.ui \ + qt/forms/overviewpage.ui \ + qt/forms/receivecoinsdialog.ui \ + qt/forms/debugwindow.ui \ + qt/forms/sendcoinsdialog.ui \ + qt/forms/logon.ui \ + qt/forms/logondlg.ui \ + qt/forms/walletpassword.ui \ + qt/forms/ipcdialog.ui \ + qt/forms/setdialog.ui \ + qt/forms/exportdialog.ui \ + qt/forms/ipcdetails.ui \ + qt/forms/ipcregister.ui \ + qt/forms/ipcregisterinformation.ui \ + qt/forms/successfultrade.ui \ + qt/forms/ipcinspectiontag.ui \ + qt/forms/ipctransfertransaction.ui \ + qt/forms/ipcauthorizationtransaction.ui \ + qt/forms/settingwidget.ui \ + qt/forms/sendcoinsaffrimwidget.ui \ + qt/forms/passwordsettingwidget.ui \ + qt/forms/addbookwidget.ui \ + qt/forms/recvhistory.ui \ + qt/forms/sendhistory.ui \ + qt/forms/recvipchistory.ui \ + qt/forms/sendipchistory.ui \ + qt/forms/updatedialog.ui \ + qt/forms/infowidget.ui \ + qt/forms/sendresultwidget.ui \ + qt/forms/ipcauthorizationtransaction.ui \ + qt/forms/setrecovery.ui \ + qt/forms/setmessageauthentication.ui \ + qt/forms/setmessageauthenticationtab.ui \ + qt/forms/setmessagesignature.ui \ + qt/forms/walletpagebuttons.ui \ + qt/forms/ipcselectaddress.ui \ + qt/forms/tallyaccount.ui \ + qt/forms/tallyapply.ui \ + qt/forms/tallyclause.ui \ + qt/forms/tallydscribe.ui \ + qt/forms/tallyoutaccount.ui \ + qt/forms/ecoinaddressdialog.ui \ + qt/forms/ecoindialog.ui \ + qt/forms/ecoincreatedialog.ui \ + qt/forms/ecoinselectaddress.ui \ + qt/forms/ecoinsenddialog.ui \ + qt/forms/ecoinsendaffrimdialog.ui \ + qt/forms/ecoinsendresultdialog.ui \ + qt/forms/sendtokenhistory.ui \ + qt/forms/recvtokenhistory.ui + +QT_MOC_CPP = \ + qt/moc_cmessagebox.cpp \ + qt/moc_addresstablemodel.cpp \ + qt/moc_bantablemodel.cpp \ + qt/moc_ipchaingui.cpp \ + qt/moc_ipchainunits.cpp \ + qt/moc_clientmodel.cpp \ + qt/moc_csvmodelwriter.cpp \ + qt/moc_guiutil.cpp \ + qt/moc_intro.cpp \ + qt/moc_macdockiconhandler.cpp \ + qt/moc_macnotificationhandler.cpp \ + qt/moc_notificator.cpp \ + qt/moc_optionsmodel.cpp \ + qt/moc_overviewpage.cpp \ + qt/moc_peertablemodel.cpp \ + qt/moc_paymentserver.cpp \ + qt/moc_qvaluecombobox.cpp \ + qt/moc_clickqlabel.cpp \ + qt/moc_receivecoinsdialog.cpp \ + qt/moc_recentrequeststablemodel.cpp \ + qt/moc_rpcconsole.cpp \ + qt/moc_sendcoinsdialog.cpp \ + qt/moc_splashscreen.cpp \ + qt/moc_trafficgraphwidget.cpp \ + qt/moc_transactionfilterproxy.cpp \ + qt/moc_transactiontablemodel.cpp \ + qt/moc_transactionview.cpp \ + qt/moc_utilitydialog.cpp \ + qt/moc_walletframe.cpp \ + qt/moc_walletmodel.cpp \ + qt/moc_tallyaccount.cpp \ + qt/moc_tallyapply.cpp \ + qt/moc_tallyclause.cpp \ + qt/moc_tallydscribe.cpp \ + qt/moc_tallyoutaccount.cpp \ + qt/moc_walletview.cpp \ + qt/moc_logon.cpp \ + qt/moc_logondlg.cpp \ + qt/moc_walletpassword.cpp \ + qt/moc_ipcdialog.cpp \ + qt/moc_setdialog.cpp \ + qt/moc_exportdialog.cpp \ + qt/moc_ipcdetails.cpp \ + qt/moc_upgradewidget.cpp \ + qt/moc_ipcregister.cpp \ + qt/moc_ipcregisterinformation.cpp \ + qt/moc_successfultrade.cpp \ + qt/moc_ipcinspectiontag.cpp \ + qt/moc_ipctransfertransaction.cpp \ + qt/moc_ipcauthorizationtransaction.cpp \ + qt/moc_settingwidget.cpp \ + qt/moc_sendcoinsaffrimwidget.cpp \ + qt/moc_passwordsettingwidget.cpp \ + qt/moc_addbookwidget.cpp \ + qt/moc_recvhistory.cpp \ + qt/moc_sendhistory.cpp \ + qt/moc_recvipchistory.cpp \ + qt/moc_sendipchistory.cpp \ + qt/moc_CDateEdit.cpp \ + qt/moc_updatedialog.cpp \ + qt/moc_infowidget.cpp \ + qt/moc_sendresultwidget.cpp \ + qt/moc_CDateEdit.cpp \ + qt/moc_setrecovery.cpp \ + qt/moc_setmessageauthentication.cpp \ + qt/moc_setmessageauthenticationtab.cpp \ + qt/moc_setmessagesignature.cpp \ + qt/moc_walletpagebuttons.cpp \ + qt/moc_ipcselectaddress.cpp \ + qt/moc_ecoinaddressdialog.cpp \ + qt/moc_ecoindialog.cpp \ + qt/moc_ecoincreatedialog.cpp \ + qt/moc_ecoinselectaddress.cpp \ + qt/moc_ecoinsenddialog.cpp \ + qt/moc_ecoinsendaffrimdialog.cpp \ + qt/moc_ecoinsendresultdialog.cpp \ + qt/moc_sendtokenhistory.cpp \ + qt/moc_recvtokenhistory.cpp \ + qt/moc_myscrollarea.cpp \ + qt/moc_qthyperlink.cpp \ + qt/moc_MyLabel.cpp + + + +BITCOIN_MM = \ + qt/macdockiconhandler.mm \ + qt/macnotificationhandler.mm + +QT_MOC = \ + qt/ipchain.moc \ + qt/intro.moc \ + qt/overviewpage.moc \ + qt/rpcconsole.moc + +QT_QRC_CPP = qt/qrc_ipchain.cpp +QT_QRC = qt/ipchain.qrc +QT_QRC_LOCALE_CPP = qt/qrc_ipchain_locale.cpp +QT_QRC_LOCALE = qt/ipchain_locale.qrc + +PROTOBUF_CC = qt/paymentrequest.pb.cc +PROTOBUF_H = qt/paymentrequest.pb.h +PROTOBUF_PROTO = qt/paymentrequest.proto + +BITCOIN_QT_H = \ + qt/log/log.h \ + qt/log/stateinfo.h \ + qt/cmessagebox.h \ + qt/addresstablemodel.h \ + qt/bantablemodel.h \ + qt/ipchaingui.h \ + qt/ipchainunits.h \ + qt/clientmodel.h \ + qt/csvmodelwriter.h \ + qt/guiconstants.h \ + qt/guiutil.h \ + qt/intro.h \ + qt/macdockiconhandler.h \ + qt/macnotificationhandler.h \ + qt/networkstyle.h \ + qt/notificator.h \ + qt/optionsmodel.h \ + qt/overviewpage.h \ + qt/paymentrequestplus.h \ + qt/paymentserver.h \ + qt/peertablemodel.h \ + qt/platformstyle.h \ + qt/qvaluecombobox.h \ + qt/clickqlabel.h \ + qt/receivecoinsdialog.h \ + qt/recentrequeststablemodel.h \ + qt/rpcconsole.h \ + qt/sendcoinsdialog.h \ + qt/splashscreen.h \ + qt/trafficgraphwidget.h \ + qt/transactionfilterproxy.h \ + qt/transactionrecord.h \ + qt/transactiontablemodel.h \ + qt/transactionview.h \ + qt/utilitydialog.h \ + qt/walletframe.h \ + qt/json/cJSON.h \ + qt/walletmodel.h \ + qt/walletmodeltransaction.h \ + qt/tallyaccount.h \ + qt/tallyapply.h \ + qt/tallyclause.h \ + qt/tallydscribe.h \ + qt/tallyoutaccount.h \ + qt/walletview.h \ + qt/winshutdownmonitor.h \ + qt/md5thread.h \ + qt/logon.h \ + qt/logondlg.h \ + qt/walletpassword.h \ + qt/ipcdialog.h \ + qt/setdialog.h \ + qt/exportdialog.h \ + qt/ipcdetails.h \ + qt/upgradewidget.h \ + qt/ipcregister.h \ + qt/ipcregisterinformation.h \ + qt/successfultrade.h \ + qt/ipcinspectiontag.h \ + qt/ipctransfertransaction.h \ + qt/ipcauthorizationtransaction.h \ + qt/settingwidget.h \ + qt/sendcoinsaffrimwidget.h \ + qt/passwordsettingwidget \ + qt/addbookwidget.h \ + qt/recvhistory.h \ + qt/sendhistory.h \ + qt/recvipchistory.h \ + qt/sendipchistory.h \ + qt/sendtokenhistory.h \ + qt/recvtokenhistory.h \ + qt/CDateEdit.h \ + qt/updatedialog.h \ + qt/infowidget.h \ + qt/sendresultwidget.h \ + qt/ipcauthorizationtransaction.h \ + qt/CDateEdit.h \ + qt/setrecovery.h \ + qt/setmessageauthentication.h \ + qt/setmessageauthenticationtab.h \ + qt/setmessagesignature.h \ + qt/walletpagebuttons.h \ + qt/ipcselectaddress.h \ + qt/ecoinaddressdialog.h \ + qt/ecoindialog.h \ + qt/ecoincreatedialog.h \ + qt/ecoinselectaddress.h \ + qt/ecoinsenddialog.h \ + qt/ecoinsendaffrimdialog.h \ + qt/ecoinsendresultdialog.h \ + qt/myscrollarea.h \ + qt/qthyperlink.h \ + qt/MyLabel.h + + + +RES_ICONS = \ + qt/res/icons/bitcoin.ico \ + qt/res/icons/bitcoin.png + +BITCOIN_QT_BASE_CPP = \ + qt/log/log.cpp \ + qt/log/stateinfo.cpp \ + qt/cmessagebox.cpp \ + qt/bantablemodel.cpp \ + qt/ipchaingui.cpp \ + qt/ipchainunits.cpp \ + qt/clientmodel.cpp \ + qt/csvmodelwriter.cpp \ + qt/guiutil.cpp \ + qt/intro.cpp \ + qt/networkstyle.cpp \ + qt/notificator.cpp \ + qt/optionsmodel.cpp \ + qt/peertablemodel.cpp \ + qt/platformstyle.cpp \ + qt/qvaluecombobox.cpp \ + qt/rpcconsole.cpp \ + qt/splashscreen.cpp \ + qt/trafficgraphwidget.cpp \ + qt/utilitydialog.cpp \ + qt/md5thread.cpp \ + qt/logon.cpp \ + qt/logondlg.cpp \ + qt/walletpassword.cpp \ + qt/ipcdialog.cpp \ + qt/setdialog.cpp \ + qt/exportdialog.cpp \ + qt/ipcdetails.cpp \ + qt/upgradewidget.cpp \ + qt/ipcregister.cpp \ + qt/ipcregisterinformation.cpp \ + qt/successfultrade.cpp \ + qt/ipcinspectiontag.cpp \ + qt/ipctransfertransaction.cpp \ + qt/ipcauthorizationtransaction.cpp \ + qt/settingwidget.cpp \ + qt/sendcoinsaffrimwidget.cpp \ + qt/passwordsettingwidget.cpp \ + qt/addbookwidget.cpp \ + qt/recvhistory.cpp \ + qt/sendhistory.cpp \ + qt/recvipchistory.cpp \ + qt/sendipchistory.cpp \ + qt/sendtokenhistory.cpp \ + qt/recvtokenhistory.cpp \ + qt/CDateEdit.cpp \ + qt/updatedialog.cpp \ + qt/infowidget.cpp \ + qt/sendresultwidget.cpp \ + qt/ipcauthorizationtransaction.cpp \ + qt/CDateEdit.cpp \ + qt/setrecovery.cpp \ + qt/setmessageauthentication.cpp \ + qt/setmessageauthenticationtab.cpp \ + qt/setmessagesignature.cpp \ + qt/walletpagebuttons.cpp \ + qt/ipcselectaddress.cpp \ + qt/ecoinaddressdialog.cpp \ + qt/ecoindialog.cpp \ + qt/ecoincreatedialog.cpp \ + qt/ecoinselectaddress.cpp \ + qt/ecoinsenddialog.cpp \ + qt/ecoinsendaffrimdialog.cpp \ + qt/ecoinsendresultdialog.cpp \ + qt/myscrollarea.cpp \ + qt/qthyperlink.cpp \ + qt/MyLabel.cpp + +BITCOIN_QT_WINDOWS_CPP = qt/winshutdownmonitor.cpp + +BITCOIN_QT_WALLET_CPP = \ + qt/addresstablemodel.cpp \ + qt/overviewpage.cpp \ + qt/paymentrequestplus.cpp \ + qt/paymentserver.cpp \ + qt/clickqlabel.cpp \ + qt/receivecoinsdialog.cpp \ + qt/recentrequeststablemodel.cpp \ + qt/sendcoinsdialog.cpp \ + qt/transactionfilterproxy.cpp \ + qt/transactionrecord.cpp \ + qt/transactiontablemodel.cpp \ + qt/transactionview.cpp \ + qt/walletframe.cpp \ + qt/json/cJSON.cpp \ + qt/walletmodel.cpp \ + qt/walletmodeltransaction.cpp \ + qt/tallyaccount.cpp \ + qt/tallyapply.cpp \ + qt/tallyclause.cpp \ + qt/tallydscribe.cpp \ + qt/tallyoutaccount.cpp \ + qt/walletview.cpp + +BITCOIN_QT_CPP = $(BITCOIN_QT_BASE_CPP) +if TARGET_WINDOWS +BITCOIN_QT_CPP += $(BITCOIN_QT_WINDOWS_CPP) +endif +if ENABLE_WALLET +BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP) +endif + +RES_IMAGES = + +RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png) + +BITCOIN_RC = qt/res/ipchian-qt-res.rc + +BITCOIN_QT_INCLUDES = -I$(builddir)/qt -I$(srcdir)/qt -I$(srcdir)/qt/forms \ + -I$(builddir)/qt/forms -DQT_NO_KEYWORDS + +qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ + $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS) +qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) + +qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ + $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) + +nodist_qt_libbitcoinqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) $(PROTOBUF_CC) \ + $(PROTOBUF_H) $(QT_QRC_CPP) $(QT_QRC_LOCALE_CPP) + +# forms/foo.h -> forms/ui_foo.h +QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI:.ui=.h)))) + +# Most files will depend on the forms and moc files as includes. Generate them +# before anything else. +$(QT_MOC): $(QT_FORMS_H) +$(qt_libbitcoinqt_a_OBJECTS) $(qt_ipchain_qt_OBJECTS) : | $(QT_MOC) + +#Generating these with a half-written protobuf header leads to wacky results. +#This makes sure it's done. +$(QT_MOC): $(PROTOBUF_H) +$(QT_MOC_CPP): $(PROTOBUF_H) + +# bitcoin-qt binary # +qt_ipchain_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ + $(QT_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS) +qt_ipchain_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) +qt_ipchain_qt_CXXFLAGS += -Lqt -llibqrencode.so +qt_ipchain_qt_SOURCES = qt/ipchain.cpp +if TARGET_DARWIN + qt_ipchain_qt_SOURCES += $(BITCOIN_MM) +endif +if TARGET_WINDOWS + qt_ipchain_qt_SOURCES += $(BITCOIN_RC) +endif +qt_ipchain_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER) +if ENABLE_WALLET +qt_ipchain_qt_LDADD += $(LIBBITCOIN_WALLET) +endif +if ENABLE_ZMQ +qt_ipchain_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) +endif +#qt_ipchain_qt_LDADD += qt/libzbarqt.a + + +qt_ipchain_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ + $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +qt_ipchain_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +qt_ipchain_qt_LIBTOOLFLAGS = --tag CXX + +#locale/foo.ts -> locale/foo.qm +QT_QM=$(QT_TS:.ts=.qm) + +SECONDARY: $(QT_QM) + +$(srcdir)/qt/ipchainstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES) + @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" + $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) PACKAGE_NAME="$(PACKAGE_NAME)" COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" COPYRIGHT_HOLDERS_SUBSTITUTION="$(COPYRIGHT_HOLDERS_SUBSTITUTION)" $(PYTHON) ../share/qt/extract_strings_qt.py $^ + +translate: $(srcdir)/qt/ipchainstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/ipchain.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) + @test -n $(LUPDATE) || echo "lupdate is required for updating translations" + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) $^ -locations relative -no-obsolete -ts $(srcdir)/qt/locale/bitcoin_en.ts + +$(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM) + @test -f $(RCC) + @cp -f $< $(@D)/temp_$( $@ + @rm $(@D)/temp_$( $@ + +CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno + +CLEANFILES += $(CLEAN_QT) + +ipchain_qt_clean: FORCE + rm -f $(CLEAN_QT) $(qt_libbitcoinqt_a_OBJECTS) $(qt_ipchain_qt_OBJECTS) qt/ipchain-qt$(EXEEXT) $(LIBBITCOINQT) + +ipchain_qt : qt/ipchain-qt$(EXEEXT) + +ui_%.h: %.ui + @test -f $(UIC) + @$(MKDIR_P) $(@D) + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(UIC) -o $@ $< || (echo "Error creating $@"; false) + +%.moc: %.cpp + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES) $(MOC_DEFS) $< | \ + $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ + +moc_%.cpp: %.h + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES) $(MOC_DEFS) $< | \ + $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ + +%.qm: %.ts + @test -f $(LRELEASE) + @$(MKDIR_P) $(@D) + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LRELEASE) -silent $< -qm $@ diff --git a/.svn/pristine/dc/dcd55cf3d99364328d561e50f934d121c35787d4.svn-base b/.svn/pristine/dc/dcd55cf3d99364328d561e50f934d121c35787d4.svn-base new file mode 100644 index 0000000..d236097 --- /dev/null +++ b/.svn/pristine/dc/dcd55cf3d99364328d561e50f934d121c35787d4.svn-base @@ -0,0 +1,9426 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/wallet.h" + +#include "base58.h" +#include "checkpoints.h" +#include "chain.h" +#include "wallet/coincontrol.h" +#include "consensus/consensus.h" +#include "consensus/validation.h" +#include "key.h" +#include "keystore.h" +#include "validation.h" +#include "net.h" +#include "policy/policy.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "script/script.h" +#include "script/sign.h" +#include "timedata.h" +#include "txmempool.h" +#include "util.h" +#include "ui_interface.h" +#include "utilmoneystr.h" +#include "univalue.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "univalue/include/univalue.h" +#include "rpc/server.h" +#include "dpoc/DpocInfo.h" +#include "dpoc/TimeService.h" +#include "dpoc/ConsensusAccountPool.h" + +using namespace std; + +CWallet* pwalletMain = NULL; +/** Transaction fee set by the user */ +CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); +unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; +bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; +bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; +bool fWalletRbf = DEFAULT_WALLET_RBF; + +const char * DEFAULT_WALLET_DAT = "wallet.dat"; +const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; + +/** + * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) + * Override with -mintxfee + */ +CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); +/** + * If fee estimation does not have enough data to provide estimates, use this fee instead. + * Has no effect if not using fee estimation + * Override with -fallbackfee + */ +CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); + +const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); + +/** @defgroup mapWallet + * + * @{ + */ + +struct CompareValueOnly +{ + bool operator()(const pair >& t1, + const pair >& t2) const + { + return t1.first < t2.first; + } +}; + +std::string COutput::ToString() const +{ + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); +} + +bool COutput::CanBeSentToOhter() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return false; + return (tx->tx->vout[i].txType == TXOUT_IPCOWNER); +} + +bool COutput::CanBeAuthorizedToOther() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return false; + return (tx->tx->vout[i].ipcLabel.reAuthorize == 1); +} + +bool COutput::CanBeUniqueAuthorizedToOther() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return false; + if (tx->tx->vout[i].txType != TXOUT_IPCOWNER) + return false; + if (tx->tx->vout[i].ipcLabel.uniqueAuthorize == 0) + return true; +} + +int COutput::GetType() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].txType; +} + +int COutput::GetIPCExtendType() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].ipcLabel.ExtendType; +} + +uint32_t COutput::GetIPCStartTime() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + return tx->tx->vout[i].ipcLabel.startTime; +} + +uint32_t COutput::GetIPCStopTime() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + return tx->tx->vout[i].ipcLabel.stopTime; +} + +uint8_t COutput::GetIPCreAuthorize() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].ipcLabel.reAuthorize; +} + +uint8_t COutput::GetIPCUniqAuthorize()const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return -1; + return tx->tx->vout[i].ipcLabel.uniqueAuthorize; +} + +std::string COutput::GetIPCHash() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + return tx->tx->vout[i].ipcLabel.hash.GetHex(); +} + +std::string COutput::GetIPCTitle() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + return tx->tx->vout[i].ipcLabel.labelTitle; +} + +std::string COutput::GetIPCLabel() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + return tx->tx->vout[i].txLabel; +} + +std::string COutput::GetTokenSymbol() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return ""; + int txtype = GetType(); + if (txtype == TXOUT_TOKENREG) + { + return tx->tx->vout[i].tokenRegLabel.getTokenSymbol(); + } + else if (txtype == TXOUT_TOKEN) + { + return tx->tx->vout[i].tokenLabel.getTokenSymbol(); + } + return ""; +} +uint8_t COutput::GetTokenAccuracy() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 10; + int txtype = GetType(); + if (txtype == TXOUT_TOKENREG) + { + return tx->tx->vout[i].tokenRegLabel.accuracy; + } + else if (txtype == TXOUT_TOKEN) + { + return tx->tx->vout[i].tokenLabel.accuracy; + } + return 0; +} + +uint64_t COutput::GetTokenvalue() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + int txtype = GetType(); + if (txtype == TXOUT_TOKENREG) + { + return tx->tx->vout[i].tokenRegLabel.totalCount; + } + else if (txtype == TXOUT_TOKEN) + { + return tx->tx->vout[i].tokenLabel.value; + } + return uint64_t(0); +} +int64_t COutput::GetAssetEntryTime() const{ + if (tx == NULL || tx->tx == NULL || i >= tx->tx->vout.size()) + return 0; + int ntxtype = GetType(); + std::string stripchash = GetIPCHash(); + int64_t ipcstarttime = tx->tx->vout[i].ipcLabel.startTime; + if (ntxtype != TXOUT_IPCOWNER && ntxtype != TXOUT_IPCAUTHORIZATION) + { + return tx->GetTimeOfTokenInChain()>ipcstarttime ? tx->GetTimeOfTokenInChain() : ipcstarttime; + } + if (pwalletMain->mapWallet.count(tx->tx->GetHash()) == 0) + return 0; + CWalletTx curtx = pwalletMain->mapWallet[tx->tx->GetHash()]; + CTransactionRef curptx = curtx.tx; + bool isFindParenttx = false; + int nindex = 0; + while (true) + { + if (NULL == curptx) + return 0; + + nindex = 0; + BOOST_FOREACH(const CTxIn& txin, curptx->vin) + { + nindex++; + isminetype isme= pwalletMain->IsMine(txin); + if (isme == ISMINE_NO) + { + isFindParenttx = true; + break; + } + if (pwalletMain->mapWallet.count(txin.prevout.hash) == 0) + continue; + CWalletTx& prev = pwalletMain->mapWallet[txin.prevout.hash]; + if (NULL == prev.tx) + return 0; + if (prev.tx->vout[txin.prevout.n].txType == ntxtype && stripchash == prev.tx->vout[txin.prevout.n].ipcLabel.hash.GetHex()) + { + ipcstarttime = prev.tx->vout[txin.prevout.n].ipcLabel.startTime; + curtx = prev; + curptx = curtx.tx; + break;; + } + else if (nindex == curptx->vin.size()) + { + isFindParenttx = true; + break; + } + + + } + if (isFindParenttx) + { + return curtx.GetTimeOfTokenInChain()>ipcstarttime ? curtx.GetTimeOfTokenInChain() : ipcstarttime; + } + + } + + return 0; +} + +const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const +{ + LOCK(cs_wallet); + std::map::const_iterator it = mapWallet.find(hash); + if (it == mapWallet.end()) + return NULL; + return &(it->second); +} + +CPubKey CWallet::GenerateNewKey() +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + + CKey secret; + + // Create new metadata + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + + // use HD key derivation if HD was enabled during wallet creation + if (IsHDEnabled()) { + DeriveNewChildKey(metadata, secret); + } else { + secret.MakeNewKey(fCompressed); + } + + // Compressed public keys were introduced in version 0.6.0 + if (fCompressed) + SetMinVersion(FEATURE_COMPRPUBKEY); + + CPubKey pubkey = secret.GetPubKey(); + assert(secret.VerifyPubKey(pubkey)); + + mapKeyMetadata[pubkey.GetID()] = metadata; + UpdateTimeFirstKey(nCreationTime); + + if (!AddKeyPubKey(secret, pubkey)) + throw std::runtime_error(std::string(__func__) + ": AddKey failed"); + return pubkey; +} + +void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret) +{ + // for now we use a fixed keypath scheme of m/0'/0'/k + CKey key; //master key seed (256bit) + CExtKey masterKey; //hd master key + CExtKey accountKey; //key at m/0' + CExtKey externalChainChildKey; //key at m/0'/0' + CExtKey childKey; //key at m/0'/0'/' + + // try to get the master key + if (!GetKey(hdChain.masterKeyID, key)) + throw std::runtime_error(std::string(__func__) + ": Master key not found"); + + masterKey.SetMaster(key.begin(), key.size()); + + // derive m/0' + // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) + masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); + + // derive m/0'/0' + accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); + + // derive child key at next index, skip keys already known to the wallet + do { + // always derive hardened keys + // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range + // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 + externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); + metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'"; + metadata.hdMasterKeyID = hdChain.masterKeyID; + // increment childkey index + hdChain.nExternalChainCounter++; + } while (HaveKey(childKey.key.GetPubKey().GetID())); + secret = childKey.key; + + // update the chain model in the database + if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) + throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); +} + +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) + return false; + + // check if we need to remove from watch-only + CScript script; + script = GetScriptForDestination(pubkey.GetID()); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); + script = GetScriptForRawPubKey(pubkey); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); + + if (!fFileBacked) + return true; + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteKey(pubkey, + secret.GetPrivKey(), + mapKeyMetadata[pubkey.GetID()]); + } + return true; +} + +bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, + const vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + if (!fFileBacked) + return true; + { + LOCK(cs_wallet); + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedKey(vchPubKey, + vchCryptedSecret, + mapKeyMetadata[vchPubKey.GetID()]); + else + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, + vchCryptedSecret, + mapKeyMetadata[vchPubKey.GetID()]); + } + return false; +} + +bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta) +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + UpdateTimeFirstKey(meta.nCreateTime); + mapKeyMetadata[keyID] = meta; + return true; +} + +bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) +{ + return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); +} + +void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) +{ + AssertLockHeld(cs_wallet); + if (nCreateTime <= 1) { + // Cannot determine birthday information, so set the wallet birthday to + // the beginning of time. + nTimeFirstKey = 1; + } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) { + nTimeFirstKey = nCreateTime; + } +} + +bool CWallet::AddCScript(const CScript& redeemScript) +{ + if (!CCryptoKeyStore::AddCScript(redeemScript)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); +} + +bool CWallet::LoadCScript(const CScript& redeemScript) +{ + /* A sanity check was added in pull #3843 to avoid adding redeemScripts + * that never can be redeemed. However, old wallets may still contain + * these. Do not add them to the wallet and warn. */ + if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) + { + std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString(); + LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", + __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); + return true; + } + + return CCryptoKeyStore::AddCScript(redeemScript); +} + +bool CWallet::AddWatchOnly(const CScript& dest) +{ + if (!CCryptoKeyStore::AddWatchOnly(dest)) + return false; + const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)]; + UpdateTimeFirstKey(meta.nCreateTime); + NotifyWatchonlyChanged(true); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta); +} + +bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime) +{ + mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime; + return AddWatchOnly(dest); +} + +bool CWallet::RemoveWatchOnly(const CScript &dest) +{ + AssertLockHeld(cs_wallet); + if (!CCryptoKeyStore::RemoveWatchOnly(dest)) + return false; + if (!HaveWatchOnly()) + NotifyWatchonlyChanged(false); + if (fFileBacked) + if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) + return false; + + return true; +} + +bool CWallet::LoadWatchOnly(const CScript &dest) +{ + return CCryptoKeyStore::AddWatchOnly(dest); +} + +bool CWallet::Unlock(const SecureString& strWalletPassphrase) +{ + CCrypter crypter; + CKeyingMaterial vMasterKey; + + { + LOCK(cs_wallet); + BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + continue; // try another master key + LogPrintf("[crypter.Decrypt]..vMasterKey.size() = %d \n",vMasterKey.size()); + if (CCryptoKeyStore::Unlock(vMasterKey)) + { + curstrWalletPassphrase = strWalletPassphrase; + return true; + } + + } + } + return false; +} + +bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) +{ + bool fWasLocked = IsLocked(); + + { + LOCK(cs_wallet); + Lock(); + + CCrypter crypter; + CKeyingMaterial vMasterKey; + BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + { + int64_t nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + + if (pMasterKey.second.nDeriveIterations < 25000) + pMasterKey.second.nDeriveIterations = 25000; + + LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) + return false; + CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); + if (fWasLocked) + Lock(); + curstrWalletPassphrase = strNewWalletPassphrase; + return true; + } + } + } + + return false; +} + +void CWallet::SetBestChain(const CBlockLocator& loc) +{ + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + walletdb.WriteBestBlock(loc); +} + +bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) +{ + LOCK(cs_wallet); // nWalletVersion + if (nWalletVersion >= nVersion) + return true; + + // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way + if (fExplicit && nVersion > nWalletMaxVersion) + nVersion = FEATURE_LATEST; + + nWalletVersion = nVersion; + + if (nVersion > nWalletMaxVersion) + nWalletMaxVersion = nVersion; + + if (fFileBacked) + { + CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); + if (nWalletVersion > 40000) + pwalletdb->WriteMinVersion(nWalletVersion); + if (!pwalletdbIn) + delete pwalletdb; + } + + return true; +} + +bool CWallet::SetMaxVersion(int nVersion) +{ + + LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion + // cannot downgrade below current version + if (nWalletVersion > nVersion) + return false; + + nWalletMaxVersion = nVersion; + + return true; +} + +set CWallet::GetConflicts(const uint256& txid) const +{ + set result; + AssertLockHeld(cs_wallet); + + std::map::const_iterator it = mapWallet.find(txid); + if (it == mapWallet.end()) + return result; + const CWalletTx& wtx = it->second; + + std::pair range; + + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (mapTxSpends.count(txin.prevout) <= 1) + continue; // No conflict if zero or one spends + range = mapTxSpends.equal_range(txin.prevout); + for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) + result.insert(_it->second); + } + return result; +} + +bool CWallet::HasWalletSpend(const uint256& txid) const +{ + AssertLockHeld(cs_wallet); + auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); + return (iter != mapTxSpends.end() && iter->first.hash == txid); +} + +void CWallet::Flush(bool shutdown) +{ + bitdb.Flush(shutdown); +} + +bool CWallet::Verify() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); + std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); + + LogPrintf("Using wallet %s\n", walletFile); + uiInterface.InitMessage(_("Verifying wallet...")); + + // Wallet file must be a plain filename without a directory + if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile)) + return InitError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string())); + + if (!bitdb.Open(GetDataDir())) + { + // try moving the database env out of the way + boost::filesystem::path pathDatabase = GetDataDir() / "database"; + boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); + try { + boost::filesystem::rename(pathDatabase, pathDatabaseBak); + LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); + } catch (const boost::filesystem::filesystem_error&) { + // failure is ok (well, not really, but it's not worse than what we started with) + } + + // try again + if (!bitdb.Open(GetDataDir())) { + // if it still fails, it probably means we can't even create the database env + return InitError(strprintf(_("Error initializing wallet database environment %s!"), GetDataDir())); + } + } + + if (GetBoolArg("-salvagewallet", false)) + { + // Recover readable keypairs: + if (!CWalletDB::Recover(bitdb, walletFile, true)) + return false; + } + + if (boost::filesystem::exists(GetDataDir() / walletFile)) + { + CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover); + if (r == CDBEnv::RECOVER_OK) + { + InitWarning(strprintf(_("Warning: Wallet file corrupt, data salvaged!" + " Original %s saved as %s in %s; if" + " your balance or transactions are incorrect you should" + " restore from a backup."), + walletFile, "wallet.{timestamp}.bak", GetDataDir())); + } + if (r == CDBEnv::RECOVER_FAIL){ + LogPrintf("Using wallet %s InitError r == CDBEnv::RECOVER_FAIL\n", walletFile); + // return InitError(strprintf(_("%s corrupt, salvage failed"), walletFile)); + } + } + + return true; +} + +void CWallet::SyncMetaData(pair range) +{ + // We want all the wallet transactions in range to have the same metadata as + // the oldest (smallest nOrderPos). + // So: find smallest nOrderPos: + + int nMinOrderPos = std::numeric_limits::max(); + const CWalletTx* copyFrom = NULL; + for (TxSpends::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + int n = mapWallet[hash].nOrderPos; + if (n < nMinOrderPos) + { + nMinOrderPos = n; + copyFrom = &mapWallet[hash]; + } + } + // Now copy data from copyFrom to rest: + for (TxSpends::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + CWalletTx* copyTo = &mapWallet[hash]; + if (copyFrom == copyTo) continue; + if (!copyFrom->IsEquivalentTo(*copyTo)) continue; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + // fTimeReceivedIsTxTime not copied on purpose + // nTimeReceived not copied on purpose + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + // nOrderPos not copied on purpose + // cached members not copied on purpose + } +} + +/** + * Outpoint is spent if any non-conflicted transaction + * spends it: + */ +bool CWallet::IsSpent(const uint256& hash, unsigned int n) const +{ + + const COutPoint outpoint(hash, n); + pair range; + range = mapTxSpends.equal_range(outpoint); + + + for (TxSpends::const_iterator it = range.first; it != range.second; ++it) + { + const uint256& wtxid = it->second; + std::map::const_iterator mit = mapWallet.find(wtxid); + if (mit != mapWallet.end()) { + int depth = mit->second.GetDepthInMainChain(); + if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) + return true; // Spent + } + } + return false; +} + +void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) +{ + mapTxSpends.insert(make_pair(outpoint, wtxid)); + + pair range; + range = mapTxSpends.equal_range(outpoint); + SyncMetaData(range); +} + + +void CWallet::AddToSpends(const uint256& wtxid) +{ + + assert(mapWallet.count(wtxid)); + CWalletTx& thisTx = mapWallet[wtxid]; + if (thisTx.IsCoinBase()) // Coinbases don't spend anything! + return; + BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin) + AddToSpends(txin.prevout, wtxid); +} + +void CWallet::RemoveFromSpends(const COutPoint& outpoint, const uint256& wtxid) +{ + mapTxSpends.erase(outpoint); +} + + +void CWallet::RemoveFromSpends(const uint256& wtxid) +{ + int count = mapWallet.count(wtxid); + assert(count); + CWalletTx& thisTx = mapWallet[wtxid]; + if (thisTx.IsCoinBase()) // Coinbases don't spend anything! + return; + + BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin) + RemoveFromSpends(txin.prevout, wtxid); +} + + +bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) +{ + if (IsCrypted()) + return false; + LogPrintf("[EncryptWallet]:in ......................\n"); + CKeyingMaterial vMasterKey; + + vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); + GetStrongRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + + CMasterKey kMasterKey; + + kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); + GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + + CCrypter crypter; + int64_t nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + + if (kMasterKey.nDeriveIterations < 25000) + kMasterKey.nDeriveIterations = 25000; + + LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) + return false; + LogPrintf("[EncryptWallet]:fFileBacked = %d \n",fFileBacked); + { + LOCK(cs_wallet); + mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; + if (fFileBacked) + { + assert(!pwalletdbEncryption); + pwalletdbEncryption = new CWalletDB(strWalletFile); + if (!pwalletdbEncryption->TxnBegin()) { + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + return false; + } + pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); + } + + if (!EncryptKeys(vMasterKey)) + { + if (fFileBacked) { + pwalletdbEncryption->TxnAbort(); + delete pwalletdbEncryption; + } + // We now probably have half of our keys encrypted in memory, and half not... + // die and let the user reload the unencrypted wallet. + assert(false); + } + + // Encryption was introduced in version 0.4.0 + SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); + + if (fFileBacked) + { + if (!pwalletdbEncryption->TxnCommit()) { + delete pwalletdbEncryption; + // We now have keys encrypted in memory, but not on disk... + // die to avoid confusion and let the user reload the unencrypted wallet. + assert(false); + } + + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + } + + Lock(); + Unlock(strWalletPassphrase); + + // if we are using HD, replace the HD master key (seed) with a new one + if (IsHDEnabled()) { + CKey key; + CPubKey masterPubKey = GenerateNewHDMasterKey(); + if (!SetHDMasterKey(masterPubKey)) + return false; + } + + NewKeyPool(); + Lock(); + + // Need to completely rewrite the wallet file; if we don't, bdb might keep + // bits of the unencrypted private key in slack space in the database file. + CDB::Rewrite(strWalletFile); + + } + NotifyStatusChanged(this); + return true; +} + +DBErrors CWallet::ReorderTransactions() +{ + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + + // Old wallets didn't have any defined order for transactions + // Probably a bad idea to change the output of this + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + walletdb.ListAccountCreditDebit("", acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + nOrderPosNext = 0; + std::vector nOrderPosOffsets; + for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + + if (nOrderPos == -1) + { + nOrderPos = nOrderPosNext++; + nOrderPosOffsets.push_back(nOrderPos); + + if (pwtx) + { + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + else + if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + else + { + int64_t nOrderPosOff = 0; + BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) + { + if (nOrderPos >= nOffsetStart) + ++nOrderPosOff; + } + nOrderPos += nOrderPosOff; + nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); + + if (!nOrderPosOff) + continue; + + // Since we're changing the order, write it back + if (pwtx) + { + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + else + if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + } + walletdb.WriteOrderPosNext(nOrderPosNext); + + return DB_LOAD_OK; +} + +int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) +{ + AssertLockHeld(cs_wallet); // nOrderPosNext + int64_t nRet = nOrderPosNext++; + if (pwalletdb) { + pwalletdb->WriteOrderPosNext(nOrderPosNext); + } else { + CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); + } + return nRet; +} + +bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) +{ + CWalletDB walletdb(strWalletFile); + if (!walletdb.TxnBegin()) + return false; + + int64_t nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.nOrderPos = IncOrderPosNext(&walletdb); + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + AddAccountingEntry(debit, &walletdb); + + // Credit + CAccountingEntry credit; + credit.nOrderPos = IncOrderPosNext(&walletdb); + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + AddAccountingEntry(credit, &walletdb); + + if (!walletdb.TxnCommit()) + return false; + + return true; +} + +bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew) +{ + CWalletDB walletdb(strWalletFile); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + if (!bForceNew) { + if (!account.vchPubKey.IsValid()) + bForceNew = true; + else { + // Check if the current key has been used + CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); + for (map::iterator it = mapWallet.begin(); + it != mapWallet.end() && account.vchPubKey.IsValid(); + ++it) + BOOST_FOREACH(const CTxOut& txout, (*it).second.tx->vout) + if (txout.scriptPubKey == scriptPubKey) { + bForceNew = true; + break; + } + } + } + + // Generate a new key + if (bForceNew) { + if (!GetKeyFromPool(account.vchPubKey)) + return false; + + SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); + walletdb.WriteAccount(strAccount, account); + } + + pubKey = account.vchPubKey; + + return true; +} + +void CWallet::MarkDirty() +{ + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + item.second.MarkDirty(); + } +} + +bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) +{ + LOCK(cs_wallet); + + auto mi = mapWallet.find(originalHash); + + // There is a bug if MarkReplaced is not called on an existing wallet transaction. + assert(mi != mapWallet.end()); + + CWalletTx& wtx = (*mi).second; + + // Ensure for now that we're not overwriting data + assert(wtx.mapValue.count("replaced_by_txid") == 0); + + wtx.mapValue["replaced_by_txid"] = newHash.ToString(); + + CWalletDB walletdb(strWalletFile, "r+"); + + bool success = true; + if (!walletdb.WriteTx(wtx)) { + LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString()); + success = false; + } + + NotifyTransactionChanged(this, originalHash, CT_UPDATED); + + return success; +} + +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) +{ + LOCK(cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); + + uint256 hash = wtxIn.GetHash(); + + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + wtx.BindWallet(this); + bool fInsertedNew = ret.second; + if (fInsertedNew) + { + wtx.nTimeReceived = GetAdjustedTime(); + wtx.nOrderPos = IncOrderPosNext(&walletdb); + wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + + wtx.nTimeSmart = wtx.nTimeReceived; + if (!wtxIn.hashUnset()) + { + if (mapBlockIndex.count(wtxIn.hashBlock)) + { + int64_t latestNow = wtx.nTimeReceived; + int64_t latestEntry = 0; + { + // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future + int64_t latestTolerated = latestNow + 300; + const TxItems & txOrdered = wtxOrdered; + for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx == &wtx) + continue; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t nSmartTime; + if (pwtx) + { + nSmartTime = pwtx->nTimeSmart; + if (!nSmartTime) + nSmartTime = pwtx->nTimeReceived; + } + else + nSmartTime = pacentry->nTime; + if (nSmartTime <= latestTolerated) + { + latestEntry = nSmartTime; + if (nSmartTime > latestNow) + latestNow = nSmartTime; + break; + } + } + } + + int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); + wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); + } + else + LogPrintf("AddToWallet(): found %s in block %s not in index\n", + wtxIn.GetHash().ToString(), + wtxIn.hashBlock.ToString()); + } + AddToSpends(hash); + } + + bool fUpdated = false; + if (!fInsertedNew) + { + + // Merge + if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + // If no longer abandoned, update + if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) + { + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + } + + //// debug print + LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!walletdb.WriteTx(wtx)) + { + return false; + } + + + // Break debit/credit balance caches: + wtx.MarkDirty(); + + // Notify UI of new or updated transaction + NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + return true; +} + +bool CWallet::RemoveFromWallet(const CWalletTx& wtxIn, bool fFlushOnClose) +{ + LOCK(cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); + + uint256 hash = wtxIn.GetHash(); + + RemoveFromSpends(hash); + + bool fUpdated = false; + //// debug print + LogPrintf("RemoveFromWallet %s %s\n", wtxIn.GetHash().ToString(), "removed"); + + int ret = mapWallet.erase(hash); + + // Write to disk + if (!walletdb.EraseTx(hash)) + return false; + + // Notify UI of new or updated transaction + NotifyTransactionChanged(this, hash, CT_DELETED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if (!strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + return true; + +} +bool CWallet::LoadToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + + mapWallet[hash] = wtxIn; + CWalletTx& wtx = mapWallet[hash]; + wtx.BindWallet(this); + wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + AddToSpends(hash); + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { + if (mapWallet.count(txin.prevout.hash)) { + CWalletTx& prevtx = mapWallet[txin.prevout.hash]; + if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { + MarkConflicted(prevtx.hashBlock, wtx.GetHash()); + } + } + } + + return true; +} + +/** + * Add a transaction to the wallet, or update it. pIndex and posInBlock should + * be set when the transaction was known to be included in a block. When + * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not + * updated in AddToWallet, but notifications happen and cached balances are + * marked dirty. + * If fUpdate is true, existing transactions will be updated. + * TODO: One exception to this is that the abandoned state is cleared under the + * assumption that any further notification of a transaction that was considered + * abandoned is an indication that it is not safe to be considered abandoned. + * Abandoned state should probably be more carefuly tracked via different + * posInBlock signals or by checking mempool presence when necessary. + */ +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) +{ + { + AssertLockHeld(cs_wallet); + + if (posInBlock != -1) { + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + std::pair range = mapTxSpends.equal_range(txin.prevout); + while (range.first != range.second) { + if (range.first->second != tx.GetHash()) { + LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); + MarkConflicted(pIndex->GetBlockHash(), range.first->second); + } + range.first++; + } + } + } + + bool fExisted = mapWallet.count(tx.GetHash()) != 0; + if (fExisted && !fUpdate) + { + std::cout << "fExisted :" << fExisted << " ,fUpdate : " << fUpdate << std::endl; + return false; + } + if (fExisted || IsMine(tx) || IsFromMe(tx) || IsFromMe(tx,1)) + { + CWalletTx wtx(this, MakeTransactionRef(tx)); + + // Get merkle branch if transaction was found in a block + if (posInBlock != -1) + wtx.SetMerkleBranch(pIndex, posInBlock); + return AddToWallet(wtx, false); + } + } + return false; +} + +bool CWallet::AbandonTransaction(const uint256& hashTx) +{ + LOCK2(cs_main, cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+"); + + std::set todo; + std::set done; + + // Can't mark abandoned if confirmed or in mempool + assert(mapWallet.count(hashTx)); + CWalletTx& origtx = mapWallet[hashTx]; + if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { + return false; + } + + todo.insert(hashTx); + + while (!todo.empty()) { + uint256 now = *todo.begin(); + todo.erase(now); + done.insert(now); + assert(mapWallet.count(now)); + CWalletTx& wtx = mapWallet[now]; + int currentconfirm = wtx.GetDepthInMainChain(); + // If the orig tx was not in block, none of its spends can be + assert(currentconfirm <= 0); + // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon} + if (currentconfirm == 0 && !wtx.isAbandoned()) { + // If the orig tx was not in block/mempool, none of its spends can be in mempool + assert(!wtx.InMempool()); + wtx.nIndex = -1; + wtx.setAbandoned(); + wtx.MarkDirty(); + walletdb.WriteTx(wtx); + NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED); + // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too + TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(hashTx, 0)); + while (iter != mapTxSpends.end() && iter->first.hash == now) { + if (!done.count(iter->second)) { + todo.insert(iter->second); + } + iter++; + } + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be recomputed + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } + } + } + + return true; +} + +void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) +{ + LOCK2(cs_main, cs_wallet); + + int conflictconfirms = 0; + if (mapBlockIndex.count(hashBlock)) { + CBlockIndex* pindex = mapBlockIndex[hashBlock]; + if (chainActive.Contains(pindex)) { + conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); + } + } + // If number of conflict confirms cannot be determined, this means + // that the block is still unknown or not yet part of the main chain, + // for example when loading the wallet during a reindex. Do nothing in that + // case. + if (conflictconfirms >= 0) + return; + + // Do not flush the wallet here for performance reasons + CWalletDB walletdb(strWalletFile, "r+", false); + + std::set todo; + std::set done; + + todo.insert(hashTx); + + while (!todo.empty()) { + uint256 now = *todo.begin(); + todo.erase(now); + done.insert(now); + assert(mapWallet.count(now)); + CWalletTx& wtx = mapWallet[now]; + int currentconfirm = wtx.GetDepthInMainChain(); + if (conflictconfirms < currentconfirm) { + // Block is 'more conflicted' than current confirm; update. + // Mark transaction as conflicted with this block. + wtx.nIndex = -1; + wtx.hashBlock = hashBlock; + wtx.MarkDirty(); + walletdb.WriteTx(wtx); + // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too + TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); + while (iter != mapTxSpends.end() && iter->first.hash == now) { + if (!done.count(iter->second)) { + todo.insert(iter->second); + } + iter++; + } + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be recomputed + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } + } + } +} + +void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) +{ + LOCK2(cs_main, cs_wallet); + + if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true)) + return; // Not one of ours + + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be + // recomputed, also: + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } +} + + +isminetype CWallet::IsMine(const CTxIn &txin) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.tx->vout.size()) + return IsMine(prev.tx->vout[txin.prevout.n]); + } + } + return ISMINE_NO; +} + + + +// Note that this function doesn't distinguish between a 0-valued input, +// and a not-"is mine" (according to the filter) input. +CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.tx->vout.size()) + if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) + return prev.tx->vout[txin.prevout.n].nValue; + } + } + return 0; +} + +isminetype CWallet::IsMine(const CTxOut& txout) const +{ + return ::IsMine(*this, txout.scriptPubKey); +} + + + +CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const +{ + if (!MoneyRange(txout.nValue)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + return ((IsMine(txout) & filter) ? txout.nValue : 0); +} + +bool CWallet::IsChange(const CTxOut& txout) const +{ + // TODO: fix handling of 'change' outputs. The assumption is that any + // payment to a script that is ours, but is not in the address book + // is change. That assumption is likely to break when we implement multisignature + // wallets that return change back into a multi-signature-protected address; + // a better way of identifying which outputs are 'the send' and which are + // 'the change' will need to be implemented (maybe extend CWalletTx to remember + // which output, if any, was change). + if (::IsMine(*this, txout.scriptPubKey)) + { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + return true; + + LOCK(cs_wallet); + if (!mapAddressBook.count(address)) + return true; + } + return false; +} + +CAmount CWallet::GetChange(const CTxOut& txout) const +{ + if (!MoneyRange(txout.nValue)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + return (IsChange(txout) ? txout.nValue : 0); +} + +bool CWallet::IsMine(const CTransaction& tx) const +{ + BOOST_FOREACH(const CTxOut& txout, tx.vout) + if (IsMine(txout)) + return true; + return false; +} + + + +bool CWallet::IsFromMe(const CTransaction& tx) const +{ + return (GetDebit(tx, ISMINE_ALL) > 0); +} + +bool CWallet::IsFromMe(const CTransaction& tx ,int type) const +{ + BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (IsMine(txin)) + return true; + return false; +} + +CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const +{ + CAmount nDebit = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nDebit += GetDebit(txin, filter); + if (!MoneyRange(nDebit)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nDebit; +} + +bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const +{ + LOCK(cs_wallet); + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + auto mi = mapWallet.find(txin.prevout.hash); + if (mi == mapWallet.end()) + return false; // any unknown inputs can't be from us + + const CWalletTx& prev = (*mi).second; + + if (txin.prevout.n >= prev.tx->vout.size()) + return false; // invalid input! + + if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) + return false; + } + return true; +} + +CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const +{ + CAmount nCredit = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nCredit += GetCredit(txout, filter); + if (!MoneyRange(nCredit)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nCredit; +} + +CAmount CWallet::GetChange(const CTransaction& tx) const +{ + CAmount nChange = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nChange = txout.nValue; + if (!MoneyRange(nChange)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nChange; +} + +CPubKey CWallet::GenerateNewHDMasterKey() +{ + CKey key; + key.MakeNewKey(true); + + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + + // calculate the pubkey + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + + // set the hd keypath to "m" -> Master, refers the masterkeyid to itself + metadata.hdKeypath = "m"; + metadata.hdMasterKeyID = pubkey.GetID(); + + { + LOCK(cs_wallet); + + // mem store the metadata + mapKeyMetadata[pubkey.GetID()] = metadata; + + // write the key&metadata to the database + if (!AddKeyPubKey(key, pubkey)) + throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); + } + + return pubkey; +} + +bool CWallet::SetHDMasterKey(const CPubKey& pubkey) +{ + LOCK(cs_wallet); + + // ensure this wallet.dat can only be opened by clients supporting HD + SetMinVersion(FEATURE_HD); + + // store the keyid (hash160) together with + // the child index counter in the database + // as a hdchain object + CHDChain newHdChain; + newHdChain.masterKeyID = pubkey.GetID(); + SetHDChain(newHdChain, false); + + return true; +} + +bool CWallet::SetHDChain(const CHDChain& chain, bool memonly) +{ + LOCK(cs_wallet); + if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain)) + throw runtime_error(std::string(__func__) + ": writing chain failed"); + + hdChain = chain; + return true; +} + +bool CWallet::IsHDEnabled() +{ + return !hdChain.masterKeyID.IsNull(); +} + +int64_t CWalletTx::GetTxTime() const +{ + int64_t n = nTimeSmart; + return n ? n : nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + { + LOCK(pwallet->cs_wallet); + if (IsCoinBase()) + { + // Generated block + if (!hashUnset()) + { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); + if (mi != pwallet->mapRequestCount.end()) + { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && !hashUnset()) + { + map::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock); + if (_mi != pwallet->mapRequestCount.end()) + nRequests = (*_mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(list& listReceived, + list& listSent, CAmount& nFee, string& strSentAccount, const isminefilter& filter) const +{ + nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + // Compute fee: + CAmount nDebit = GetDebit(filter); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + CAmount nValueOut = tx->GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. + for (unsigned int i = 0; i < tx->vout.size(); ++i) + { + const CTxOut& txout = tx->vout[i]; + isminetype fIsMine = pwallet->IsMine(txout); + // Only need to handle txouts if AT LEAST one of these is true: + // 1) they debit from us (sent) + // 2) the output is to us (received) + if (nDebit > 0) + { + // Don't report 'change' txouts + if (pwallet->IsChange(txout)) + continue; + } + else if (!(fIsMine & filter)) + continue; + + // In either case, we need to get the destination address + CTxDestination address; + + if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable()) + { + LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString()); + address = CNoDestination(); + } + + COutputEntry output = {address, txout.nValue, (int)i}; + + // If we are debited by the transaction, add the output as a "sent" entry + if (nDebit > 0) + listSent.push_back(output); + + // If we are receiving the output, add it as a "received" entry + if (fIsMine & filter) + listReceived.push_back(output); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived, + CAmount& nSent, CAmount& nFee, const isminefilter& filter) const +{ + nReceived = nSent = nFee = 0; + + CAmount allFee; + string strSentAccount; + list listReceived; + list listSent; + GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); + + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const COutputEntry& s, listSent) + nSent += s.amount; + nFee = allFee; + } + { + LOCK(pwallet->cs_wallet); + BOOST_FOREACH(const COutputEntry& r, listReceived) + { + if (pwallet->mapAddressBook.count(r.destination)) + { + map::const_iterator mi = pwallet->mapAddressBook.find(r.destination); + if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount) + nReceived += r.amount; + } + else if (strAccount.empty()) + { + nReceived += r.amount; + } + } + } +} + +/** + * Scan the block chain (starting in pindexStart) for transactions + * from or to us. If fUpdate is true, found transactions that already + * exist in the wallet will be updated. + * + * Returns pointer to the first block in the last contiguous range that was + * successfully scanned. + * + */ +CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + CBlockIndex* ret = nullptr; + int64_t nNow = GetTime(); + const CChainParams& chainParams = Params(); + + CBlockIndex* pindex = pindexStart; + { + LOCK2(cs_main, cs_wallet); + + // no need to read and scan block, if block was created before + // our wallet birthday (as adjusted for block time variability) + while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) + pindex = chainActive.Next(pindex); + + ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup + double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); + double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); + while (pindex) + { + if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) + ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); + + CBlock block; + if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { + for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { + AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate); + } + if (!ret) { + ret = pindex; + } + } else { + ret = nullptr; + } + pindex = chainActive.Next(pindex); + if (GetTime() >= nNow + 60) { + nNow = GetTime(); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); + } + } + ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI + } + return ret; +} + +void CWallet::ReacceptWalletTransactions() +{ + // If transactions aren't being broadcasted, don't let them into local mempool either + if (!fBroadcastTransactions) + return; + LOCK2(cs_main, cs_wallet); + std::map mapSorted; + + // Sort pending wallet transactions based on their initial wallet insertion order + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + const uint256& wtxid = item.first; + CWalletTx& wtx = item.second; + assert(wtx.GetHash() == wtxid); + + int nDepth = wtx.GetDepthInMainChain(); + + if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { + mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); + } + } + LogPrintf("mapSorted.size() = %d \n",mapSorted.size()); + // Try to add wallet transactions to memory pool + BOOST_FOREACH(PAIRTYPE(const int64_t, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *(item.second); + + LOCK(mempool.cs); + CValidationState state; + wtx.AcceptToMemoryPool(maxTxFee, state); + } + LogPrintf("Done[mapSorted] \n"); +} + +bool CWalletTx::RelayWalletTransaction(CConnman* connman) +{ + assert(pwallet->GetBroadcastTransactions()); + if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0) + { + CValidationState state; + /* GetDepthInMainChain already catches known conflicts. */ + if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) { + LogPrintf("Relaying wtx %s\n", GetHash().ToString()); + if (connman) { + CInv inv(MSG_TX, GetHash()); + connman->ForEachNode([&inv](CNode* pnode) + { + pnode->PushInventory(inv); + }); + return true; + } + } + } + return false; +} + +set CWalletTx::GetConflicts() const +{ + set result; + if (pwallet != NULL) + { + uint256 myHash = GetHash(); + result = pwallet->GetConflicts(myHash); + result.erase(myHash); + } + return result; +} + +CAmount CWalletTx::GetDebit(const isminefilter& filter) const +{ + if (tx->vin.empty()) + return 0; + + CAmount debit = 0; + if(filter & ISMINE_SPENDABLE) + { + if (fDebitCached) + debit += nDebitCached; + else + { + nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); + fDebitCached = true; + debit += nDebitCached; + } + } + if(filter & ISMINE_WATCH_ONLY) + { + if(fWatchDebitCached) + debit += nWatchDebitCached; + else + { + nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); + fWatchDebitCached = true; + debit += nWatchDebitCached; + } + } + return debit; +} + +CAmount CWalletTx::GetCredit(const isminefilter& filter) const +{ + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + CAmount credit = 0; + if (filter & ISMINE_SPENDABLE) + { + // GetBalance can assume transactions in mapWallet won't change + if (fCreditCached) + credit += nCreditCached; + else + { + nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fCreditCached = true; + credit += nCreditCached; + } + } + if (filter & ISMINE_WATCH_ONLY) + { + if (fWatchCreditCached) + credit += nWatchCreditCached; + else + { + nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fWatchCreditCached = true; + credit += nWatchCreditCached; + } + } + return credit; +} + +CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const +{ + if (pwallet == 0) + return 0; + bool reAvailableCreditCached = false; + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached ) + return nAvailableCreditCached; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < tx->vout.size(); i++) + { + if (!pwallet->IsSpent(hashTx, i)) + { + const CTxOut &txout = tx->vout[i]; + //Remove the COINS that have been locked in. add by xxy 20171030 + if (txout.txType == TXOUT_CAMPAIGN && + txout.devoteLabel.ExtendType == TYPE_CONSENSUS_REGISTER) + { + reAvailableCreditCached = true; //The utxo that applies to join the type in tx will need to be re-checked next time, and the state will change! + if (!CConsensusAccountPool::Instance().IsAviableUTXO(hashTx)) + { + continue; + } + + } + //end + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + //add by xxy + if (reAvailableCreditCached) + fAvailableCreditCached = false; + //end + return nCredit; +} + +//add by xxy 20171219 +CAmount CWalletTx::GetLockCredit(bool fUseCache) const +{ + if (pwallet == 0) + return 0; + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return 0; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < tx->vout.size(); i++) + { + if (!pwallet->IsSpent(hashTx, i)) + { + const CTxOut &txout = tx->vout[i]; + if (txout.txType == TXOUT_CAMPAIGN && + txout.devoteLabel.ExtendType == TYPE_CONSENSUS_REGISTER) + { + if (!CConsensusAccountPool::Instance().IsAviableUTXO(hashTx)) + { + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + continue; + } + + } + //end + + + } + } + fAvailableCreditCached = false; + + return nCredit; +} +//end +CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureWatchCreditCached) + return nImmatureWatchCreditCached; + nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fImmatureWatchCreditCached = true; + return nImmatureWatchCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const +{ + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableWatchCreditCached) + return nAvailableWatchCreditCached; + + CAmount nCredit = 0; + for (unsigned int i = 0; i < tx->vout.size(); i++) + { + if (!pwallet->IsSpent(GetHash(), i)) + { + const CTxOut &txout = tx->vout[i]; + nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableWatchCreditCached = nCredit; + fAvailableWatchCreditCached = true; + return nCredit; +} + +CAmount CWalletTx::GetChange() const +{ + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; +} + +bool CWalletTx::InMempool() const +{ + LOCK(mempool.cs); + if (mempool.exists(GetHash())) { + return true; + } + return false; +} + +bool CWalletTx::IsTrusted() const +{ + + int nDepth = GetDepthInMainChain(); + if (nDepth >= 1) + return true; + if (nDepth < 0) + return false; + if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit + return false; + + // Don't trust unconfirmed transactions from us unless they are in the mempool. + if (!InMempool()) + return false; + + // Trusted if all inputs are from us and are in the mempool: + BOOST_FOREACH(const CTxIn& txin, tx->vin) + { + // Transactions not sent by us: not trusted + const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); + if (parent == NULL) + return false; + const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) + return false; + } + return true; +} + +bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const +{ + CMutableTransaction tx1 = *this->tx; + CMutableTransaction tx2 = *_tx.tx; + for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript(); + for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript(); + return CTransaction(tx1) == CTransaction(tx2); +} + +std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) +{ + std::vector result; + + LOCK(cs_wallet); + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast if newer than nTime: + if (wtx.nTimeReceived > nTime) + continue; + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + if (wtx.RelayWalletTransaction(connman)) + result.push_back(wtx.GetHash()); + } + return result; +} + +void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + if (GetTime() < nNextResend || !fBroadcastTransactions) + return; + bool fFirst = (nNextResend == 0); + nNextResend = GetTime() + GetRand(30 * 60); + if (fFirst) + return; + + // Only do it if there's been a new block since last time + if (nBestBlockTime < nLastResend) + return; + nLastResend = GetTime(); + + // Rebroadcast unconfirmed txes older than 5 minutes before the last + // block was found: + std::vector relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman); + if (!relayed.empty()) + LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); +} + + + + + + + + +CAmount CWallet::GetDeposit() const +{ + CAmount nTotal = 0; + std::string strPublicKey; + uint160 pkhash; + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetLockCredit(); + + } + } + + return nTotal; +} +int CWallet::GetDepthofJoinTX() +{ + int depth = 0; + std::string strPublicKey; + uint256 hash; + uint160 pkhash; + if (CDpocInfo::Instance().GetLocalAccount(strPublicKey)) + { + pkhash.SetHex(strPublicKey); + hash = CConsensusAccountPool::Instance().GetTXhashBypkhash(pkhash); + if (hash.IsNull()) + return depth; + if (mapWallet.count(hash)>0) + { + const CWalletTx* pcoin = &mapWallet[hash]; + return pcoin->GetDepthInMainChain(); + } + + } + return depth; + +} +CAmount CWallet::GetBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->GetDepthInMainChain() >= nTxConfirmTarget && pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableCredit(); + + } + } + + return nTotal; +} +CAmount CWallet::GetUnconfirmedBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + // if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) + if (pcoin->GetDepthInMainChain() < nTxConfirmTarget || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())) + nTotal += pcoin->GetAvailableCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetImmatureBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetWatchOnlyBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + + return nTotal; +} + +CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetImmatureWatchOnlyBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); + } + } + return nTotal; +} + +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + const CWalletTx* pcoin = &(*it).second; + +// if (!CheckIPCFinalTx(*pcoin)) +// continue; + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } +} +//add by xxy +void CWallet::AvailableNormalCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + const CWalletTx* pcoin = &(*it).second; + + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + + if (nDepth < nTxConfirmTarget) + continue; + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))&& + (pcoin->tx->vout[i].txType == TXOUT_NORMAL || (pcoin->tx->vout[i].txType == TXOUT_CAMPAIGN && pcoin->tx->vout[i].devoteLabel.ExtendType == TYPE_CONSENSUS_REGISTER && + CConsensusAccountPool::Instance().IsAviableUTXO(pcoin->tx->GetHash())))) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } + +} +void CWallet::AvailableIPCCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + + const CWalletTx* pcoin = &(*it).second; + + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue >= 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))&& + (pcoin->tx->vout[i].txType == TXOUT_IPCOWNER || pcoin->tx->vout[i].txType == TXOUT_IPCAUTHORIZATION)) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } +} +void CWallet::ListTokenBalance(std::map& TokenList) +{ + TokenList.clear(); + UpdateTokenBalanceList(); + std::map::iterator it; + for (it = TokenValueMap.begin(); it != TokenValueMap.end();it++) + { + TokenList.insert(std::make_pair(it->first,it->second)); + } + return; +} +uint8_t CWallet::GetAccuracyBySymbol(std::string& tokensymbol) +{ + if (tokenDataMap.count(tokensymbol) == 0) + return 10; + return tokenDataMap[tokensymbol].accuracy; +} +uint32_t CWallet::GetIssueDateBySymbol(std::string& tokensymbol) +{ + if (tokenDataMap.count(tokensymbol) == 0) + return 0; + return tokenDataMap[tokensymbol].issueDate; +} +typedef std::multimap::iterator sit; +bool CWallet::GetSymbolbalance(std::string& tokensymbol, uint64_t& value) +{ + value = 0; + std::vector vAvailableTokenCoins; + const CCoinControl *coinControl = NULL; + AvailableTokenCoins(vAvailableTokenCoins, true, coinControl); + uint8_t txtype = -1; + std::string strsymbol = ""; + uint64_t nvalue = 0; + bool isFind = false; + for (const auto& coin : vAvailableTokenCoins) + { + if (coin.tx->GetDepthInMainChain()tx->vout[coin.i].txType; + if (txtype == 4) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenRegLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenRegLabel.totalCount; + } + else if (txtype == 5) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenLabel.value; + } + if (strsymbol == tokensymbol) //Symbol symbol is consistent + { + isFind = true; + value += nvalue; + if (txtype == 4) //There could be only one token registered + return isFind; + } + if (tokensymbol != strsymbol) + continue; + } + + return isFind; +} +bool CWallet::IsHaveTheTokensymbol(std::string&tokensymbol) +{ + std::string str = ""; + for (unsigned int i = 0; i < TokensymbolList.size(); i++) + { + str = TokensymbolList[i]; + if (tokensymbol == str) + { + return true; + } + } + return false; +} +void CWallet::UpdateTokenBalanceList( ) +{ + + std::vector vAvailableTokenCoins; + std::string strsymbol = ""; + std::string cursymbol = ""; + uint8_t txtype = -1; + uint64_t balance = 0; + uint64_t nvalue = 0; + const CCoinControl *coinControl = NULL; + AvailableTokenCoins(vAvailableTokenCoins,true, coinControl); + for (const auto& coin : vAvailableTokenCoins) + { + txtype = coin.tx->tx->vout[coin.i].txType; + if (txtype == 4) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenRegLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenRegLabel.totalCount; + } + else if (txtype == 5) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenLabel.value; + } + if (!IsHaveTheTokensymbol(strsymbol)) //If not, add it + { + TokensymbolList.push_back(strsymbol); + } + + + } + TokenValueMap.clear(); + if (TokensymbolList.size()) + { + for (unsigned int i = 0; i < TokensymbolList.size(); i++) + { + cursymbol = TokensymbolList[i]; + balance = 0; //For a symbol, the balance is 0 + for (const auto& coin : vAvailableTokenCoins) + { + txtype = coin.tx->tx->vout[coin.i].txType; + if (txtype == 4) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenRegLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenRegLabel.totalCount; + } + else if (txtype == 5) + { + strsymbol = coin.tx->tx->vout[coin.i].tokenLabel.getTokenSymbol(); + nvalue = coin.tx->tx->vout[coin.i].tokenLabel.value; + } + if (cursymbol != strsymbol) + continue; + balance += nvalue; + } + TokenValueMap.insert(std::make_pair(cursymbol, balance)); + } + } + return; +} +void CWallet::AvailableTokenCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + const CWalletTx* pcoin = &(*it).second; + + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) { + continue; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { + continue; + } + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + isminetype mine = IsMine(pcoin->tx->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue >= 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))&& + (pcoin->tx->vout[i].txType == TXOUT_TOKEN || pcoin->tx->vout[i].txType == TXOUT_TOKENREG))// + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } +} +//end +static void ApproximateBestSubset(vector > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue, + vector& vfBest, CAmount& nBest, int iterations = 1000) +{ + vector vfIncluded; + + vfBest.assign(vValue.size(), true); + nBest = nTotalLower; + + FastRandomContext insecure_rand; + + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + CAmount nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (unsigned int i = 0; i < vValue.size(); i++) + { + //The solver here uses a randomized algorithm, + //the randomness serves no real security purpose but is just + //needed to prevent degenerate behavior and it is important + //that the rng is fast. We do not use a constant random sequence, + //because there may be some privacy improvement by making + //the selection random. + if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } +} + +bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, vector vCoins, + set >& setCoinsRet, CAmount& nValueRet) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits::max(); + coinLowestLarger.second.first = NULL; + vector > > vValue; + CAmount nTotalLower = 0; + + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(const COutput &output, vCoins) + { + if (!output.fSpendable) + continue; + + const CWalletTx *pcoin = output.tx; + + if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) + continue; + + if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors)) + continue; + + int i = output.i; + CAmount n = pcoin->tx->vout[i].nValue; + + pair > coin = make_pair(n,make_pair(pcoin, i)); + + if (n == nTargetValue) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } + else if (n < nTargetValue + MIN_CHANGE) + { + vValue.push_back(coin); + nTotalLower += n; + } + else if (n < coinLowestLarger.first) + { + coinLowestLarger = coin; + } + } + + if (nTotalLower == nTargetValue) + { + for (unsigned int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } + + if (nTotalLower < nTargetValue) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + // Solve subset sum by stochastic approximation + std::sort(vValue.begin(), vValue.end(), CompareValueOnly()); + std::reverse(vValue.begin(), vValue.end()); + vector vfBest; + CAmount nBest; + + ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) + ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest); + + // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, + // or the next bigger coin is closer), return the bigger coin + if (coinLowestLarger.second.first && + ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest)) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + LogPrint("selectcoins", "SelectCoins() best subset: "); + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first)); + LogPrint("selectcoins", "total %s\n", FormatMoney(nBest)); + } + + return true; +} + +bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const +{ + vector vCoins(vAvailableCoins); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + if (!out.fSpendable) + continue; + nValueRet += out.tx->tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + // calculate value from preset inputs and store them + set > setPresetCoins; + CAmount nValueFromPresetInputs = 0; + + std::vector vPresetInputs; + if (coinControl) + coinControl->ListSelected(vPresetInputs); + BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) + { + map::const_iterator it = mapWallet.find(outpoint.hash); + if (it != mapWallet.end()) + { + const CWalletTx* pcoin = &it->second; + // Clearly invalid input, fail + if (pcoin->tx->vout.size() <= outpoint.n) + return false; + nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; + setPresetCoins.insert(make_pair(pcoin, outpoint.n)); + } else + return false; // TODO: Allow non-wallet inputs + } + + // remove preset inputs from vCoins + for (vector::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) + { + if (setPresetCoins.count(make_pair(it->tx, it->i))) + it = vCoins.erase(it); + else + ++it; + } + + size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); + bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + + bool res = nTargetValue <= nValueFromPresetInputs || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits::max(), vCoins, setCoinsRet, nValueRet)); + + // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset + setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); + + // add preset inputs to the total value selected + nValueRet += nValueFromPresetInputs; + + return res; +} +//add by xxy +bool CWallet::SelectNormalCoins(const vector& vAvailableCoins, const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const +{ + vector vCoins(vAvailableCoins); + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + if (!out.fSpendable) + continue; + nValueRet += out.tx->tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + // calculate value from preset inputs and store them + set > setPresetCoins; + CAmount nValueFromPresetInputs = 0; + + std::vector vPresetInputs; + + if (coinControl) + coinControl->ListSelected(vPresetInputs); + BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) + { + map::const_iterator it = mapWallet.find(outpoint.hash); + if (it != mapWallet.end()) + { + const CWalletTx* pcoin = &it->second; + // Clearly invalid input, fail + if (pcoin->tx->vout.size() <= outpoint.n) + return false; + nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; + setPresetCoins.insert(make_pair(pcoin, outpoint.n)); + } + else + return false; // TODO: Allow non-wallet inputs + } + + // remove preset inputs from vCoins + + for (vector::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) + { + if (setPresetCoins.count(make_pair(it->tx, it->i))) + it = vCoins.erase(it); + else + ++it; + } + + size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); + bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + + bool res = nTargetValue <= nValueFromPresetInputs || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength / 3), vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength / 2, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) || + (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits::max(), vCoins, setCoinsRet, nValueRet)); + + // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset + setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); + + // add preset inputs to the total value selected + nValueRet += nValueFromPresetInputs; + + return res; +} + +bool CWallet::SelectTokenCoins(const std::vector& vAvailableCoins, std::set >& setCoinsRet, std::string & symbol, uint64_t& nTokenValue, const CCoinControl *coinControl) const +{ + vector vCoins(vAvailableCoins); + uint8_t txtype = -1; + uint64_t tempValue = 0; + + //Go through it and see if you can find only one tokencoins to trade and find and return + tempValue = nTokenValue; + BOOST_FOREACH(const COutput& out, vCoins) + { + if (!out.fSpendable) + continue; + //Eight confirmations can be spent + if (out.nDepth < nTxConfirmTarget) + continue; + // + txtype = out.tx->tx->vout[out.i].txType; + if (txtype == 4) + { + if (symbol != out.tx->tx->vout[out.i].tokenRegLabel.getTokenSymbol()) + continue; + if (out.tx->tx->vout[out.i].tokenRegLabel.totalCount >= nTokenValue) + { + setCoinsRet.insert(make_pair(out.tx, out.i)); + return true; + } + return false; + } + if (txtype == 5) + { + if (symbol != out.tx->tx->vout[out.i].tokenLabel.getTokenSymbol()) + continue; + if (out.tx->tx->vout[out.i].tokenLabel.value >= nTokenValue) + { + //clear before + setCoinsRet.clear(); + setCoinsRet.insert(make_pair(out.tx, out.i)); + return true; + } + else if (out.tx->tx->vout[out.i].tokenLabel.value < tempValue) + { + setCoinsRet.insert(make_pair(out.tx, out.i)); + tempValue = tempValue - out.tx->tx->vout[out.i].tokenLabel.value; + } + else + { + setCoinsRet.insert(make_pair(out.tx, out.i)); + return true; + } + } + } + + return false; + +} +//end +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange) +{ + vector vecSend; + + // Turn the txout set into a CRecipient vector + for (size_t idx = 0; idx < tx.vout.size(); idx++) + { + const CTxOut& txOut = tx.vout[idx]; + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; + vecSend.push_back(recipient); + } + + CCoinControl coinControl; + coinControl.destChange = destChange; + coinControl.fAllowOtherInputs = true; + coinControl.fAllowWatchOnly = includeWatching; + coinControl.fOverrideFeeRate = overrideEstimatedFeeRate; + coinControl.nFeeRate = specificFeeRate; + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + coinControl.Select(txin.prevout); + + CReserveKey reservekey(this); + CWalletTx wtx; + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false)) + return false; + + if (nChangePosInOut != -1) + tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); + + // Copy output sizes from new transaction; they may have had the fee subtracted from them + for (unsigned int idx = 0; idx < tx.vout.size(); idx++) + tx.vout[idx].nValue = wtx.tx->vout[idx].nValue; + + // Add new txins (keeping original txin scriptSig/order) + BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) + { + if (!coinControl.IsSelected(txin.prevout)) + { + tx.vin.push_back(txin); + + if (lockUnspents) + { + LOCK2(cs_main, cs_wallet); + LockCoin(txin.prevout); + } + } + } + + // optionally keep the change output key + if (keepReserveKey) + reservekey.KeepKey(); + + return true; +} +bool CWallet::address2pkhash(std::string& address, uint160& pkhash) +{ + //uint160 pkhash = 0; + CBitcoinAddress Caddress(address); + if (!Caddress.IsValid()) + return false; + const CTxDestination &desaddress = Caddress.Get(); + CKeyID id = *get(&desaddress); + pkhash = id; + return true; +} +bool CWallet::JoinCampaign(const CTxDestination &address, CAmount deposi, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) + +{ + + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + + //First verify that the bottom layer is already in charge, and if the bottom is in the bookkeeping, it does not create a transaction + std::string currentAccountHex; + + CKeyID id = *get(&address); + uint160 devoterhash = id; + if (CConsensusAccountPool::Instance().verifyPkInCandidateListByIndex(chainActive.Tip(), id)) + { + std::cout << " 111111111111111111 "<< std::endl; + std::string curaddress = CBitcoinAddress(CKeyID(devoterhash)).ToString(); + strFailReason = "Address " + curaddress + " already join the campaign before,can't join now"; + return false; + } + if (CDpocInfo::Instance().GetLocalAccount(currentAccountHex)) + { + uint160 devoterhash; + devoterhash.SetHex(currentAccountHex); + std::string curaddress = CBitcoinAddress(CKeyID(devoterhash)).ToString(); + strFailReason = "Address " + curaddress + " already join the campaign"; + return false; + } + + + //Construct system payee - change to destination address + //CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(Params().system_account_address).Get()); + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(address).Get()); + vector vecSend; + CRecipient recipient = { scriptPubKey, deposi, false}; + vecSend.push_back(recipient); + + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + //Construct a txout for trading + DevoteLabel joinLabel; + joinLabel.ExtendType = TYPE_CONSENSUS_REGISTER; + + joinLabel.hash = id; + + CTxOut txout(recipient.nAmount, recipient.scriptPubKey , joinLabel); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + //Add an output that supports exit bookkeeping 1 ipc + CAmount nNewoutValue = 1*COIN; + CTxOut newout(nNewoutValue, recipient.scriptPubKey); + txNew.vout.push_back(newout); + nValueToSelect += nNewoutValue; + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at random position: + //nChangePosInOut = GetRandInt(txNew.vout.size() + 1); + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::ExitCampaign(CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + + //Get the pubkey that is currently in charge at the bottom and return directly if it is empty + std::string currentCampaignAccountPubkeyStr; + int ret = CDpocInfo::Instance().GetLocalAccount(currentCampaignAccountPubkeyStr); + if (ret < 0) + { + std::cout << "Not join the campaign yet\n"; + strFailReason = _("Not join the campaign yet"); + return false; + } + CKeyID id; + id.SetHex(currentCampaignAccountPubkeyStr); + CTxDestination address(id); + + //Construct system payee + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(Params().system_account_address).Get()); + vector vecSend; + CRecipient recipient = { scriptPubKey, 0, false }; + vecSend.push_back(recipient); + + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector tmpAvailableCoins; + std::vector vAvailableCoins; + AvailableNormalCoins(tmpAvailableCoins, true, coinControl); + + //Filtering is only the source UTXO that specifies the exit address + BOOST_FOREACH(const COutput& out, tmpAvailableCoins) { + + CTxDestination utxoaddress; + const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, utxoaddress); + + //if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + if (!fValidAddress) + continue; + if (utxoaddress != address) + continue; + + vAvailableCoins.push_back(out); + } + if (vAvailableCoins.size() == 0) + { + strFailReason = _("The Address which you want to ExitCampaign must have some money!"); + return false; + } + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + //Construct a txout from the transaction + DevoteLabel exitLabel; + exitLabel.ExtendType = TYPE_CONSENSUS_QUITE; + CKeyID id = *get(&address); + exitLabel.hash = id; + + printf("pubkey hash to exit:%s\n", exitLabel.hash.GetHex().c_str()); + + CTxOut txout(recipient.nAmount, exitLabel); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::PunishRequest(const CTxDestination &address, const std::string evdience, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + + + //Construct system payee + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(Params().system_account_address).Get()); + vector vecSend; + CRecipient recipient = { scriptPubKey, 0, false }; + vecSend.push_back(recipient); + + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + //Construct the penalty application transaction + CKeyID id = *get(&address); + CTxOut txout(id, evdience); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + for (const auto& pcoin : setCoins) + { + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + for (const auto& pcoin : setCoins) + { + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin()+nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin()+nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin()+nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//add by xxy +bool CWallet::CreateNormalTransaction(const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + CAmount nValue = 0; + nFeeRet = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE*2); + { + set > setCoins; + + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey); + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + } + } + + if (txout.IsDust(dustRelayFee)) + { + if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) + { + if (txout.nValue < 0) + strFailReason = _("The transaction amount is too small to pay the fee"); + else + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + } + else + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//StrReglabel: know the IPCLabel of the register; VecSend: recipient information +bool CWallet::CreateIPCRegTransaction(std::string& strReglabel, const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + if (strReglabel.size() == 0) + { + strFailReason = _("IPCRegTransaction must have IPCLabel"); + return false; + } + UniValue JsonIpclabel; + bool isNUll = JsonIpclabel.read(strReglabel); + IPCLabel label; + UniValue o = find_value(JsonIpclabel, "ExtendType"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's ExtendType err"); + return false; + } + label.ExtendType = (uint8_t)o.get_int(); + label.startTime = timeService.GetCurrentTimeSeconds(); + label.stopTime = 0; + label.reAuthorize = 1; + label.uniqueAuthorize = 0; + o = find_value(JsonIpclabel, "hashLen"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's hashLen err"); + return false; + } + label.hashLen = (uint8_t)o.get_int(); + + o = find_value(JsonIpclabel, "hash"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's hash err"); + return false; + } + std::string ipchash = o.get_str(); + if (ipchash.length() != 32) + { + strFailReason = _("this ipchash length is Wrongful "); + return false; + } + label.hash.SetHex(ipchash); + + o = find_value(JsonIpclabel, "labelTitle"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's labelTitle err"); + return false; + } + label.labelTitle = o.get_str(); + o = find_value(JsonIpclabel, "txLabel"); + if (o.isNull() || o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's txLabel err"); + return false; + } + + std::string TxLabel = o.get_str(); + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + //bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + + + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, TXOUT_IPCOWNER, label, TxLabel); + + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//Knowledge transfer transaction txid: transaction id for input; Index: the corresponding Index in the pre output; VecSend: recipient information +bool CWallet::CreateIPCSendTransaction(std::string& txid, int Index, const vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + UniValue uTxid = UniValue(txid); + uint256 hash = ParseHashV(uTxid, "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + + IPCLabel ipcSendLabel; + ipcSendLabel = tx->vout[Index].ipcLabel; + ipcSendLabel.startTime = timeService.GetCurrentTimeSeconds(); + std::string TxLabel = ""; + + { + //Check the incoming txid index for your unspent knowledge + std::vector vAvailableIPCCoins; + AvailableIPCCoins(vAvailableIPCCoins, true, coinControl); + bool isFind = false; + std::string cointxid = ""; + BOOST_FOREACH(const COutput& coin, vAvailableIPCCoins) + { + cointxid = coin.tx->tx->GetHash().GetHex(); + if (cointxid == txid && coin.i == Index && coin.tx->GetDepthInMainChain() >= DEFAULT_TX_CONFIRM_TARGET) + isFind = true; + } + if (!isFind) + { + strFailReason = _("This coins you can't spent now!"); + return false; + } + + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + // bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, TXOUT_IPCOWNER, ipcSendLabel, TxLabel); + + txNew.vout.push_back(txout); + } + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + txNew.vin.push_back(CTxIn(tx->GetHash(), Index, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + //Add fake signature, calculate service charge + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + int iNewVinsize = txNew.vin.size(); + if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + { + strFailReason = _("Signing Addvin of transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, iNewVinsize - 1, sigdata); + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added ipc type input also needs to be signed + int itxNewVinsize = txNew.vin.size(); + + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, itxNewVinsize - 1, tx->vout[Index].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, itxNewVinsize-1, sigdata); + } + + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +//To authorize and re-authorize transactions; Txid: the transaction id of the input; Index: the corresponding Index in the pre output; VecSend: recipient information; IPCAuthorizeLabel: IP licensed or reauthorized IPClabel +bool CWallet::CreateIPCAuthorizationTransaction(std::string& txid, int Index, const vector& vecSend, std::string& IPCAuthorizeLabel, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + UniValue uTxid = UniValue(txid); + uint256 hash = ParseHashV(uTxid, "parameter 1"); + if (hash.IsNull()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid Bitcoin txid"); + } + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + + if (IPCAuthorizeLabel.size() == 0) + { + strFailReason = _("IPCAuthorizeTransaction must have IPCLabel"); + return false; + } + UniValue JsonIpclabel; + bool isRightRead = JsonIpclabel.read(IPCAuthorizeLabel); + IPCLabel label; + + UniValue o = find_value(JsonIpclabel, "startTime"); + if (o.isNull()||o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's startTime err"); + return false; + } + label.startTime = o.get_int32(); + o = find_value(JsonIpclabel, "stopTime"); + if (o.isNull()||o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's stopTime err"); + return false; + } + label.stopTime = o.get_int32(); + o = find_value(JsonIpclabel, "reAuthorize"); + if (o.isNull()|| o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's reAuthorize err"); + return false; + } + label.reAuthorize = (uint8_t)o.get_int(); + + o = find_value(JsonIpclabel, "uniqueAuthorize"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's uniqueAuthorize err"); + return false; + } + label.uniqueAuthorize = (uint8_t)o.get_int(); + label.uniqueAuthorize = 0; + bool IsUniqueAuthorize = (bool)(label.uniqueAuthorize); + label.ExtendType = tx->vout[Index].ipcLabel.ExtendType; + label.hashLen = tx->vout[Index].ipcLabel.hashLen; + label.hash = tx->vout[Index].ipcLabel.hash; + label.labelTitle = tx->vout[Index].ipcLabel.labelTitle; + std::string TxLabel = ""; + + { + //Check the incoming txid index for your unspent knowledge + std::vector vAvailableIPCCoins; + AvailableIPCCoins(vAvailableIPCCoins, true, coinControl); + bool isFind = false; + std::string cointxid = ""; + BOOST_FOREACH(const COutput& coin, vAvailableIPCCoins) + { + cointxid = coin.tx->tx->GetHash().GetHex(); + if (cointxid == txid && coin.i == Index) + isFind = true; + } + if (!isFind) + { + strFailReason = _("This coins you have spent already!"); + return false; + } + + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + // bool fFirst = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, TXOUT_IPCAUTHORIZATION, label, TxLabel); + + txNew.vout.push_back(txout); + //Add the output of the change of ownership + + CTxOut txinpretxout ; + + if (!IsUniqueAuthorize) //Non-exclusive authorization + { + txinpretxout = tx->vout[Index]; + } + else + { + txinpretxout = tx->vout[Index]; + txinpretxout.txLabel = ""; + } + txNew.vout.push_back(txinpretxout); + + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + //end***************************************************************************************************************** + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + //Join an input + txNew.vin.push_back(CTxIn(tx->GetHash(), Index, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + //Add fake signature, calculate service charge + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + int iNewVinsize = txNew.vin.size(); + if(!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + { + strFailReason = _("Signing Addvin of transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, iNewVinsize-1, sigdata); + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added ipc type input also needs to be signed + int itxNewVinsize = txNew.vin.size(); + + const CScript& scriptPubKey = tx->vout[Index].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, itxNewVinsize - 1, tx->vout[Index].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, itxNewVinsize - 1, sigdata); + } + nIn++; + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//To create a token registration transaction; StrReglabel: TokenRegLabel; VecSend: recipient information +bool CWallet::CreateTokenRegTransaction(std::string& strReglabel, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl , bool sign ) +{ + nFeeRet = 0; + CAmount nValue = 0; + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + break; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + if (strReglabel.size() == 0) + { + strFailReason = _("IPCRegTransaction must have IPCTokenLabel"); + return false; + } + UniValue JsonIpclabel; + bool isNUll = JsonIpclabel.read(strReglabel); + + TokenRegLabel label; + UniValue o = find_value(JsonIpclabel, "TokenSymbol"); + if (o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's TokenSymbol err"); + return false; + } + std::string strTokenSymbol = o.get_str(); + if (strTokenSymbol == "") + { + strFailReason = _(" IPCTokenLabel's TokenSymbol is NULL"); + return false; + } + if (strTokenSymbol.length()>8) + { + strFailReason = _(" IPCTokenLabel's TokenSymbol is longer than 8"); + return false; + } + strncpy((char*)(label.TokenSymbol), strTokenSymbol.c_str(), strTokenSymbol.length()); + o = find_value(JsonIpclabel, "value"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's value err"); + return false; + } + label.value = o.get_int64(); + o = find_value(JsonIpclabel, "hash"); + if ( o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's hash err"); + return false; + } + label.hash.SetHex(o.get_str()); + o = find_value(JsonIpclabel, "label"); + if (o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's label err"); + return false; + } + std::string strlabel = o.get_str(); + if (strlabel.length() > 16) + { + strFailReason = _(" IPCTokenLabel's Label is longer than 16"); + return false; + } + strncpy((char*)(label.label), strlabel.c_str(), strlabel.length()); + o = find_value(JsonIpclabel, "issueDate"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's issueDate err"); + return false; + } + label.issueDate = o.get_int32(); + o = find_value(JsonIpclabel, "totalCount"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's totalCount err"); + return false; + } + label.totalCount = o.get_int64(); + o = find_value(JsonIpclabel, "accuracy"); + if (o.isNull() || o.type() != UniValue::VNUM) + { + strFailReason = _("The IPCLabel's accuracy err"); + return false; + } + label.accuracy = o.get_int(); + o = find_value(JsonIpclabel, "txLabel"); + if ( o.isNull() || o.type() != UniValue::VSTR) + { + strFailReason = _("The IPCLabel's txLabel err"); + return false; + } + std::string TxLabel = o.get_str(); + CAmount nDefalutFee = 100 * COIN; + nValue += nDefalutFee; + + { + set > setCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, label, TxLabel); + txNew.vout.push_back(txout); + break; + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//To create a token trade; Tokensymbol: token symbol; TokenValue: token trading details; VecSend: recipient information +bool CWallet::CreateTokenTransaction(std::string& tokensymbol, uint64_t TokenValue, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + uint64_t curTokenBalance = 0; + + GetSymbolbalance(tokensymbol, curTokenBalance); + + if (TokenValue > curTokenBalance) + { + strFailReason = _("The Tokenvalue is too big,you have not enough Tokencoins."); + return false; + } + + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + strFailReason = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + break; + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + std::string txLabel = ""; + TokenLabel tokenlabel; + memcpy((char*)(tokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(tokenlabel.TokenSymbol)); + tokenlabel.value = TokenValue; + if (tokenDataMap.count(tokenlabel.getTokenSymbol()) == 0) + { + strFailReason = _("Can't found 'accuracy' of the Token"); + return false; + } + tokenlabel.accuracy = tokenDataMap[tokenlabel.getTokenSymbol()].accuracy; + { + + set > setCoins; + set > setTokenCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + std::vector vAvailableTokenCoins; + AvailableTokenCoins(vAvailableTokenCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, tokenlabel,txLabel); + txNew.vout.push_back(txout); + break; + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + //Join an input + //Find the appropriate utxo input in the current sybmol balance + int icurinsize = txNew.vin.size(); + setTokenCoins.clear(); + if (!SelectTokenCoins(vAvailableTokenCoins, setTokenCoins, tokensymbol, TokenValue)) + { + strFailReason = _("Can't select enough TokenCoins!"); + return false; + } + bool getVinscriptPubKey = false; + uint64_t TotalvalueVin = 0; + CScript newscriptPubKey; + for (const auto& coin : setTokenCoins) + { + if (coin.first->tx->vout[coin.second].txType == 4) + TotalvalueVin += coin.first->tx->vout[coin.second].tokenRegLabel.totalCount; + else + TotalvalueVin += coin.first->tx->vout[coin.second].tokenLabel.value; + + if (!getVinscriptPubKey) + { + newscriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + getVinscriptPubKey = true; + } + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + } + if (!DummySignTx(txNew, setTokenCoins, icurinsize)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + //After finding enough input, you need to compute the add token change + TokenLabel stokenlabel; + for (const auto& recipient : vecSend) + { + memcpy((char*)(stokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(stokenlabel.TokenSymbol)); + stokenlabel.value = TotalvalueVin - TokenValue; + stokenlabel.accuracy = tokenlabel.accuracy; + + if (stokenlabel.value > 0) + { + CTxOut txsout(recipient.nAmount, newscriptPubKey, stokenlabel, txLabel); + int ntokenchangepos = txNew.vout.size() - 1; + vector::iterator tposition = txNew.vout.begin() + ntokenchangepos; + if (ntokenchangepos > 0) + txNew.vout.insert(tposition, txsout); + else + txNew.vout.push_back(txsout); + } + break; + } + + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added Token type input also needs to be signed + for (const auto& coin : setTokenCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::CreateTokenTransactionM(std::string& tokensymbol, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + uint64_t curTokenBalance = 0; + uint64_t TokenValue = 0; + GetSymbolbalance(tokensymbol, curTokenBalance); + + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0 || TokenValue < 0 ||recipient.nvalue < 0) + { + strFailReason = _("Transaction amounts/tokenvalue must not be negative"); + return false; + } + nValue += recipient.nAmount; + TokenValue += recipient.nvalue; + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + if (TokenValue > curTokenBalance) + { + strFailReason = _("The Tokenvalue is too big,you have not enough Tokencoins."); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + std::string txLabel = ""; + TokenLabel tokenlabel; + memcpy((char*)(tokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(tokenlabel.TokenSymbol)); + tokenlabel.value = 0; + if (tokenDataMap.count(tokenlabel.getTokenSymbol()) == 0) + { + strFailReason = _("Can't found 'accuracy' of the Token"); + return false; + } + tokenlabel.accuracy = tokenDataMap[tokenlabel.getTokenSymbol()].accuracy; + { + + set > setCoins; + set > setTokenCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + std::vector vAvailableTokenCoins; + AvailableTokenCoins(vAvailableTokenCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + TokenLabel nowtokenlabel = tokenlabel; + nowtokenlabel.value = recipient.nvalue; + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, nowtokenlabel, txLabel); + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + //Join an input + //Find the appropriate utxo input in the current sybmol balance + int icurinsize = txNew.vin.size(); + setTokenCoins.clear(); + if (!SelectTokenCoins(vAvailableTokenCoins, setTokenCoins, tokensymbol, TokenValue)) + { + strFailReason = _("Can't select enough TokenCoins!"); + return false; + } + bool getVinscriptPubKey = false; + uint64_t TotalvalueVin = 0; + CScript newscriptPubKey; + for (const auto& coin : setTokenCoins) + { + if (coin.first->tx->vout[coin.second].txType == 4) + TotalvalueVin += coin.first->tx->vout[coin.second].tokenRegLabel.totalCount; + else + TotalvalueVin += coin.first->tx->vout[coin.second].tokenLabel.value; + + if (!getVinscriptPubKey) + { + newscriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + getVinscriptPubKey = true; + } + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + } + if (!DummySignTx(txNew, setTokenCoins, icurinsize)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + //After finding enough input, you need to compute the add token change + TokenLabel stokenlabel; + + { + memcpy((char*)(stokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(stokenlabel.TokenSymbol)); + stokenlabel.value = TotalvalueVin - TokenValue; + stokenlabel.accuracy = tokenlabel.accuracy; + + if (stokenlabel.value > 0) + { + CAmount nstokenamount = 0; + CTxOut txsout(nstokenamount, newscriptPubKey, stokenlabel, txLabel); + int ntokenchangepos = txNew.vout.size() - 1; + vector::iterator tposition = txNew.vout.begin() + ntokenchangepos; + if (ntokenchangepos > 0) + txNew.vout.insert(tposition, txsout); + else + txNew.vout.push_back(txsout); + } + + } + + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added Token type input also needs to be signed + for (const auto& coin : setTokenCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} +//Submit a bid for the election +bool CWallet::CommitJoinCampaignTransaction(const CTxDestination &address, CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state) +{ + { + LOCK2(cs_main, cs_wallet); + LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); + + + if (fBroadcastTransactions) + { + // Broadcast + if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { + LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); + // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. + return false; + } + else { + + { + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Notify that old coins are spent + BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin) + { + CWalletTx &coin = mapWallet[txin.prevout.hash]; + coin.BindWallet(this); + NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + } + } + + // Track how many getdata requests our transaction gets + mapRequestCount[wtxNew.GetHash()] = 0; + + wtxNew.RelayWalletTransaction(connman); + } + } + } + + //After the transaction is successfully sent, the underlying system interface is invoked to set the public key + CKeyID id = *get(&address); + std::string sethexstring = id.GetHex(); + int nRet = CDpocInfo::Instance().SetLocalAccount(sethexstring); + if (0 == nRet) + { + CDpocInfo::Instance().setJoinCampaign(true); + CDpocInfo::Instance().setLocalAccoutVar(sethexstring); + } + + LogPrintf("[CWallet::CommitJoinCampaignTransaction] Sets the underlying public key return value %d\n", nRet); + + //Set the consensus status to be normal 0 + std::string strStatus("0"); + bool bRet = CDpocInfo::Instance().SetConsensusStatus(strStatus, sethexstring); + LogPrintf("[CWallet::CommitJoinCampaignTransaction] Set the consensus return value %d\n", bRet); + + return true; +} + +//Gets the current node bookkeeping reward +bool CWallet::GetCurrentRewards(std::vector& timelist, std::vector& valuelist) +{ + timelist.clear(); + valuelist.clear(); + std::vector listInfo; + int nSum; + bool ret = CDpocInfo::Instance().GetBlockInfo(listInfo, nSum); + if (!ret) return ret; + + BOOST_FOREACH(const BlockInfo &info, listInfo) + { + timelist.push_back(info.strTime); + valuelist.push_back(info.strFee); + } + return true; +} + +//end +/** + * Call after CreateTransaction unless you want to abort + */ +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state) +{ + { + LOCK2(cs_main, cs_wallet); + LogPrintf("CommitTransaction: %s\n", wtxNew.GetHash().GetHex()); + + + if (fBroadcastTransactions) + { + // Broadcast + if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { + LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); + // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. + + return false; + } else { + + { + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Notify that old coins are spent + BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin) + { + CWalletTx &coin = mapWallet[txin.prevout.hash]; + coin.BindWallet(this); + NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + } + } + + // Track how many getdata requests our transaction gets + mapRequestCount[wtxNew.GetHash()] = 0; + + wtxNew.RelayWalletTransaction(connman); + } + } + } + return true; +} + +void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list& entries) { + CWalletDB walletdb(strWalletFile); + return walletdb.ListAccountCreditDebit(strAccount, entries); +} + +bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry) +{ + CWalletDB walletdb(strWalletFile); + + return AddAccountingEntry(acentry, &walletdb); +} + +bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwalletdb) +{ + if (!pwalletdb->WriteAccountingEntry_Backend(acentry)) + return false; + + laccentries.push_back(acentry); + CAccountingEntry & entry = laccentries.back(); + wtxOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); + + return true; +} + +CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) +{ + return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); +} + +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) +{ + // payTxFee is the user-set global for desired feerate + return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes)); +} + +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee) +{ + CAmount nFeeNeeded = targetFee; + // User didn't set: use -txconfirmtarget to estimate... + if (nFeeNeeded == 0) { + int estimateFoundTarget = nConfirmTarget; + nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes); + // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee + if (nFeeNeeded == 0) + nFeeNeeded = fallbackFee.GetFee(nTxBytes); + } + // prevent user from paying a fee below minRelayTxFee or minTxFee + nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); + // But always obey the maximum + if (nFeeNeeded > maxTxFee) + nFeeNeeded = maxTxFee; + return nFeeNeeded; +} + + + + +DBErrors CWallet::LoadWallet(bool& fFirstRunRet) +{ + if (!fFileBacked) + return DB_LOAD_OK; + fFirstRunRet = false; + DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); + if (nLoadWalletRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // that requires a new key. + } + } + + if (nLoadWalletRet != DB_LOAD_OK) + return nLoadWalletRet; + fFirstRunRet = !vchDefaultKey.IsValid(); + + uiInterface.LoadWallet(this); + + return DB_LOAD_OK; +} + +DBErrors CWallet::ZapSelectTx(vector& vHashIn, vector& vHashOut) +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(this, vHashIn, vHashOut); + if (nZapSelectTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // that requires a new key. + } + } + + if (nZapSelectTxRet != DB_LOAD_OK) + return nZapSelectTxRet; + + MarkDirty(); + + return DB_LOAD_OK; + +} + +DBErrors CWallet::ZapWalletTx(std::vector& vWtx) +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx); + if (nZapWalletTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // that requires a new key. + } + } + + if (nZapWalletTxRet != DB_LOAD_OK) + return nZapWalletTxRet; + + return DB_LOAD_OK; +} + + +bool CWallet::SetAddressBook(const CTxDestination& address, const string& strName, const string& strPurpose) +{ + bool fUpdated = false; + { + LOCK(cs_wallet); // mapAddressBook + std::map::iterator mi = mapAddressBook.find(address); + fUpdated = mi != mapAddressBook.end(); + mapAddressBook[address].name = strName; + if (!strPurpose.empty()) /* update purpose only if requested */ + mapAddressBook[address].purpose = strPurpose; + } + NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, + strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); + if (!fFileBacked) + return false; + if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) + return false; + return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); +} + +bool CWallet::DelAddressBook(const CTxDestination& address) +{ + { + LOCK(cs_wallet); // mapAddressBook + + if(fFileBacked) + { + // Delete destdata tuples associated with address + std::string strAddress = CBitcoinAddress(address).ToString(); + BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) + { + CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); + } + } + mapAddressBook.erase(address); + } + + NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); + + if (!fFileBacked) + return false; + CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString()); + return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); +} + +bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) +{ + if (fFileBacked) + { + if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) + return false; + } + vchDefaultKey = vchPubKey; + return true; +} + +/** + * Mark old keypool keys as used, + * and generate all new keys + */ +bool CWallet::NewKeyPool() +{ + { + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + BOOST_FOREACH(int64_t nIndex, setKeyPool) + walletdb.ErasePool(nIndex); + setKeyPool.clear(); + + if (IsLocked()) + return false; + + int64_t nKeys = max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t)0); + for (int i = 0; i < nKeys; i++) + { + int64_t nIndex = i+1; + walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); + setKeyPool.insert(nIndex); + } + LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); + } + return true; +} + +bool CWallet::TopUpKeyPool(unsigned int kpSize) +{ + { + LOCK(cs_wallet); + + if (IsLocked()) + return false; + + CWalletDB walletdb(strWalletFile); + + // Top up key pool + unsigned int nTargetSize; + if (kpSize > 0) + nTargetSize = kpSize; + else + nTargetSize = max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0); + + while (setKeyPool.size() < (nTargetSize + 1)) + { + int64_t nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + throw runtime_error(std::string(__func__) + ": writing generated key failed"); + setKeyPool.insert(nEnd); + LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); + } + } + return true; +} + +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey = CPubKey(); + { + LOCK(cs_wallet); + + if (!IsLocked()) + TopUpKeyPool(); + + // Get the oldest key + if(setKeyPool.empty()) + return; + + CWalletDB walletdb(strWalletFile); + + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error(std::string(__func__) + ": read failed"); + if (!HaveKey(keypool.vchPubKey.GetID())) + throw runtime_error(std::string(__func__) + ": unknown key in key pool"); + assert(keypool.vchPubKey.IsValid()); + LogPrintf("keypool reserve %d\n", nIndex); + } +} + +void CWallet::KeepKey(int64_t nIndex) +{ + // Remove from key pool + if (fFileBacked) + { + CWalletDB walletdb(strWalletFile); + walletdb.ErasePool(nIndex); + } + LogPrintf("keypool keep %d\n", nIndex); +} + +void CWallet::ReturnKey(int64_t nIndex) +{ + // Return to key pool + { + LOCK(cs_wallet); + setKeyPool.insert(nIndex); + } + LogPrintf("keypool return %d\n", nIndex); +} + +bool CWallet::GetKeyFromPool(CPubKey& result) +{ + int64_t nIndex = 0; + CKeyPool keypool; + { + LOCK(cs_wallet); + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + { + if (IsLocked()) return false; + result = GenerateNewKey(); + return true; + } + KeepKey(nIndex); + result = keypool.vchPubKey; + } + return true; +} + +int64_t CWallet::GetOldestKeyPoolTime() +{ + LOCK(cs_wallet); + + // if the keypool is empty, return + if (setKeyPool.empty()) + return GetTime(); + + // load oldest key from keypool, get time and return + CKeyPool keypool; + CWalletDB walletdb(strWalletFile); + int64_t nIndex = *(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); + assert(keypool.vchPubKey.IsValid()); + return keypool.nTime; +} + +std::map CWallet::GetAddressBalances() +{ + map balances; + + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (!pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) + continue; + + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) + { + CTxDestination addr; + if (!IsMine(pcoin->tx->vout[i])) + continue; + if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) + continue; + + CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; + + if (!balances.count(addr)) + balances[addr] = 0; + balances[addr] += n; + } + } + } + + return balances; +} + +set< set > CWallet::GetAddressGroupings() +{ + AssertLockHeld(cs_wallet); // mapWallet + set< set > groupings; + set grouping; + + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (pcoin->tx->vin.size() > 0) + { + bool any_mine = false; + // group all input addresses with each other + BOOST_FOREACH(CTxIn txin, pcoin->tx->vin) + { + CTxDestination address; + if(!IsMine(txin)) /* If this input isn't mine, ignore it */ + continue; + if(!ExtractDestination(mapWallet[txin.prevout.hash].tx->vout[txin.prevout.n].scriptPubKey, address)) + continue; + grouping.insert(address); + any_mine = true; + } + + // group change with input addresses + if (any_mine) + { + BOOST_FOREACH(CTxOut txout, pcoin->tx->vout) + if (IsChange(txout)) + { + CTxDestination txoutAddr; + if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) + continue; + grouping.insert(txoutAddr); + } + } + if (grouping.size() > 0) + { + groupings.insert(grouping); + grouping.clear(); + } + } + + // group lone addrs by themselves + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) + if (IsMine(pcoin->tx->vout[i])) + { + CTxDestination address; + if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, address)) + continue; + grouping.insert(address); + groupings.insert(grouping); + grouping.clear(); + } + } + + set< set* > uniqueGroupings; // a set of pointers to groups of addresses + map< CTxDestination, set* > setmap; // map addresses to the unique group containing it + BOOST_FOREACH(set _grouping, groupings) + { + // make a set of all the groups hit by this new group + set< set* > hits; + map< CTxDestination, set* >::iterator it; + BOOST_FOREACH(CTxDestination address, _grouping) + if ((it = setmap.find(address)) != setmap.end()) + hits.insert((*it).second); + + // merge all hit groups into a new single group and delete old groups + set* merged = new set(_grouping); + BOOST_FOREACH(set* hit, hits) + { + merged->insert(hit->begin(), hit->end()); + uniqueGroupings.erase(hit); + delete hit; + } + uniqueGroupings.insert(merged); + + // update setmap + BOOST_FOREACH(CTxDestination element, *merged) + setmap[element] = merged; + } + + set< set > ret; + BOOST_FOREACH(set* uniqueGrouping, uniqueGroupings) + { + ret.insert(*uniqueGrouping); + delete uniqueGrouping; + } + + return ret; +} + +CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CWalletDB walletdb(strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); +} + +CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CAmount nBalance = 0; + + // Tally wallet transactions + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!CheckIPCFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + continue; + + CAmount nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance -= nSent + nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + + return nBalance; +} + +std::set CWallet::GetAccountAddresses(const std::string& strAccount) const +{ + LOCK(cs_wallet); + set result; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, mapAddressBook) + { + const CTxDestination& address = item.first; + const string& strName = item.second.name; + if (strName == strAccount) + result.insert(address); + } + return result; +} + +bool CReserveKey::GetReservedKey(CPubKey& pubkey) +{ + if (nIndex == -1) + { + CKeyPool keypool; + pwallet->ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex != -1) + vchPubKey = keypool.vchPubKey; + else { + return false; + } + } + assert(vchPubKey.IsValid()); + pubkey = vchPubKey; + return true; +} + +void CReserveKey::KeepKey() +{ + if (nIndex != -1) + pwallet->KeepKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CReserveKey::ReturnKey() +{ + if (nIndex != -1) + pwallet->ReturnKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CWallet::GetAllReserveKeys(set& setAddress) const +{ + setAddress.clear(); + + CWalletDB walletdb(strWalletFile); + + LOCK2(cs_main, cs_wallet); + BOOST_FOREACH(const int64_t& id, setKeyPool) + { + CKeyPool keypool; + if (!walletdb.ReadPool(id, keypool)) + throw runtime_error(std::string(__func__) + ": read failed"); + assert(keypool.vchPubKey.IsValid()); + CKeyID keyID = keypool.vchPubKey.GetID(); + if (!HaveKey(keyID)) + throw runtime_error(std::string(__func__) + ": unknown key in key pool"); + setAddress.insert(keyID); + } +} + +void CWallet::UpdatedTransaction(const uint256 &hashTx) +{ + { + LOCK(cs_wallet); + // Only notify UI if this transaction is in this wallet + map::const_iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + NotifyTransactionChanged(this, hashTx, CT_UPDATED); + } +} + +void CWallet::GetScriptForMining(boost::shared_ptr &script) +{ + boost::shared_ptr rKey(new CReserveKey(this)); + CPubKey pubkey; + if (!rKey->GetReservedKey(pubkey)) + return; + + script = rKey; + script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; +} + +void CWallet::LockCoin(const COutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.insert(output); +} + +void CWallet::UnlockCoin(const COutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.erase(output); +} + +void CWallet::UnlockAllCoins() +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.clear(); +} + +bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const +{ + AssertLockHeld(cs_wallet); // setLockedCoins + COutPoint outpt(hash, n); + + return (setLockedCoins.count(outpt) > 0); +} + +void CWallet::ListLockedCoins(std::vector& vOutpts) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + for (std::set::iterator it = setLockedCoins.begin(); + it != setLockedCoins.end(); it++) { + COutPoint outpt = (*it); + vOutpts.push_back(outpt); + } +} + +/** @} */ // end of Actions + +class CAffectedKeysVisitor : public boost::static_visitor { +private: + const CKeyStore &keystore; + std::vector &vKeys; + +public: + CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + + void Process(const CScript &script) { + txnouttype type; + std::vector vDest; + int nRequired; + if (ExtractDestinations(script, type, vDest, nRequired)) { + BOOST_FOREACH(const CTxDestination &dest, vDest) + boost::apply_visitor(*this, dest); + } + } + + void operator()(const CKeyID &keyId) { + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CScriptID &scriptId) { + CScript script; + if (keystore.GetCScript(scriptId, script)) + Process(script); + } + + void operator()(const CNoDestination &none) {} +}; + +void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { + AssertLockHeld(cs_wallet); // mapKeyMetadata + mapKeyBirth.clear(); + + // get birth times for keys with metadata + for (const auto& entry : mapKeyMetadata) { + if (entry.second.nCreateTime) { + mapKeyBirth[entry.first] = entry.second.nCreateTime; + } + } + + // map in which we'll infer heights of other keys + CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin + std::map mapKeyFirstBlock; + std::set setKeys; + GetKeys(setKeys); + BOOST_FOREACH(const CKeyID &keyid, setKeys) { + if (mapKeyBirth.count(keyid) == 0) + mapKeyFirstBlock[keyid] = pindexMax; + } + setKeys.clear(); + + // if there are no such keys, we're done + if (mapKeyFirstBlock.empty()) + return; + + // find first block that affects those keys, if there are any left + std::vector vAffected; + for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { + // iterate over all wallet transactions... + const CWalletTx &wtx = (*it).second; + BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); + if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { + // ... which are already in a block + int nHeight = blit->second->nHeight; + BOOST_FOREACH(const CTxOut &txout, wtx.tx->vout) { + // iterate over all their outputs + CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); + BOOST_FOREACH(const CKeyID &keyid, vAffected) { + // ... and all their affected keys + std::map::iterator rit = mapKeyFirstBlock.find(keyid); + if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) + rit->second = blit->second; + } + vAffected.clear(); + } + } + } + + // Extract block timestamps for those keys + for (std::map::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) + mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off +} + +bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) +{ + if (boost::get(&dest)) + return false; + + mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value); +} + +bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) +{ + if (!mapAddressBook[dest].destdata.erase(key)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key); +} + +bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) +{ + mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); + return true; +} + +bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const +{ + std::map::const_iterator i = mapAddressBook.find(dest); + if(i != mapAddressBook.end()) + { + CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key); + if(j != i->second.destdata.end()) + { + if(value) + *value = j->second; + return true; + } + } + return false; +} + +std::string CWallet::GetWalletHelpString(bool showDebug) +{ + std::string strUsage = HelpMessageGroup(_("Wallet options:")); + strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); + strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); + strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); + strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); + strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); + if (showDebug) + strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); + strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); + strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); + strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); + strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); + strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); + strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); + + if (showDebug) + { + strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); + + strUsage += HelpMessageOpt("-dblogsize=", strprintf("Flush wallet database activity from memory to disk log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); + strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); + strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); + } + + return strUsage; +} + +extern std::string DecodeDumpString(const std::string &str); +extern int64_t DecodeDumpTime(const std::string &str); + +bool CWallet::LoadWalletFromFile(const std::string filepath){ + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (pwalletMain->IsLocked()) + { + printf("Error: Please enter the wallet passphrase with walletpassphrase first."); + return false; + } + + std::ifstream file; + file.open(filepath, std::ios::in | std::ios::ate); + if (!file.is_open()) + { + printf("Cannot open wallet dump file"); + return false; + } + + int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); + + bool fGood = true; + + int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); + file.seekg(0, file.beg); + + pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + while (file.good()) { + pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + std::string line; + std::getline(file, line); + if (line.empty() || line[0] == '#') + continue; + + std::vector vstr; + boost::split(vstr, line, boost::is_any_of(" ")); + if (vstr.size() < 2) + continue; + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(vstr[0])) + continue; + CKey key = vchSecret.GetKey(); + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + CKeyID keyid = pubkey.GetID(); + if (pwalletMain->HaveKey(keyid)) { + LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString()); + continue; + } + int64_t nTime = DecodeDumpTime(vstr[1]); + std::string strLabel; + bool fLabel = true; + for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { + if (boost::algorithm::starts_with(vstr[nStr], "#")) + break; + if (vstr[nStr] == "change=1") + fLabel = false; + if (vstr[nStr] == "reserve=1") + fLabel = false; + if (boost::algorithm::starts_with(vstr[nStr], "label=")) { + strLabel = DecodeDumpString(vstr[nStr].substr(6)); + fLabel = true; + } + } + LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); + if (!pwalletMain->AddKeyPubKey(key, pubkey)) { + fGood = false; + continue; + } + pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime; + if (fLabel) + pwalletMain->SetAddressBook(keyid, strLabel, "receive"); + nTimeBegin = std::min(nTimeBegin, nTime); + } + file.close(); + pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI + + CBlockIndex *pindex = chainActive.Tip(); + while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200) + pindex = pindex->pprev; + + pwalletMain->UpdateTimeFirstKey(nTimeBegin); + + LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); + pwalletMain->ScanForWalletTransactions(pindex); + pwalletMain->MarkDirty(); + + if (!fGood) + { + printf("Error adding some keys to wallet"); + return false; + } + + return true; +} + +extern std::string EncodeDumpString(const std::string &str); +extern std::string EncodeDumpTime(int64_t nTime); +bool CWallet::ExportWalletToFile(const std::string filepath){ + + LOCK2(cs_main, pwalletMain->cs_wallet); + +// EnsureWalletIsUnlocked(); + if (pwalletMain->IsLocked()) + { + printf("Error: Please enter the wallet passphrase with walletpassphrase first."); + return false; + } + ofstream file; + file.open(filepath.c_str()); + if (!file.is_open()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); + + std::map mapKeyBirth; + std::set setKeyPool; + pwalletMain->GetKeyBirthTimes(mapKeyBirth); + pwalletMain->GetAllReserveKeys(setKeyPool); + + // sort time/key pairs + std::vector > vKeyBirth; + for (const auto& entry : mapKeyBirth) { + if (const CKeyID* keyID = boost::get(&entry.first)) { // set and test + vKeyBirth.push_back(std::make_pair(entry.second, *keyID)); + } + } + mapKeyBirth.clear(); + std::sort(vKeyBirth.begin(), vKeyBirth.end()); + + // produce output + file << strprintf("# Wallet dump created by Ipchain %s\n", CLIENT_BUILD); + file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); + file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); + file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); + file << "\n"; + + // add the base58check encoded extended master if the wallet uses HD + CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID; + if (!masterKeyID.IsNull()) + { + CKey key; + if (pwalletMain->GetKey(masterKeyID, key)) + { + CExtKey masterKey; + masterKey.SetMaster(key.begin(), key.size()); + + CBitcoinExtKey b58extkey; + b58extkey.SetKey(masterKey); + + file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n"; + } + } + for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { + const CKeyID &keyid = it->second; + std::string strTime = EncodeDumpTime(it->first); + std::string strAddr = CBitcoinAddress(keyid).ToString(); + CKey key; + if (pwalletMain->GetKey(keyid, key)) { + file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); + if (pwalletMain->mapAddressBook.count(keyid)) { + file << strprintf("label=%s", EncodeDumpString(pwalletMain->mapAddressBook[keyid].name)); + } + else if (keyid == masterKeyID) { + file << "hdmaster=1"; + } + else if (setKeyPool.count(keyid)) { + file << "reserve=1"; + } + else if (pwalletMain->mapKeyMetadata[keyid].hdKeypath == "m") { + file << "inactivehdmaster=1"; + } + else { + file << "change=1"; + } + file << strprintf(" # addr=%s%s\n", strAddr, (pwalletMain->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath=" + pwalletMain->mapKeyMetadata[keyid].hdKeypath : "")); + } + } + file << "\n"; + file << "# End of dump\n"; + file.close(); + return true; +} + +CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) +{ + // needed to restore wallet transaction meta data after -zapwallettxes + std::vector vWtx; + + if (GetBoolArg("-zapwallettxes", false)) { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + + CWallet *tempWallet = new CWallet(walletFile); + DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); + if (nZapWalletRet != DB_LOAD_OK) { + InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); + return NULL; + } + + delete tempWallet; + tempWallet = NULL; + } + + uiInterface.InitMessage(_("Loading wallet...")); + + int64_t nStart = GetTimeMillis(); + bool fFirstRun = true; + CWallet *walletInstance = new CWallet(walletFile); + DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); + if (nLoadWalletRet != DB_LOAD_OK) + { + if (nLoadWalletRet == DB_CORRUPT) { + InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); + return NULL; + } + else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) + { + InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data" + " or address book entries might be missing or incorrect."), + walletFile)); + } + else if (nLoadWalletRet == DB_TOO_NEW) { + InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME))); + return NULL; + } + else if (nLoadWalletRet == DB_NEED_REWRITE) + { + InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); + return NULL; + } + else { + InitError(strprintf(_("Error loading %s"), walletFile)); + return NULL; + } + } + + if (GetBoolArg("-upgradewallet", fFirstRun)) + { + int nMaxVersion = GetArg("-upgradewallet", 0); + if (nMaxVersion == 0) // the -upgradewallet without argument case + { + LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); + nMaxVersion = CLIENT_VERSION; + walletInstance->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately + } + else + LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); + if (nMaxVersion < walletInstance->GetVersion()) + { + InitError(_("Cannot downgrade wallet")); + return NULL; + } + walletInstance->SetMaxVersion(nMaxVersion); + } + + if (fFirstRun) + { + // Create new keyUser and set as default key + if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { + // generate a new master key + CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); + if (!walletInstance->SetHDMasterKey(masterPubKey)) + throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + } + CPubKey newDefaultKey; + if (walletInstance->GetKeyFromPool(newDefaultKey)) { + walletInstance->SetDefaultKey(newDefaultKey); + if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) { + InitError(_("Cannot write default address") += "\n"); + return NULL; + } + } + + walletInstance->SetBestChain(chainActive.GetLocator()); + } + else if (IsArgSet("-usehd")) { + bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + if (walletInstance->IsHDEnabled() && !useHD) { + InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile)); + return NULL; + } + if (!walletInstance->IsHDEnabled() && useHD) { + InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile)); + return NULL; + } + } + + LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); + + RegisterValidationInterface(walletInstance); + + CBlockIndex *pindexRescan = chainActive.Tip(); + if (GetBoolArg("-rescan", false)) + pindexRescan = chainActive.Genesis(); + else + { + CWalletDB walletdb(walletFile); + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = FindForkInGlobalIndex(chainActive, locator); + else + pindexRescan = chainActive.Genesis(); + } + if (chainActive.Tip() && chainActive.Tip() != pindexRescan) + { + //We can't rescan beyond non-pruned blocks, stop and throw an error + //this might happen if a user uses a old wallet within a pruned node + // or if he ran -disablewallet for a longer time, then decided to re-enable + if (fPruneMode) + { + CBlockIndex *block = chainActive.Tip(); + while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block) + block = block->pprev; + + if (pindexRescan != block) { + InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)")); + return NULL; + } + } + + uiInterface.InitMessage(_("Rescanning...")); + LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); + nStart = GetTimeMillis(); + walletInstance->ScanForWalletTransactions(pindexRescan, true); + LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); + walletInstance->SetBestChain(chainActive.GetLocator()); + CWalletDB::IncrementUpdateCounter(); + + // Restore wallet transaction metadata after -zapwallettxes=1 + if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") + { + CWalletDB walletdb(walletFile); + + BOOST_FOREACH(const CWalletTx& wtxOld, vWtx) + { + uint256 hash = wtxOld.GetHash(); + std::map::iterator mi = walletInstance->mapWallet.find(hash); + if (mi != walletInstance->mapWallet.end()) + { + const CWalletTx* copyFrom = &wtxOld; + CWalletTx* copyTo = &mi->second; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + copyTo->nTimeReceived = copyFrom->nTimeReceived; + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + copyTo->nOrderPos = copyFrom->nOrderPos; + walletdb.WriteTx(*copyTo); + } + } + } + } + walletInstance->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); + + { + LOCK(walletInstance->cs_wallet); + LogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize()); + LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size()); + LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size()); + } + + return walletInstance; +} + +bool CWallet::InitLoadWallet() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + pwalletMain = NULL; + LogPrintf("Wallet disabled!\n"); + return true; + } + + std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); + + CWallet * const pwallet = CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + pwalletMain = pwallet; + + return true; +} + +std::atomic CWallet::fFlushThreadRunning(false); + +void CWallet::postInitProcess(boost::thread_group& threadGroup) +{ + // Add wallet transactions that aren't already in a block to mempool + // Do this here as mempool requires genesis block to be loaded + ReacceptWalletTransactions(); + + // Run a thread to flush wallet periodically + if (!CWallet::fFlushThreadRunning.exchange(true)) { + threadGroup.create_thread(ThreadFlushWalletDB); + } +} + +bool CWallet::ParameterInteraction() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); + } + + if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) { + // Rewrite just private keys: rescan to find transactions + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + + // -zapwallettx implies a rescan + if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); + } + + if (GetBoolArg("-sysperms", false)) + return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); + if (GetArg("-prune", 0) && GetBoolArg("-rescan", false)) + return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); + + if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-minrelaytxfee") + " " + + _("The wallet will avoid paying less than the minimum relay fee.")); + + if (IsArgSet("-mintxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", ""))); + if (n > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on every transaction.")); + CWallet::minTxFee = CFeeRate(n); + } + if (IsArgSet("-fallbackfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), GetArg("-fallbackfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee estimates are not available.")); + CWallet::fallbackFee = CFeeRate(nFeePerK); + } + if (IsArgSet("-paytxfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) + return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you send a transaction.")); + + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), + GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (IsArgSet("-maxtxfee")) + { + CAmount nMaxFee = 0; + if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) + return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", ""))); + if (nMaxFee > HIGH_MAX_TX_FEE) + InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + maxTxFee = nMaxFee; + if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + } + } + nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); + fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + + if (fSendFreeTransactions && GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0) + return InitError("Creation of free transactions with their relay disabled is not supported."); + + return true; +} + +bool CWallet::BackupWallet(const std::string& strDest) +{ + if (!fFileBacked) + return false; + while (true) + { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0) + { + // Flush log data to the dat file + bitdb.CloseDb(strWalletFile); + bitdb.CheckpointLSN(strWalletFile); + bitdb.mapFileUseCount.erase(strWalletFile); + + // Copy wallet file + boost::filesystem::path pathSrc = GetDataDir() / strWalletFile; + boost::filesystem::path pathDest(strDest); + if (boost::filesystem::is_directory(pathDest)) + pathDest /= strWalletFile; + + try { +#if BOOST_VERSION >= 104000 + boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); +#else + boost::filesystem::copy_file(pathSrc, pathDest); +#endif + LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string()); + return true; + } catch (const boost::filesystem::filesystem_error& e) { + LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what()); + return false; + } + } + } + MilliSleep(100); + } + return false; +} + + +CKeyPool::CKeyPool() +{ + nTime = GetTime(); +} + +CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn) +{ + nTime = GetTime(); + vchPubKey = vchPubKeyIn; +} + +CWalletKey::CWalletKey(int64_t nExpires) +{ + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; +} + +void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) +{ + // Update the tx's hashBlock + hashBlock = pindex->GetBlockHash(); + + // set the position of the transaction in the block + nIndex = posInBlock; +} + +int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const +{ + if (hashUnset()) + return 0; + + AssertLockHeld(cs_main); + + // Find the block it claims to be in + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + pindexRet = pindex; + return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); +} + +int64_t CMerkleTx::GetTimeOfTokenInChain() const +{ + /*const CBlockIndex* &pindexRet;*/ + if (hashUnset()) + return 0; + + AssertLockHeld(cs_main); + + // Find the block it claims to be in + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + return pindex->GetBlockTime(); +} + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) +{ + return ::AcceptToMemoryPool(mempool, state, tx, false, NULL, NULL, false, nAbsurdFee); //The fourth parameter is modified to false +} diff --git a/.svn/pristine/de/de8aee2ac1326ecb43d5913fd236927368019496.svn-base b/.svn/pristine/de/de8aee2ac1326ecb43d5913fd236927368019496.svn-base new file mode 100644 index 0000000..153e439 --- /dev/null +++ b/.svn/pristine/de/de8aee2ac1326ecb43d5913fd236927368019496.svn-base @@ -0,0 +1,1185 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_WALLET_H +#define BITCOIN_WALLET_WALLET_H + +#include "amount.h" +#include "streams.h" +#include "tinyformat.h" +#include "ui_interface.h" +#include "utilstrencodings.h" +#include "validationinterface.h" +#include "script/ismine.h" +#include "script/sign.h" +#include "wallet/crypter.h" +#include "wallet/walletdb.h" +#include "wallet/rpcwallet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +extern CWallet* pwalletMain; + +/** + * Settings + */ +extern CFeeRate payTxFee; +extern unsigned int nTxConfirmTarget; +extern bool bSpendZeroConfChange; +extern bool fSendFreeTransactions; +extern bool fWalletRbf; + +static const unsigned int DEFAULT_KEYPOOL_SIZE = 100; +//! -paytxfee default +static const CAmount DEFAULT_TRANSACTION_FEE = 50000; +//! -fallbackfee default +static const CAmount DEFAULT_FALLBACK_FEE = 20000; +//! -mintxfee default +static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000; +//! minimum recommended increment for BIP 125 replacement txs +static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000; +//! target minimum change amount +static const CAmount MIN_CHANGE = CENT; +//! final minimum change amount after paying for fees +static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2; +//! Default for -spendzeroconfchange +static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true; +//! Default for -sendfreetransactions +static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false; +//! Default for -walletrejectlongchains +static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false; +//! -txconfirmtarget default +static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 8; +//! -walletrbf default +static const bool DEFAULT_WALLET_RBF = false; +//! Largest (in bytes) free transaction we're willing to create +static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; +static const bool DEFAULT_WALLETBROADCAST = true; +static const bool DEFAULT_DISABLE_WALLET = false; +//! if set, all keys will be derived by using BIP32 +static const bool DEFAULT_USE_HD_WALLET = true; + +extern const char * DEFAULT_WALLET_DAT; + +class CBlockIndex; +class CCoinControl; +class COutput; +class CReserveKey; +class CScript; +class CTxMemPool; +class CWalletTx; +/** (client) version numbers for particular wallet features */ +enum WalletFeature +{ + FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output) + + FEATURE_WALLETCRYPT = 40000, // wallet encryption + FEATURE_COMPRPUBKEY = 60000, // compressed public keys + + FEATURE_HD = 130000, // Hierarchical key derivation after BIP32 (HD Wallet) + FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version +}; + + +/** A key pool entry*/ +class CKeyPool +{ +public: + int64_t nTime; + CPubKey vchPubKey; + + CKeyPool(); + CKeyPool(const CPubKey& vchPubKeyIn); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + int nVersion = s.GetVersion(); + if (!(s.GetType() & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(nTime); + READWRITE(vchPubKey); + } +}; + +/** Address book data */ +class CAddressBookData +{ +public: + std::string name; + std::string purpose; + + CAddressBookData() + { + purpose = "unknown"; + } + + typedef std::map StringMap; + StringMap destdata; +}; + + + +struct CRecipient +{ + CScript scriptPubKey; + CAmount nAmount; + bool fSubtractFeeFromAmount; +}; + +struct CRecipientToken +{ + CScript scriptPubKey; + CAmount nAmount; + uint64_t nvalue; + bool fSubtractFeeFromAmount; +}; + +typedef std::map mapValue_t; + + +static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (!mapValue.count("n")) + { + nOrderPos = -1; // TODO: calculate elsewhere + return; + } + nOrderPos = atoi64(mapValue["n"].c_str()); +} + + +static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (nOrderPos == -1) + return; + mapValue["n"] = i64tostr(nOrderPos); +} + +struct COutputEntry +{ + CTxDestination destination; + CAmount amount; + int vout; +}; + +/** A transaction with a merkle branch linking it to the block chain. */ +class CMerkleTx +{ +private: + /** Constant used in hashBlock to indicate tx has been abandoned **/ + static const uint256 ABANDON_HASH; + +public: + CTransactionRef tx; + uint256 hashBlock; + + /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest + * block in the chain we know this or any in-wallet dependency conflicts + * with. Older clients interpret nIndex == -1 as unconfirmed for backward + * compatibility. + */ + int nIndex; + + CMerkleTx() + { + SetTx(MakeTransactionRef()); + Init(); + } + + CMerkleTx(CTransactionRef arg) + { + SetTx(std::move(arg)); + Init(); + } + + /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected. + * TODO: adapt callers and remove this operator. */ + operator const CTransaction&() const { return *tx; } + + void Init() + { + hashBlock = uint256(); + nIndex = -1; + } + + void SetTx(CTransactionRef arg) + { + tx = std::move(arg); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + std::vector vMerkleBranch; // For compatibility with older versions. + READWRITE(tx); + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + } + + void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock); + + /** + * Return depth of transaction in blockchain: + * <0 : conflicts with a transaction this deep in the blockchain + * 0 : in memory pool, waiting to be included in a block + * >=1 : this many blocks deep in the main chain + */ + int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; + int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } + bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; } + int GetBlocksToMaturity() const; + /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ + bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state); + bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } + bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } + void setAbandoned() { hashBlock = ABANDON_HASH; } + + int64_t GetTimeOfTokenInChain()const; //The time to get the block + const uint256& GetHash() const { return tx->GetHash(); } + bool IsCoinBase() const { return tx->IsCoinBase(); } +}; + +/** + * A transaction with a bunch of additional info that only the owner cares about. + * It includes any unrecorded transactions needed to link it back to the block chain. + */ +class CWalletTx : public CMerkleTx +{ +private: + const CWallet* pwallet; + +public: + mapValue_t mapValue; + std::vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; //!< time received by this node + unsigned int nTimeSmart; + /** + * From me flag is set to 1 for transactions that were created by the wallet + * on this bitcoin node, and set to 0 for transactions that were created + * externally and came in through the network or sendrawtransaction RPC. + */ + char fFromMe; + std::string strFromAccount; + int64_t nOrderPos; //!< position in ordered transaction list + + // memory only + mutable bool fDebitCached; + mutable bool fCreditCached; + mutable bool fImmatureCreditCached; + mutable bool fAvailableCreditCached; + mutable bool fWatchDebitCached; + mutable bool fWatchCreditCached; + mutable bool fImmatureWatchCreditCached; + mutable bool fAvailableWatchCreditCached; + mutable bool fChangeCached; + mutable CAmount nDebitCached; + mutable CAmount nCreditCached; + mutable CAmount nImmatureCreditCached; + mutable CAmount nAvailableCreditCached; + mutable CAmount nWatchDebitCached; + mutable CAmount nWatchCreditCached; + mutable CAmount nImmatureWatchCreditCached; + mutable CAmount nAvailableWatchCreditCached; + mutable CAmount nChangeCached; + + CWalletTx() + { + Init(NULL); + } + + CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg)) + { + Init(pwalletIn); + } + + void Init(const CWallet* pwalletIn) + { + pwallet = pwalletIn; + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + nTimeSmart = 0; + fFromMe = false; + strFromAccount.clear(); + fDebitCached = false; + fCreditCached = false; + fImmatureCreditCached = false; + fAvailableCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nImmatureCreditCached = 0; + nAvailableCreditCached = 0; + nWatchDebitCached = 0; + nWatchCreditCached = 0; + nAvailableWatchCreditCached = 0; + nImmatureWatchCreditCached = 0; + nChangeCached = 0; + nOrderPos = -1; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + if (ser_action.ForRead()) + Init(NULL); + char fSpent = false; + + if (!ser_action.ForRead()) + { + mapValue["fromaccount"] = strFromAccount; + + WriteOrderPos(nOrderPos, mapValue); + + if (nTimeSmart) + mapValue["timesmart"] = strprintf("%u", nTimeSmart); + } + + READWRITE(*(CMerkleTx*)this); + std::vector vUnused; //!< Used to be vtxPrev + READWRITE(vUnused); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (ser_action.ForRead()) + { + strFromAccount = mapValue["fromaccount"]; + + ReadOrderPos(nOrderPos, mapValue); + + nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; + } + + mapValue.erase("fromaccount"); + mapValue.erase("version"); + mapValue.erase("spent"); + mapValue.erase("n"); + mapValue.erase("timesmart"); + } + + //! make sure balances are recalculated + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fImmatureCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void BindWallet(CWallet *pwalletIn) + { + pwallet = pwalletIn; + MarkDirty(); + } + + //! filter decides which addresses will count towards the debit + CAmount GetDebit(const isminefilter& filter) const; + CAmount GetCredit(const isminefilter& filter) const; + CAmount GetImmatureCredit(bool fUseCache=true) const; + CAmount GetAvailableCredit(bool fUseCache=true) const; + //add by xxy Calculate lock deposit + CAmount GetLockCredit(bool fUseCache = true) const; + //end + CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const; + CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const; + CAmount GetChange() const; + + void GetAmounts(std::list& listReceived, + std::list& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const; + + void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived, + CAmount& nSent, CAmount& nFee, const isminefilter& filter) const; + + bool IsFromMe(const isminefilter& filter) const + { + return (GetDebit(filter) > 0); + } + + // True if only scriptSigs are different + bool IsEquivalentTo(const CWalletTx& tx) const; + + bool InMempool() const; + bool IsTrusted() const; + + int64_t GetTxTime() const; + int GetRequestCount() const; + + bool RelayWalletTransaction(CConnman* connman); + + std::set GetConflicts() const; +}; + + + + +class COutput +{ +public: + const CWalletTx *tx; + int i; + int nDepth; + bool fSpendable; + bool fSolvable; + + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn) + { + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; + } + + + bool CanBeSentToOhter() const; + bool CanBeAuthorizedToOther() const; + bool CanBeUniqueAuthorizedToOther() const; + + int GetType() const; + int GetIPCExtendType() const; + uint32_t GetIPCStartTime() const; + uint32_t GetIPCStopTime() const; + uint8_t GetIPCreAuthorize() const; + uint8_t GetIPCUniqAuthorize() const; + std::string GetIPCHash() const; + std::string GetIPCTitle() const; + std::string GetIPCLabel() const; + + std::string GetTokenSymbol() const; + uint8_t GetTokenAccuracy() const; + uint64_t GetTokenvalue() const; + + int64_t GetAssetEntryTime() const;//Get the asset entry time + std::string ToString() const; + +}; + + + + +/** Private key that includes an expiration date in case it never gets used. */ +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64_t nTimeCreated; + int64_t nTimeExpires; + std::string strComment; + //! todo: add something to note what created it (user, getnewaddress, change) + //! maybe should have a map property map + + CWalletKey(int64_t nExpires=0); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + int nVersion = s.GetVersion(); + if (!(s.GetType() & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(LIMITED_STRING(strComment, 65536)); + } +}; + +/** + * Internal transfers. + * Database key is acentry. + */ +class CAccountingEntry +{ +public: + std::string strAccount; + CAmount nCreditDebit; + int64_t nTime; + std::string strOtherAccount; + std::string strComment; + mapValue_t mapValue; + int64_t nOrderPos; //!< position in ordered transaction list + uint64_t nEntryNo; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + nOrderPos = -1; + nEntryNo = 0; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + int nVersion = s.GetVersion(); + if (!(s.GetType() & SER_GETHASH)) + READWRITE(nVersion); + //! Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(LIMITED_STRING(strOtherAccount, 65536)); + + if (!ser_action.ForRead()) + { + WriteOrderPos(nOrderPos, mapValue); + + if (!(mapValue.empty() && _ssExtra.empty())) + { + CDataStream ss(s.GetType(), s.GetVersion()); + ss.insert(ss.begin(), '\0'); + ss << mapValue; + ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); + strComment.append(ss.str()); + } + } + + READWRITE(LIMITED_STRING(strComment, 65536)); + + size_t nSepPos = strComment.find("\0", 0, 1); + if (ser_action.ForRead()) + { + mapValue.clear(); + if (std::string::npos != nSepPos) + { + CDataStream ss(std::vector(strComment.begin() + nSepPos + 1, strComment.end()), s.GetType(), s.GetVersion()); + ss >> mapValue; + _ssExtra = std::vector(ss.begin(), ss.end()); + } + ReadOrderPos(nOrderPos, mapValue); + } + if (std::string::npos != nSepPos) + strComment.erase(nSepPos); + + mapValue.erase("n"); + } + +private: + std::vector _ssExtra; +}; + + +/** + * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, + * and provides the ability to create new transactions. + * + */ +class CWallet : public CCryptoKeyStore, public CValidationInterface +{ +private: + static std::atomic fFlushThreadRunning; + + /** + * Select a set of coins such that nValueRet >= nTargetValue and at least + * all coins from coinControl are selected; Never select unconfirmed coins + * if they are not ours + */ + bool SelectCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const; + + //add by xxy + bool SelectNormalCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const; + bool SelectTokenCoins(const std::vector& vAvailableCoins, std::set >& setCoinsRet,std::string & symbol,uint64_t& nTokenValue, const CCoinControl *coinControl = NULL) const; + //end + CWalletDB *pwalletdbEncryption; + + //! the current wallet version: clients below this version are not able to load the wallet + int nWalletVersion; + + //! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded + int nWalletMaxVersion; + + int64_t nNextResend; + int64_t nLastResend; + bool fBroadcastTransactions; + + /** + * Used to keep track of spent outpoints, and + * detect and report conflicts (double-spends or + * mutated transactions where the mutant gets mined). + */ + typedef std::multimap TxSpends; + TxSpends mapTxSpends; + void AddToSpends(const COutPoint& outpoint, const uint256& wtxid); + void AddToSpends(const uint256& wtxid); + + void RemoveFromSpends(const COutPoint& outpoint, const uint256& wtxid); + void RemoveFromSpends(const uint256& wtxid); + + /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ + void MarkConflicted(const uint256& hashBlock, const uint256& hashTx); + + void SyncMetaData(std::pair); + + /* the HD chain data model (external chain counters) */ + CHDChain hdChain; + + bool fFileBacked; + + std::set setKeyPool; + + int64_t nTimeFirstKey; + + /** + * Private version of AddWatchOnly method which does not accept a + * timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if + * the watch key did not previously have a timestamp associated with it. + * Because this is an inherited virtual method, it is accessible despite + * being marked private, but it is marked private anyway to encourage use + * of the other AddWatchOnly which accepts a timestamp and sets + * nTimeFirstKey more intelligently for more efficient rescans. + */ + bool AddWatchOnly(const CScript& dest) override; + +public: + /* + * Main wallet lock. + * This lock protects all the fields added by CWallet + * except for: + * fFileBacked (immutable after instantiation) + * strWalletFile (immutable after instantiation) + */ + mutable CCriticalSection cs_wallet; + + const std::string strWalletFile; + + void LoadKeyPool(int nIndex, const CKeyPool &keypool) + { + setKeyPool.insert(nIndex); + + // If no metadata exists yet, create a default with the pool key's + // creation time. Note that this may be overwritten by actually + // stored metadata for that key later, which is fine. + CKeyID keyid = keypool.vchPubKey.GetID(); + if (mapKeyMetadata.count(keyid) == 0) + mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); + } + + // Map from Key ID (for regular keys) or Script ID (for watch-only keys) to + // key metadata. + std::map mapKeyMetadata; + + typedef std::map MasterKeyMap; + MasterKeyMap mapMasterKeys; + unsigned int nMasterKeyMaxID; + + CWallet() + { + SetNull(); + } + + CWallet(const std::string& strWalletFileIn) : strWalletFile(strWalletFileIn) + { + SetNull(); + fFileBacked = true; + } + + ~CWallet() + { + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + } + + void SetNull() + { + nWalletVersion = FEATURE_BASE; + nWalletMaxVersion = FEATURE_BASE; + fFileBacked = false; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; + nOrderPosNext = 0; + nNextResend = 0; + nLastResend = 0; + nTimeFirstKey = 0; + fBroadcastTransactions = false; + } + + std::map mapWallet; + std::list laccentries; + + typedef std::pair TxPair; + typedef std::multimap TxItems; + TxItems wtxOrdered; + + int64_t nOrderPosNext; + std::map mapRequestCount; + + std::map mapAddressBook; + + CPubKey vchDefaultKey; + + std::set setLockedCoins; + + const CWalletTx* GetWalletTx(const uint256& hash) const; + + std::map TokenValueMap; //The corresponding balance of various tokens - return list to the wallet + std::vector TokensymbolList; //A collection of tokens in the wallet + SecureString curstrWalletPassphrase; //Current wallet password + //! check whether we are allowed to upgrade (or already support) to the named feature + bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } + + /** + * populate vCoins with vector of available COutputs. + */ + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const; + + //add by xxy + void AvailableNormalCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue = false) const; + void AvailableIPCCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue = false) const; + void AvailableTokenCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue = false) const; + // + bool GetSymbolbalance(std::string& tokensymbol, uint64_t& value); + void ListTokenBalance(std::map& TokenList); + uint8_t GetAccuracyBySymbol(std::string& tokensymbol); + uint32_t GetIssueDateBySymbol(std::string& tokensymbol); + void UpdateTokenBalanceList(); + bool IsHaveTheTokensymbol(std::string&tokensymbol); + + /** + * Shuffle and select coins until nTargetValue is reached while avoiding + * small change; This method is stochastic for some inputs and upon + * completion the coin set and corresponding actual target value is + * assembled + */ + bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; + + bool IsSpent(const uint256& hash, unsigned int n) const; + + bool IsLockedCoin(uint256 hash, unsigned int n) const; + void LockCoin(const COutPoint& output); + void UnlockCoin(const COutPoint& output); + void UnlockAllCoins(); + void ListLockedCoins(std::vector& vOutpts); + + /** + * keystore implementation + * Generate a new key + */ + CPubKey GenerateNewKey(); + void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret); + //! Adds a key to the store, and saves it to disk. + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; + //! Adds a key to the store, without saving it to disk (used by LoadWallet) + bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } + //! Load metadata (used by LoadWallet) + bool LoadKeyMetadata(const CTxDestination& pubKey, const CKeyMetadata &metadata); + + bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } + void UpdateTimeFirstKey(int64_t nCreateTime); + + //! Adds an encrypted key to the store, and saves it to disk. + bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) override; + //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) + bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); + bool AddCScript(const CScript& redeemScript) override; + bool LoadCScript(const CScript& redeemScript); + + //! Adds a destination data tuple to the store, and saves it to disk + bool AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value); + //! Erases a destination data tuple in the store and on disk + bool EraseDestData(const CTxDestination &dest, const std::string &key); + //! Adds a destination data tuple to the store, without saving it to disk + bool LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value); + //! Look up a destination data tuple in the store, return true if found false otherwise + bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const; + + //! Adds a watch-only address to the store, and saves it to disk. + bool AddWatchOnly(const CScript& dest, int64_t nCreateTime); + bool RemoveWatchOnly(const CScript &dest) override; + //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) + bool LoadWatchOnly(const CScript &dest); + + bool Unlock(const SecureString& strWalletPassphrase); + bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); + bool EncryptWallet(const SecureString& strWalletPassphrase); + + void GetKeyBirthTimes(std::map &mapKeyBirth) const; + + /** + * Increment the next transaction order id + * @return next transaction order id + */ + int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL); + DBErrors ReorderTransactions(); + bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = ""); + bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew = false); + + void MarkDirty(); + bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose = true); + bool RemoveFromWallet(const CWalletTx& wtxIn, bool fFlushOnClose = true); + bool LoadToWallet(const CWalletTx& wtxIn); + void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) override; + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); + CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + void ReacceptWalletTransactions(); + void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; + std::vector ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman); + CAmount GetBalance() const; + // + CAmount GetDeposit()const; + int GetDepthofJoinTX(); + // + CAmount GetUnconfirmedBalance() const; + CAmount GetImmatureBalance() const; + CAmount GetWatchOnlyBalance() const; + CAmount GetUnconfirmedWatchOnlyBalance() const; + CAmount GetImmatureWatchOnlyBalance() const; + + /** + * Insert additional inputs into the transaction by + * calling CreateTransaction(); + */ + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, bool keepReserveKey = true, const CTxDestination& destChange = CNoDestination()); + + /** + * Create a new transaction paying the recipients with a set of coins + * selected by SelectCoins(); Also create the change output, when needed + * @note passing nChangePosInOut as -1 will result in setting a random position + */ + bool CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + //add by xxy + //Create ordinary transactions + bool CreateNormalTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + //Create a knowledge register transaction + bool CreateIPCRegTransaction(std::string& strReglabel, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + //Create knowledge transfer transactions + bool CreateIPCSendTransaction(std::string& txid,int Index, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + //Create an informed authorization transaction + bool CreateIPCAuthorizationTransaction(std::string& txid, int Index, const std::vector& vecSend, std::string& IPCAuthorizeLabel, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + //Create token reg trade + bool CreateTokenRegTransaction(std::string& strReglabel, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + //Create a token trade -- Support only for one address transaction. + bool CreateTokenTransaction(std::string& tokensymbol, uint64_t TokenValue, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + bool CreateTokenTransactionM(std::string& tokensymbol, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + //end + bool address2pkhash(std::string& address,uint160& pkhash); + //Join and exit the consensus trading interface + bool JoinCampaign(const CTxDestination &address, CAmount deposi, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + bool ExitCampaign(CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + bool PunishRequest(const CTxDestination &address, const std::string evdience, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + + //The interface of the consensus class is submitted, and the addition and exit interface of the dpoc layer is invoked in this interface + bool CommitJoinCampaignTransaction(const CTxDestination &address, CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state); + //Get the consensus reward list + bool GetCurrentRewards(std::vector& timelist, std::vector& valuelist); + + bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state); + + void ListAccountCreditDebit(const std::string& strAccount, std::list& entries); + bool AddAccountingEntry(const CAccountingEntry&); + bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb); + template + bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins,int nseek = 0); + static CFeeRate minTxFee; + static CFeeRate fallbackFee; + /** + * Estimate the minimum fee considering user set parameters + * and the required fee + */ + static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); + /** + * Estimate the minimum fee considering required fee and targetFee or if 0 + * then fee estimation for nConfirmTarget + */ + static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee); + /** + * Return the minimum required fee taking into account the + * floating relay fee and user set minimum transaction fee + */ + static CAmount GetRequiredFee(unsigned int nTxBytes); + + bool NewKeyPool(); + bool TopUpKeyPool(unsigned int kpSize = 0); + void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool); + void KeepKey(int64_t nIndex); + void ReturnKey(int64_t nIndex); + bool GetKeyFromPool(CPubKey &key); + int64_t GetOldestKeyPoolTime(); + void GetAllReserveKeys(std::set& setAddress) const; + + std::set< std::set > GetAddressGroupings(); + std::map GetAddressBalances(); + + CAmount GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter); + CAmount GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter); + std::set GetAccountAddresses(const std::string& strAccount) const; + + isminetype IsMine(const CTxIn& txin) const; + + /** + * Returns amount of debit if the input matches the + * filter, otherwise returns 0 + */ + CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; + isminetype IsMine(const CTxOut& txout) const; + CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const; + bool IsChange(const CTxOut& txout) const; + CAmount GetChange(const CTxOut& txout) const; + bool IsMine(const CTransaction& tx) const; + + /** should probably be renamed to IsRelevantToMe */ + bool IsFromMe(const CTransaction& tx) const; + bool IsFromMe(const CTransaction& tx, int type) const; + CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; + /** Returns whether all of the inputs match the filter */ + bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const; + CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; + CAmount GetChange(const CTransaction& tx) const; + void SetBestChain(const CBlockLocator& loc) override; + + DBErrors LoadWallet(bool& fFirstRunRet); + DBErrors ZapWalletTx(std::vector& vWtx); + DBErrors ZapSelectTx(std::vector& vHashIn, std::vector& vHashOut); + + bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); + + bool DelAddressBook(const CTxDestination& address); + + void UpdatedTransaction(const uint256 &hashTx) override; + + void Inventory(const uint256 &hash) override + { + { + LOCK(cs_wallet); + std::map::iterator mi = mapRequestCount.find(hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + + void GetScriptForMining(boost::shared_ptr &script) override; + void ResetRequestCount(const uint256 &hash) override + { + LOCK(cs_wallet); + mapRequestCount[hash] = 0; + }; + + unsigned int GetKeyPoolSize() + { + AssertLockHeld(cs_wallet); // setKeyPool + return setKeyPool.size(); + } + + bool SetDefaultKey(const CPubKey &vchPubKey); + + //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower + bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); + + //! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) + bool SetMaxVersion(int nVersion); + + //! get the current wallet format (the oldest client version guaranteed to understand this wallet) + int GetVersion() { LOCK(cs_wallet); return nWalletVersion; } + + //! Get wallet transactions that conflict with given transaction (spend same outputs) + std::set GetConflicts(const uint256& txid) const; + + //! Check if a given transaction has any of its outputs spent by another transaction in the wallet + bool HasWalletSpend(const uint256& txid) const; + + //! Flush wallet (bitdb flush) + void Flush(bool shutdown=false); + + //! Verify the wallet database and perform salvage if required + static bool Verify(); + + /** + * Address book entry changed. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyAddressBookChanged; + + /** + * Wallet transaction added, removed or updated. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyTransactionChanged; + + /** Show progress e.g. for rescan */ + boost::signals2::signal ShowProgress; + + /** Watch-only address added */ + boost::signals2::signal NotifyWatchonlyChanged; + + /** Inquire whether this wallet broadcasts transactions. */ + bool GetBroadcastTransactions() const { return fBroadcastTransactions; } + /** Set whether this wallet broadcasts transactions. */ + void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; } + + /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ + bool AbandonTransaction(const uint256& hashTx); + + /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ + bool MarkReplaced(const uint256& originalHash, const uint256& newHash); + + /* Returns the wallets help message */ + static std::string GetWalletHelpString(bool showDebug); + + /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ + static CWallet* CreateWalletFromFile(const std::string walletFile); + static bool InitLoadWallet(); + + static bool LoadWalletFromFile(const std::string filepath); + static bool ExportWalletToFile(const std::string filepath); + + /** + * Wallet post-init setup + * Gives the wallet a chance to register repetitive tasks and complete post-init tasks + */ + void postInitProcess(boost::thread_group& threadGroup); + + /* Wallets parameter interaction */ + static bool ParameterInteraction(); + + bool BackupWallet(const std::string& strDest); + + /* Set the HD chain model (chain child index counters) */ + bool SetHDChain(const CHDChain& chain, bool memonly); + const CHDChain& GetHDChain() { return hdChain; } + + /* Returns true if HD is enabled */ + bool IsHDEnabled(); + + /* Generates a new HD master key (will not be activated) */ + CPubKey GenerateNewHDMasterKey(); + + /* Set the current HD master key (will reset the chain child index counters) */ + bool SetHDMasterKey(const CPubKey& key); +}; + +/** A key allocated from the key pool. */ +class CReserveKey : public CReserveScript +{ +protected: + CWallet* pwallet; + int64_t nIndex; + CPubKey vchPubKey; +public: + CReserveKey(CWallet* pwalletIn) + { + nIndex = -1; + pwallet = pwalletIn; + } + + ~CReserveKey() + { + ReturnKey(); + } + + void ReturnKey(); + bool GetReservedKey(CPubKey &pubkey); + void KeepKey(); + void KeepScript() { KeepKey(); } +}; + + +/** + * Account information. + * Stored in wallet with key "acc"+string account name. + */ +class CAccount +{ +public: + CPubKey vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey = CPubKey(); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + int nVersion = s.GetVersion(); + if (!(s.GetType() & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + } +}; + +// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes) +// ContainerType is meant to hold pair, and be iterable +// so that each entry corresponds to each vIn, in order. +template +bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins,int nseek) +{ + // Fill in dummy signatures for fee calculation. + int nIn = 0; + if (nseek > 0) + nIn = nseek; + for (const auto& coin : coins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + { + return false; + } else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + return true; +} + +#endif // BITCOIN_WALLET_WALLET_H diff --git a/.svn/pristine/df/dfd7921a3fc269ae1c424536bdbb84942ea4adde.svn-base b/.svn/pristine/df/dfd7921a3fc269ae1c424536bdbb84942ea4adde.svn-base new file mode 100644 index 0000000..20d72ca --- /dev/null +++ b/.svn/pristine/df/dfd7921a3fc269ae1c424536bdbb84942ea4adde.svn-base @@ -0,0 +1,833 @@ + + + IpcDetails + + + + 0 + 0 + 776 + 726 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 776 + 935 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + 590 + 16777215 + + + + + 10 + + + 15 + + + + + Qt::Horizontal + + + + + + + false + + + font-size : 12px;color: rgb(153, 153, 153); + + + TextLabel + + + + + + + Qt::Horizontal + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + Electronic tag + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + Txid + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + confirm + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + font-size : 14px;color: rgb(153, 153, 153); + + + TextLabel + + + + + + + Qt::Horizontal + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + type + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + end time + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + font-size : 14px;color: rgb(153, 153, 153); + + + Qt::ImhNone + + + TextLabel + + + true + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + authorize type + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + start time + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + limit + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px; +color: rgb(51, 51, 51); + + + name + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + font-size : 14px;color: rgb(51, 51, 51); + + + introduction + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + font-size : 12px; + + + TextLabel + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + true + + + font-size : 12px; + + + + TextLabel + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-size : 14px;color: rgb(153, 153, 153); + + + TextLabel + + + + + + + font-size : 14px;color: rgb(153, 153, 153); + + + TextLabel + + + + + + + font-size : 14px;color: rgb(153, 153, 153); + + + TextLabel + + + + + + + + 0 + 70 + + + + + 16777215 + 70 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + Qt::NoFocus + + + true + + + font-size : 14px;background-color: rgb(242, 241, 240); + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + font-size : 14px;color: rgb(51, 51, 51); + + + inure time + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + font-size : 14px;color: rgb(153, 153, 153); + + + TextLabel + + + + + + + font-size : 14px;color: rgb(153, 153, 153); + + + TextLabel + + + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 186 + 47 + + + + + 186 + 47 + + + + Qt::NoFocus + + + background-image: url(:/res/png/buttons/186_47.png);border:none;font-size : 16px; +color: rgb(255,255,255); + + + transfer IP + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 186 + 47 + + + + + 186 + 47 + + + + Qt::NoFocus + + + background-image: url(:/res/png/buttons/186_47.png);border:none;font-size : 16px; +color: rgb(255,255,255); + + + authorization IP + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 186 + 47 + + + + + 186 + 47 + + + + + 205 + 44 + + + + Qt::NoFocus + + + background-image: url(:/res/png/buttons/186_47.png);border:none;font-size : 16px; +color: rgb(255,255,255); + + + Export certificate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 20 + 30 + 27 + + + + + 30 + 16777215 + + + + back + + + + + + 60 + 20 + 72 + 27 + + + + IPC details + + + Qt::AlignCenter + + + + + + + + + diff --git a/.svn/pristine/f0/f0f18f5f9b421661bc4ecfa1c0d9f31c8d708d12.svn-base b/.svn/pristine/f0/f0f18f5f9b421661bc4ecfa1c0d9f31c8d708d12.svn-base new file mode 100644 index 0000000..d9895db --- /dev/null +++ b/.svn/pristine/f0/f0f18f5f9b421661bc4ecfa1c0d9f31c8d708d12.svn-base @@ -0,0 +1,1410 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "chain.h" +#include "coins.h" +#include "consensus/validation.h" +#include "core_io.h" +#include "init.h" +#include "keystore.h" +#include "validation.h" +#include "merkleblock.h" +#include "net.h" +#include "policy/policy.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" +#include "txmempool.h" +#include "uint256.h" +#include "utilstrencodings.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif + +#include + +#include +#include + +#include + + +using namespace std; + +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) +{ + txnouttype type; + vector addresses; + int nRequired; + + out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey))); + if (fIncludeHex) + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + + if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + out.push_back(Pair("type", GetTxnOutputType(type))); + return; + } + + out.push_back(Pair("reqSigs", nRequired)); + out.push_back(Pair("type", GetTxnOutputType(type))); + + UniValue a(UniValue::VARR); + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + out.push_back(Pair("addresses", a)); +} + +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) +{ + entry.push_back(Pair("txid", tx.GetHash().GetHex())); + entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex())); + entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); + entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx))); + entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + CTransactionRef prevTx; + uint256 hashblock; + std::vector txoutdestes; + std::vector addresses; + addresses.clear(); + txnouttype type; + int nRequired; + UniValue vin(UniValue::VARR); + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const CTxIn& txin = tx.vin[i]; + UniValue in(UniValue::VOBJ); + if (tx.IsCoinBase()) + in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + else { + in.push_back(Pair("txid", txin.prevout.hash.GetHex())); + in.push_back(Pair("vout", (int64_t)txin.prevout.n)); + if (fTxIndex) + { + UniValue add(UniValue::VARR); + if (!GetTransaction(txin.prevout.hash, prevTx, Params().GetConsensus(), hashblock, true)) + { + if (!GetCachedChainTransaction(tx.vin[i].prevout.hash, prevTx)) + throw JSONRPCError(RPC_VERIFY_ERROR, "bad-input,Wrongful."); + } + const CTxOut& prev = prevTx->vout[txin.prevout.n]; + if (!ExtractDestinations(prev.scriptPubKey, type, txoutdestes, nRequired)) + throw JSONRPCError(RPC_VERIFY_ERROR, "txin-address-unextracted."); + + BOOST_FOREACH(CTxDestination &dest, txoutdestes){ + add.push_back(CBitcoinAddress(dest).ToString()); + } + + + in.push_back(Pair("addresses",add)); + in.push_back(Pair("type",(int64_t)prev.txType)); + switch ((int64_t)prev.txType) + { + case 4: + { + in.push_back(Pair("tokensymbol", prev.tokenRegLabel.getTokenSymbol())); + std::string strvalue = std::to_string(ValueFromTCoins(prev.tokenRegLabel.totalCount, (int)tokenDataMap[prev.tokenRegLabel.getTokenSymbol()].accuracy).get_int64()); + in.push_back(Pair("tokenvalue", strvalue)); + in.push_back(Pair("accuracy", prev.tokenRegLabel.accuracy)); + } + break; + case 5: + { + in.push_back(Pair("tokensymbol", prev.tokenLabel.getTokenSymbol())); + std::string strvalue = std::to_string(ValueFromTCoins(prev.tokenLabel.value, (int)tokenDataMap[prev.tokenLabel.getTokenSymbol()].accuracy).get_int64()); + in.push_back(Pair("tokenvalue", strvalue)); + in.push_back(Pair("accuracy", prev.tokenLabel.accuracy)); + } + break; + default: + break; + } + in.push_back(Pair("nvalue", ValueFromAmount(prev.nValue))); + } + //end + UniValue o(UniValue::VOBJ); + o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true))); + o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + in.push_back(Pair("scriptSig", o)); + } + if (tx.HasWitness()) { + UniValue txinwitness(UniValue::VARR); + for (unsigned int j = 0; j < tx.vin[i].scriptWitness.stack.size(); j++) { + std::vector item = tx.vin[i].scriptWitness.stack[j]; + txinwitness.push_back(HexStr(item.begin(), item.end())); + } + in.push_back(Pair("txinwitness", txinwitness)); + } + in.push_back(Pair("sequence", (int64_t)txin.nSequence)); + vin.push_back(in); + } + entry.push_back(Pair("vin", vin)); + UniValue vout(UniValue::VARR); + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut& txout = tx.vout[i]; + UniValue out(UniValue::VOBJ); + out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + out.push_back(Pair("n", (int64_t)i)); + out.push_back(Pair("type", (int64_t)txout.txType)); + + switch (txout.txType) + { + case 1: + out.push_back(Pair("devotetype", txout.devoteLabel.ExtendType)); + out.push_back(Pair("devotepubkeyhash160", txout.devoteLabel.hash.GetHex())); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel", txout.txLabel)); + break; + + case 4: + { + out.push_back(Pair("TokenSymbol", txout.tokenRegLabel.getTokenSymbol())); + out.push_back(Pair("TokenValue", txout.tokenRegLabel.value)); + out.push_back(Pair("TokenHash", txout.tokenRegLabel.hash.GetHex())); + out.push_back(Pair("TokenLabel", txout.tokenRegLabel.getTokenLabel())); + out.push_back(Pair("TokenIssue", txout.tokenRegLabel.issueDate)); + std::string strvalue = std::to_string(ValueFromTCoins(txout.tokenRegLabel.totalCount, (int)tokenDataMap[txout.tokenRegLabel.getTokenSymbol()].accuracy).get_int64()); + out.push_back(Pair("TokenTotalCount", strvalue)); + out.push_back(Pair("accuracy", txout.tokenRegLabel.accuracy)); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel", txout.txLabel)); + } + + break; + + case 5: + { + out.push_back(Pair("TokenSymbol", txout.tokenLabel.getTokenSymbol())); + std::string strvalue =std:: to_string(ValueFromTCoins(txout.tokenLabel.value, (int)tokenDataMap[txout.tokenLabel.getTokenSymbol()].accuracy).get_int64()); + out.push_back(Pair("TokenValue", strvalue)); + out.push_back(Pair("accuracy", txout.tokenLabel.accuracy)); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel", txout.txLabel)); + } + + break; + + case 2: + case 3: + out.push_back(Pair("extype", txout.ipcLabel.ExtendType)); + out.push_back(Pair("starttime", txout.ipcLabel.startTime)); + out.push_back(Pair("stoptime", txout.ipcLabel.stopTime)); + out.push_back(Pair("reauthorize", txout.ipcLabel.reAuthorize)); + out.push_back(Pair("uniqueauthorize", txout.ipcLabel.uniqueAuthorize)); + out.push_back(Pair("hashLen", txout.ipcLabel.hashLen)); + out.push_back(Pair("IPChash", txout.ipcLabel.hash.GetHex())); + out.push_back(Pair("IPCTitle", txout.ipcLabel.labelTitle)); + out.push_back(Pair("txLabelLen", txout.txLabelLen)); + out.push_back(Pair("txLabel",txout.txLabel)); + break; + + default: + case 0: + break; + } + + UniValue o(UniValue::VOBJ); + ScriptPubKeyToJSON(txout.scriptPubKey, o, true); + out.push_back(Pair("scriptPubKey", o)); + vout.push_back(out); + } + entry.push_back(Pair("vout", vout)); + + if (!hashBlock.IsNull()) { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) { + entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); + entry.push_back(Pair("time", pindex->GetBlockTime())); + entry.push_back(Pair("blocktime", pindex->GetBlockTime())); + } + else + entry.push_back(Pair("confirmations", 0)); + } + } +} + +UniValue getrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "getrawtransaction \"txid\" ( verbose )\n" + + "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" + "enabled, it also works for blockchain transactions.\n" + "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" + + "\nReturn the raw transaction data.\n" + "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" + "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n" + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n" + + "\nResult (if verbose is not set or set to false):\n" + "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + + "\nResult (if verbose is set to true):\n" + "{\n" + " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" + " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" + " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" + " \"size\" : n, (numeric) The serialized transaction size\n" + " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) \n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"sequence\": n (numeric) The script sequence number\n" + " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + " \"blockhash\" : \"hash\", (string) the block hash\n" + " \"confirmations\" : n, (numeric) The confirmations\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getrawtransaction", "\"mytxid\"") + + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + + HelpExampleRpc("getrawtransaction", "\"mytxid\", true") + ); + + LOCK(cs_main); + + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + + // Accept either a bool (true) or a num (>=1) to indicate verbose output. + bool fVerbose = false; + if (request.params.size() > 1) { + if (request.params[1].isNum()) { + if (request.params[1].get_int() != 0) { + fVerbose = true; + } + } + else if(request.params[1].isBool()) { + if(request.params[1].isTrue()) { + fVerbose = true; + } + } + else { + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid type provided. Verbose parameter must be a boolean."); + } + } + + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + + string strHex = EncodeHexTx(*tx, RPCSerializationFlags()); + + if (!fVerbose) + return strHex; + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", strHex)); + TxToJSON(*tx, hashBlock, result); + + return result; +} + +UniValue getfeeoftxid(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + throw runtime_error( + "getfeeoftxid \"txid\" \n" + + "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" + "enabled, it also works for blockchain transactions.\n" + "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" + + "\nReturn the raw transaction fee.\n" + "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + + "\nResult (if verbose is not set or set to false):\n" + "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + + "\nResult :\n" + "{\n" + " \"fee\" : \"amount\", (string) The transaction fee \n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getfeeoftxid", "\"mytxid\"") + ); + + LOCK(cs_main); + + uint256 hash = ParseHashV(request.params[0], "parameter 1"); + + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); + CAmount nValue = 0; + nValue = Getfeebytxid(*tx); + UniValue result(UniValue::VOBJ); + result.push_back(Pair("fee", ValueFromAmount(nValue))); + + return result; +} + +UniValue gettxoutproof(const JSONRPCRequest& request) +{ + if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) + throw runtime_error( + "gettxoutproof [\"txid\",...] ( blockhash )\n" + "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" + "\nNOTE: By default this function only works sometimes. This is when there is an\n" + "unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option or\n" + "specify the block in which the transaction is included manually (by blockhash).\n" + "\nArguments:\n" + "1. \"txids\" (string) A json array of txids to filter\n" + " [\n" + " \"txid\" (string) A transaction hash\n" + " ,...\n" + " ]\n" + "2. \"blockhash\" (string, optional) If specified, looks for txid in the block with this hash\n" + "\nResult:\n" + "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" + ); + + set setTxids; + uint256 oneTxid; + UniValue txids = request.params[0].get_array(); + for (unsigned int idx = 0; idx < txids.size(); idx++) { + const UniValue& txid = txids[idx]; + if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); + uint256 hash(uint256S(txid.get_str())); + if (setTxids.count(hash)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); + setTxids.insert(hash); + oneTxid = hash; + } + + LOCK(cs_main); + + CBlockIndex* pblockindex = NULL; + + uint256 hashBlock; + if (request.params.size() > 1) + { + hashBlock = uint256S(request.params[1].get_str()); + if (!mapBlockIndex.count(hashBlock)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + pblockindex = mapBlockIndex[hashBlock]; + } else { + CCoins coins; + if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) + pblockindex = chainActive[coins.nHeight]; + } + + if (pblockindex == NULL) + { + CTransactionRef tx; + if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); + if (!mapBlockIndex.count(hashBlock)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); + pblockindex = mapBlockIndex[hashBlock]; + } + + CBlock block; + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + unsigned int ntxFound = 0; + for (const auto& tx : block.vtx) + if (setTxids.count(tx->GetHash())) + ntxFound++; + if (ntxFound != setTxids.size()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); + + CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); + CMerkleBlock mb(block, setTxids); + ssMB << mb; + std::string strHex = HexStr(ssMB.begin(), ssMB.end()); + return strHex; +} + +UniValue verifytxoutproof(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "verifytxoutproof \"proof\"\n" + "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" + "and throwing an RPC error if the block is not in our best chain\n" + "\nArguments:\n" + "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" + "\nResult:\n" + "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" + ); + + CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); + CMerkleBlock merkleBlock; + ssMB >> merkleBlock; + + UniValue res(UniValue::VARR); + + vector vMatch; + vector vIndex; + if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) + return res; + + LOCK(cs_main); + + if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); + + BOOST_FOREACH(const uint256& hash, vMatch) + res.push_back(hash.GetHex()); + return res; +} + +UniValue createrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) + throw runtime_error( + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" + "\nCreate a transaction spending the given inputs and creating new outputs.\n" + "Outputs can be addresses or data.\n" + "Returns hex-encoded raw transaction.\n" + "Note that the transaction's inputs are not signed, and\n" + "it is not stored in the wallet or transmitted to the network.\n" + + "\nArguments:\n" + "1. \"inputs\" (array, required) A json array of json objects\n" + " [\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"sequence\":n (numeric, optional) The sequence number\n" + " } \n" + " ,...\n" + " ]\n" + "2. \"outputs\" (object, required) a json object with outputs\n" + " {\n" + " \"address\": x.xxx, (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" + " \"data\": \"hex\" (string, required) The key is \"data\", the value is hex encoded data\n" + " ,...\n" + " }\n" + "3. txtype (numeric, optional, default=0) tx type \n" + "\nResult:\n" + "\"transaction\" (string) hex string of the transaction\n" + + "\nExamples:\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VARR)(UniValue::VARR)(UniValue::VNUM), true); + if (request.params[0].isNull() || request.params[1].isNull()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); + + UniValue inputs = request.params[0].get_array(); + UniValue outputs = request.params[1].get_array(); + + CMutableTransaction rawTx; + int TxType = 0; + if (request.params.size() > 2 && !request.params[2].isNull()) { + TxType = request.params[2].get_int(); + } + + multimap mapInput; + multimap::iterator m; + CAmount nVinValue = 0; + CScript strScriptnewout; + + set > setCoins; + setCoins.clear(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + const UniValue& o = input.get_obj(); + + uint256 txid = ParseHashO(o, "txid"); + + const UniValue& vout_v = find_value(o, "vout"); + if (!vout_v.isNum()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + const UniValue& VinValue = find_value(o, "value"); + const UniValue& Vinaddress = find_value(o, "address"); + + if (!VinValue.isNull() || !Vinaddress.isNull()) + { + if (!VinValue.isNum() || !Vinaddress.isStr()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vin value must be num"); + nVinValue = AmountFromValue(VinValue); + const string& addressname = Vinaddress.get_str(); + CBitcoinAddress address(addressname); + strScriptnewout = GetScriptForDestination(address.Get()); + } + + + mapInput.insert(make_pair(txid.GetHex(), nOutput)); + + uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); + + // set the sequence number if passed in the parameters object + const UniValue& sequenceObj = find_value(o, "sequence"); + if (sequenceObj.isNum()) { + int64_t seqNr64 = sequenceObj.get_int64(); + if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); + else + nSequence = (uint32_t)seqNr64; + } + + CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); + + rawTx.vin.push_back(in); + if (pwalletMain->mapWallet.count(txid) == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txid can't found"); + CWalletTx& prewtx = pwalletMain->mapWallet[txid]; + const CWalletTx* pprewtx = &prewtx; + setCoins.insert(make_pair(pprewtx, nOutput)); + } + CAmount totalvalue = 0; + set setNormalAddress; + multimap setIPCAddress; + multimap setTokenAddress; + for (unsigned int idx = 0; idx < outputs.size(); idx++) { + const UniValue& output = outputs[idx]; + const UniValue& o = output.get_obj(); + + const UniValue& txDataObj = find_value(o, "data"); + if (txDataObj.isStr() || txDataObj.isNum()) + { + std::vector data = ParseHexV(txDataObj.getValStr(), "Data"); + CTxOut out(0, CScript() << OP_RETURN << data); + rawTx.vout.push_back(out); + } + else + { + const UniValue& txAddressObj = find_value(o, "address"); + const UniValue& txAmountObj = find_value(o, "value"); + if (!txAddressObj.isStr() || !txAmountObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, ("Invalid parameter, none of address or amount")); + } + + const string& name_ = txAddressObj.get_str(); + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ") + name_); + + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(txAmountObj); + CTxOut out; + + //Screening transaction type + const UniValue& txTypeObj = find_value(o, "txtype"); + if (!txTypeObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txType is not a Number"); + } + uint8_t txType = txTypeObj.get_int(); + + + + const UniValue& exTypeObj = find_value(o, "extype"); + const UniValue& txLabelObj = find_value(o, "txLabel"); + + //IPC transaction value + const UniValue& startTimeObj = find_value(o, "starttime"); + const UniValue& stopTimeObj = find_value(o, "stoptime"); + const UniValue& reAuthObj = find_value(o, "reAu"); + const UniValue& uniAuthObj = find_value(o, "uniAu"); + const UniValue& ipcHashObj = find_value(o, "IPChash"); + const UniValue& ipcTitleObj = find_value(o, "IPCtitle"); + + //Value of token trade + const UniValue& TokenSymbolObj = find_value(o, "tokensymbol"); + const UniValue& TokenHashObj = find_value(o, "tokenhash"); + const UniValue& TokenLabelObj = find_value(o, "tokenlabel"); + const UniValue& TokenIssueObj = find_value(o, "tokenissue"); + const UniValue& TokenTotalCountObj = find_value(o, "totalcount"); + const UniValue& TokenValueObj = find_value(o, "tokenvalue"); + const UniValue& TokenAccuracyObj = find_value(o, "accuracy"); + + //Campaign - related values + const UniValue& devotetypeObj = find_value(o, "devotetype"); + const UniValue& devotepubkeyhash160Obj = find_value(o, "devotepubkeyhash160"); + + IPCLabel ipcLabel; + DevoteLabel devoteLabel; + TokenRegLabel regLabel; + TokenLabel tokenlabel; + + std::string checkStr; + std::string devoterpubkeyhashstring; + uint8_t devoteType; + CKeyID gottenKeyID; + std::string devotecontentstring; + std::string strtxLabel = ""; + strtxLabel=txLabelObj.get_str(); + + switch (txType) + { + case 1://For trading + devoterpubkeyhashstring = devotepubkeyhash160Obj.get_str(); + devoteLabel.hash.SetHex(devoterpubkeyhashstring); + devoteType = devotetypeObj.get_int(); + devoteLabel.ExtendType = devoteType; + out = CTxOut(nAmount, devoteLabel); + break; + + case 2://Ownership transaction + case 3://Licensing deals + if (!exTypeObj.isNum() || + !startTimeObj.isStr() || + !stopTimeObj.isStr() || + !reAuthObj.isNum() || + !uniAuthObj.isNum() || + !ipcHashObj.isStr() || + !ipcTitleObj.isStr()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, IPC Params is invalid"); + } + if (!ParseUInt32(startTimeObj.get_str(), &ipcLabel.startTime)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, IPC startTime is invalid"); + if (!ParseUInt32(stopTimeObj.get_str(), &ipcLabel.stopTime)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, IPC stopTime is invalid"); + ipcLabel.hash.SetHex(ipcHashObj.get_str()); + + setIPCAddress.insert(make_pair(address, ipcLabel.hash)); + + + ipcLabel.ExtendType = exTypeObj.get_int(); + ipcLabel.reAuthorize = reAuthObj.get_int(); + ipcLabel.uniqueAuthorize = uniAuthObj.get_int(); + ipcLabel.labelTitle = ipcTitleObj.get_str(); + out = CTxOut(nAmount, scriptPubKey, (TransactionOutType_t)txType, ipcLabel, strtxLabel); + break; + + case 4: //Trade in token currency + if (!TokenSymbolObj.isStr() || + !TokenHashObj.isStr() || + !TokenLabelObj.isStr() || + !TokenIssueObj.isNum() || + !TokenTotalCountObj.isNum()|| + !TokenAccuracyObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Token Params is invalid"); + } + //There are no various forms of ipc in Symbol and label + checkStr = TokenSymbolObj.get_str(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != string::npos) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol cannot contain IPC"); + } + if (checkStr.size() > 8) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol MaxLen = 8"); + + checkStr = TokenLabelObj.get_str(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != string::npos) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenLabel cannot contain IPC"); + } + if (checkStr.size() > 16) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenLabel MaxLen = 16"); + + //TokenHash + checkStr = TokenHashObj.get_str(); + if (checkStr.size() > 32) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenHash MaxLen = 32(16Byte)"); + + + memcpy(regLabel.TokenSymbol, TokenSymbolObj.get_str().c_str(), TokenSymbolObj.get_str().size()>8 ? 8 : TokenSymbolObj.get_str().size()); + + setTokenAddress.insert(make_pair(address, tokenlabel.getTokenSymbol())); + + regLabel.hash.SetHex(TokenHashObj.get_str()); + memcpy(regLabel.label, TokenLabelObj.get_str().c_str(), TokenLabelObj.get_str().size() > 16 ? 16 : TokenLabelObj.get_str().size()); + regLabel.issueDate = TokenIssueObj.get_int(); + regLabel.totalCount = TokenTotalCountObj.get_int64(); + regLabel.value = TokenTotalCountObj.get_int64(); + regLabel.accuracy = TokenAccuracyObj.get_int(); + out = CTxOut(nAmount, scriptPubKey, regLabel,strtxLabel); + break; + + case 5: //Tokens trading + if (!TokenSymbolObj.isStr() || + !TokenValueObj.isNum() || + !TokenAccuracyObj.isNum()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Token Params is invalid"); + } + checkStr = TokenSymbolObj.get_str(); + boost::to_upper(checkStr); + if (checkStr.find("IPC") != string::npos) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol cannot contain IPC"); + } + if (checkStr.size() > 8) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, TokenSymbol MaxLen = 8"); + + memcpy(tokenlabel.TokenSymbol, TokenSymbolObj.get_str().c_str(), TokenSymbolObj.get_str().size() > 8 ? 8 : TokenSymbolObj.get_str().size()); + + setTokenAddress.insert(make_pair(address, tokenlabel.getTokenSymbol())); + + tokenlabel.value = TokenValueObj.get_int64(); + tokenlabel.accuracy = TokenAccuracyObj.get_int(); + out = CTxOut(nAmount, scriptPubKey, tokenlabel,strtxLabel); + break; + + + case 0://General trading + setNormalAddress.insert(address); + + out = CTxOut(nAmount, scriptPubKey); + break; + default: + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txType out of range"); + + } + + rawTx.vout.push_back(out); + totalvalue += out.nValue; + + + } + + } + CAmount nvalue1(0); + CTxOut newout(nvalue1, strScriptnewout); + rawTx.vout.push_back(newout); + + + if (!(pwalletMain->DummySignTx(rawTx, setCoins))) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "DummySignTx erro!"); + } + unsigned int nBytes = GetVirtualTransactionSize(rawTx); + CAmount fee = pwalletMain->GetMinimumFee(nBytes, 8, mempool) * 2; + //Add a change + if (TxType == 4) + { + CAmount newoutvalue = nVinValue - totalvalue - AmountFromValue(10) - fee; + rawTx.vout[rawTx.vout.size() - 1].nValue = newoutvalue; + } + else + { + CAmount newoutvalue = nVinValue - totalvalue - fee; + rawTx.vout[rawTx.vout.size() - 1].nValue = newoutvalue; + } + + return EncodeHexTx(rawTx); +} + +UniValue decoderawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "decoderawtransaction \"hexstring\"\n" + "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + + "\nResult:\n" + "{\n" + " \"txid\" : \"id\", (string) The transaction id\n" + " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" + " \"size\" : n, (numeric) The transaction size\n" + " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) The output number\n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" + " \"sequence\": n (numeric) The script sequence number\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") + ); + + LOCK(cs_main); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + + CMutableTransaction mtx; + + if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + UniValue result(UniValue::VOBJ); + TxToJSON(CTransaction(std::move(mtx)), uint256(), result); + + return result; +} + +UniValue decodescript(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "decodescript \"hexstring\"\n" + "\nDecode a hex-encoded script.\n" + "\nArguments:\n" + "1. \"hexstring\" (string) the hex encoded script\n" + "\nResult:\n" + "{\n" + " \"asm\":\"asm\", (string) Script public key\n" + " \"hex\":\"hex\", (string) hex encoded public key\n" + " \"type\":\"type\", (string) The output type\n" + " \"reqSigs\": n, (numeric) The required signatures\n" + " \"addresses\": [ (json array of string)\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ],\n" + " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("decodescript", "\"hexstring\"") + + HelpExampleRpc("decodescript", "\"hexstring\"") + ); + + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + + UniValue r(UniValue::VOBJ); + CScript script; + if (request.params[0].get_str().size() > 0){ + vector scriptData(ParseHexV(request.params[0], "argument")); + script = CScript(scriptData.begin(), scriptData.end()); + } else { + // Empty scripts are valid + } + ScriptPubKeyToJSON(script, r, false); + + UniValue type; + type = find_value(r, "type"); + + if (type.isStr() && type.get_str() != "scripthash") { + // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, + // don't return the address for a P2SH of the P2SH. + r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + } + + return r; +} + +/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ +static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) +{ + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", txin.prevout.hash.ToString())); + entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); + entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); + entry.push_back(Pair("error", strMessage)); + vErrorsRet.push_back(entry); +} + +UniValue signrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) + throw runtime_error( + "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" + "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "The second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + "The third optional argument (may be null) is an array of base58-encoded private\n" + "keys that, if given, will be the only keys used to sign the transaction.\n" +#ifdef ENABLE_WALLET + + HelpRequiringPassphrase() + "\n" +#endif + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + " [ (json array of json objects, or 'null' if none provided)\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"scriptPubKey\": \"hex\", (string, required) script key\n" + " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" + " \"amount\": value (numeric, required) The amount spent\n" + " }\n" + " ,...\n" + " ]\n" + "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n" + " [ (json array of strings, or 'null' if none provided)\n" + " \"privatekey\" (string) private key in base58-encoding\n" + " ,...\n" + " ]\n" + "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + + "\nResult:\n" + "{\n" + " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" + " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" + " {\n" + " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" + " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" + " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" + " \"sequence\" : n, (numeric) Script sequence number\n" + " \"error\" : \"text\" (string) Verification or signing error related to the input\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + HelpExampleRpc("signrawtransaction", "\"myhex\"") + ); + +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); + + vector txData(ParseHexV(request.params[0], "argument 1")); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + vector txVariants; + while (!ssData.empty()) { + try { + CMutableTransaction tx; + ssData >> tx; + txVariants.push_back(tx); + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + } + + if (txVariants.empty()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); + + // mergedTx will end up with all the signatures; it + // starts as a clone of the rawtx: + CMutableTransaction mergedTx(txVariants[0]); + + // Fetch previous transactions (inputs): + CCoinsView viewDummy; + CCoinsViewCache view(&viewDummy); + { + LOCK(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(&viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { + const uint256& prevHash = txin.prevout.hash; + CCoins coins; + view.AccessCoins(prevHash); // this is certainly allowed to fail + } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (request.params.size() > 2 && !request.params[2].isNull()) { + fGivenKeys = true; + UniValue keys = request.params[2].get_array(); + for (unsigned int idx = 0; idx < keys.size(); idx++) { + UniValue k = keys[idx]; + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key = vchSecret.GetKey(); + if (!key.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + tempKeystore.AddKey(key); + } + } +#ifdef ENABLE_WALLET + else if (pwalletMain) + EnsureWalletIsUnlocked(); +#endif + + // Add previous txouts given in the RPC call: + if (request.params.size() > 1 && !request.params[1].isNull()) { + UniValue prevTxs = request.params[1].get_array(); + for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { + const UniValue& p = prevTxs[idx]; + if (!p.isObject()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); + + UniValue prevOut = p.get_obj(); + + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + }); + + uint256 txid = ParseHashO(prevOut, "txid"); + + int nOut = find_value(prevOut, "vout").get_int(); + if (nOut < 0) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); + + vector pkData(ParseHexO(prevOut, "scriptPubKey")); + CScript scriptPubKey(pkData.begin(), pkData.end()); + + { + CCoinsModifier coins = view.ModifyCoins(txid); + if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { + string err("Previous output scriptPubKey mismatch:\n"); + err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ + ScriptToAsmStr(scriptPubKey); + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); + } + if ((unsigned int)nOut >= coins->vout.size()) + coins->vout.resize(nOut+1); + coins->vout[nOut].scriptPubKey = scriptPubKey; + coins->vout[nOut].nValue = 0; + if (prevOut.exists("amount")) { + coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount")); + } + } + + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + {"redeemScript", UniValueType(UniValue::VSTR)}, + }); + UniValue v = find_value(prevOut, "redeemScript"); + if (!v.isNull()) { + vector rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } + } + } + } + +#ifdef ENABLE_WALLET + const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); +#else + const CKeyStore& keystore = tempKeystore; +#endif + + int nHashType = SIGHASH_ALL; + if (request.params.size() > 3 && !request.params[3].isNull()) { + static map mapSigHashValues = + boost::assign::map_list_of + (string("ALL"), int(SIGHASH_ALL)) + (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) + (string("NONE"), int(SIGHASH_NONE)) + (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) + (string("SINGLE"), int(SIGHASH_SINGLE)) + (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) + ; + string strHashType = request.params[3].get_str(); + if (mapSigHashValues.count(strHashType)) + nHashType = mapSigHashValues[strHashType]; + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); + } + + bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + + // Script verification errors + UniValue vErrors(UniValue::VARR); + + // Use CTransaction for the constant parts of the + // transaction to avoid rehashing. + const CTransaction txConst(mergedTx); + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { + CTxIn& txin = mergedTx.vin[i]; + const CCoins* coins = view.AccessCoins(txin.prevout.hash); + if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { + TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); + continue; + } + const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + const CAmount& amount = coins->vout[txin.prevout.n].nValue; + + SignatureData sigdata; + // Only sign SIGHASH_SINGLE if there's a corresponding output: + if (!fHashSingle || (i < mergedTx.vout.size())) + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); + + // ... and merge in other signatures: + BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { + if (txv.vin.size() > i) { + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); + } + } + + UpdateTransaction(mergedTx, i, sigdata); + + ScriptError serror = SCRIPT_ERR_OK; + if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } + } + bool fComplete = vErrors.empty(); + unsigned int nBytes = GetVirtualTransactionSize(mergedTx); + std::cout << " tx.size :" << nBytes << std::endl; + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(mergedTx))); + result.push_back(Pair("complete", fComplete)); + if (!vErrors.empty()) { + result.push_back(Pair("errors", vErrors)); + } + + return result; +} + +UniValue sendrawtransaction(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "sendrawtransaction \"hexstring\" ( allowhighfees )\n" + "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" + "\nAlso see createrawtransaction and signrawtransaction calls.\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" + "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" + "\nResult:\n" + "\"hex\" (string) The transaction hash in hex\n" + "\nExamples:\n" + "\nCreate a transaction\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + + "Sign the transaction, and get back the hex\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + "\nSend the transaction (signed hex)\n" + + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") + ); + + LOCK(cs_main); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); + + // parse hex string from parameter + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, request.params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + CTransactionRef tx(MakeTransactionRef(std::move(mtx))); + const uint256& hashTx = tx->GetHash(); +// + bool fLimitFree = false; + CAmount nMaxRawTxFee = maxTxFee; + if (request.params.size() > 1 && request.params[1].get_bool()) + nMaxRawTxFee = 0; + + CCoinsViewCache &view = *pcoinsTip; + const CCoins* existingCoins = view.AccessCoins(hashTx); + bool fHaveMempool = mempool.exists(hashTx); + bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) { + if (state.IsInvalid()) { + throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); + } else { + if (fMissingInputs) { + throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); + } + throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); + } + } + } else if (fHaveChain) { + throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); + } + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + CInv inv(MSG_TX, hashTx); + g_connman->ForEachNode([&inv](CNode* pnode) + { + pnode->PushInventory(inv); + }); + + return hashTx.GetHex(); +} + + +UniValue base58decodetohexstring(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "base58decodetohexstring \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + std::vector vch; + if (!DecodeBase58(request.params[0].get_str().c_str(), vch)) + throw JSONRPCError(RPC_INVALID_PARAMS, "Error: invalid Base58 Input String"); + std::string result = HexStr(vch.begin(), vch.end()); + return result; +} + +UniValue base58encodefromhexstring(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw runtime_error( + "base58encodefromhexstring \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + std::vector vch = ParseHex(request.params[0].get_str()); + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +UniValue gensystemaddress(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1) + throw runtime_error( + "gensystemaddress \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + //Public key plus prefix + std::vectorvch = ParseHex("035762" + request.params[0].get_str()); + //Do two sha256 + uint256 hash; + CSHA256().Write(&vch[0], vch.size()).Finalize(hash.begin()); + CSHA256().Write(hash.begin(), 32).Finalize(hash.begin()); + //Get the first four bytes attached + std::vector result = ParseHex("035762" + request.params[0].get_str() + "00000000"); + memcpy(&result[0] + 23, hash.begin(), 4); + std::string tmp = EncodeBase58(&result[0], &result[0] + result.size()); + return HexStr(result.begin(), result.end()) + "\n" + EncodeBase58(&result[0], &result[0] + result.size()); +} + +UniValue gensystemprivkey(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1) + throw runtime_error( + "base58encodehexstring \"hexstring\"\n" + ); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); + //Public key plus prefix + std::vectorvch = ParseHex("ef" + request.params[0].get_str()); + //Do two sha256 + uint256 hash; + CSHA256().Write(&vch[0], vch.size()).Finalize(hash.begin()); + CSHA256().Write(hash.begin(), 32).Finalize(hash.begin()); + //Get the first four bytes attached + std::vector result = ParseHex("ef" + request.params[0].get_str() + "00000000"); + memcpy(&result[0] + 34, hash.begin(), 4); + std::string tmp = EncodeBase58(&result[0], &result[0] + result.size()); + return HexStr(result.begin(), result.end()) + "\n" + EncodeBase58(&result[0], &result[0] + result.size()); +} + + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "getrawtransaction", &getrawtransaction, true, {"txid","verbose"} }, + { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","txtype"} }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} }, + { "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ + { "rawtransactions", "getfeeoftxid", &getfeeoftxid, true, { "txid" } }, + + { "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} }, + { "blockchain", "verifytxoutproof", &verifytxoutproof, true, {"proof"} }, + + + { "rawtransactions", "base58decodetohexstring", &base58decodetohexstring, true, {"hexstring"} }, + { "rawtransactions", "base58encodefromhexstring", &base58encodefromhexstring, true, {"hexstring"} }, + { "rawtransactions", "gensystemaddress", &gensystemaddress, true, {"hexstring"} }, + { "rawtransactions", "gensystemprivkey", &gensystemprivkey, true, { "hexstring" } }, +}; + +void RegisterRawTransactionRPCCommands(CRPCTable &t) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + t.appendCommand(commands[vcidx].name, &commands[vcidx]); +} diff --git a/.svn/wc.db b/.svn/wc.db index ce1684cba04aae0294dc5f3c69b4b4c81779653b..59265d5f02b255c1c3c4a8ef25ff46a0b406cd1b 100644 GIT binary patch delta 48108 zcma&P2YgknyTeLPzxmCh!VKrlv{hEEf4u1>jC0;BUub1i{U1YSCTGRxHL}uf{)~N9 z@#du|R%*u0vtS#AQBIHWD&rp?Jy0>$y5RpBi*>1(;&;9D8vkP=Om~LMQeuK2oa1Ni zPtUA1B|K(yEIfJi_;LS$f>6#kK5AyxyXo~mS0yYI2Jnn2>6tmBC*OcE^gB|Bd-?7)>FFhptgkN0-Ag+Bn7L)g;yw)8Zt5vjE2+QqBi6m-($bSq zv8M*BR>D{2l^7+f;qk*7Vzn*hB|&Hl4PcaIU*lJ8rD{(sKLn))qLx|mWFO7mY3EKQ@FfUti|i? zW_3z}8%I2`>7$aB8$En#Q!8)FADawOE|NxzyvjkTP-H!xcq6f8Ek8!jlwOL-#pFogI+%OY{#eBA z^A`pEK2O{eF7kTbB{y4)(pATe5O{Z>N}7 zl2*17iVe2Ouq9>N@dMh})jJ*sha3D=9}JXfs16;hTv2YnrOlB^V&fvO zCsO1J`U9?zH|mSH0m`vZA{6z;Lot6`9YtSkUnJ&M6%MB&T?iiv>`if*)Kd7cq)Kq7 zdfvFTINYox`-`HITR+uG^iOZC0MEUjcGioB<<&B}=mEZTYg%T>!xz#^PJcQa8dOOr z)k-e!%`2IHbTK?O=&9zHxQ|VR;-1zLVBxU>1U;5Z{yGlS;Lt(iJwOMam%}4r@MoVR zety^x@%#%9eSp+*`K!E=rYG0qD?n0yJgUI9o>~Xbn{e+Bo%#}rh{{`bd>KOOpkd5& z?-ET_D>-)#8|;~oo>_A8{9*hIh;qx0@8`e>fC5wl1Xt4Ff{pFwr&KSwaAA8AJ<5OT zu2vO>3c}D5{bHk%78hIbw|7fTw=B7MzYMLBeoJco_PNuV2m6|NC56Agh=btV^k4Ey zQvP@b8~Vh|k{y3^lQURHfoDt>vv@&_>6zkXsj<{gdO%tyeFEDd(EhY;qvxtpCY7&M z&&VxVboq@fS3gpjv*G{q^_E4KBW&|uOm-Mu9f5tr>PP``^Rf&!PY1!)*YN30dH*W_ zy@z#XmOOl1S+<)E(Tlsb&ib#fy1|SQqPz}cWl%}Cwmi~!16bM_LH9ndvrtEkg9EXYgT>;$~ngynS}f*q7Yc6kx&F3VL|rcjb` zxe;%kDb*U_ioi78(IR)qTT~Q}gxnFBc{t+tCPLw&NXVy7L6k~zm`KnQ0*2-FB?94~ zKOTsi|@*o?yrS8>l7@z#HT)=k**@-k{9ulh}uxnBk zR)wdvW;uvzM~vI_BJTE!nadlnbpCJ)rtsmd*%n^dnk`GN`&euCnX{x>yh=O9p^Tuh z%iFQTxNamW{!AAKH*no+x-yhe2m`yU8~eHvILG?1`Sd@FOGp+;9X*@IiGDw+4fIywv8^BHGKw(bwR_kn$+EruUY1o!72l3wsBT=kL(k;L$5NRVdT<;=ZV2T}d&hY8Vp5yf z2z5xQ1nc3{^ zWW8NJhrNc=LUn}CdW5}G(Zis5Y#)?S+2O8#v6rC?Q^+lSlzo&^(f;_`FiPq&VnWpO ziDm3`#mghhY4n2wdb_hHrX65mg3sOuTTEXTz9COFN#avZ5N*MiI7cEy6 zH#p@EVy+!s!R{AxJU)Ni7f-~ZVSkZ75OW7yE?+F+3I&RwGNL}f53gherO&WYuol|b z2cE5*GVi^L-G@s-eiU48wpZk*0M!7GAMPtY%M@Kxpxoz zo1xf2fIhKXwbgZOr;c+W3A(Z3T^@gC6Pp6{#G3ee*zZ1@3A+f`zhOnz7OHSK*%n%V za-nasJr%>HmNJy1gsVT6(sm(I*yT3chA4w-Dc@PnTJe25*uS8HCNg|yr9|O*yI66u z2fKV1%M=UZ{(wIkibaat-eBCFh$Q@ecPQ))ghL6hC*k(^;z56?NKFt$7T#$U+Q08% zM_K89=B=dt?g!ag=#@~r`XTm2as*y>i2Vp@AaVG8 z;=&Gl{(#-9!pB4gw;W|#Dh8-=ta1Rm{21%1*AWCom?nr1N%ycfm=B8ZKh_}2XbF}> zmFXZ2U4YC+aj4ky&$=GTx$%XkS!d~iVmHs;#B%t+Gt5^p*qdkABq$T<-SQPX2IUT) zvQGT_`(%@k_?n?IjJv;lsHj9i=KuyX4$$!c`&q0n_7v|IpBE3`tbB-9rCh1MG)sE9 zLJ?^$-H;aUV9@3D$J}mjktY=N27KXAAm$B+{a$apDDDfY_wZ5Qu>bIH&T84b@6&8^ zQZ;Rno+~P!+wiWGm7Zk{X=9M#W_<>zHS9;)9fI&wtY~wAEr59w9i6=J%7|C7yvdVnIZlRsHEE_Y7NFDclV!h?UY+Z`Kc;S*Y+C?2wR|HTfV z{ztv8Wa9HsrcR=wI1S3A#r!0S+pr9b57Z%X0ki12B9di3Oi{9^TXJKJbgE3&1e2%K3^Ih!06Yhfb-^SMzXssr@F=c8%INgf}o2Ey|E zJ%oD>x!fzxCR9R78!%Qx?Vsl88%I45hu1iWxIW`Wq*a{H@(JQO90>{V?+G*o+V8T7 zxJF{EQznV8U=u=;`v}8mC6mR~D3^%hw@!mC{`>*)VW^OReWZbz36N=y=nLI zO>Z7rFG@F>y&{rUMa}XyiB&7!@T1(w=PNhgH24*U-uTb-`l{N}sjhA1+K2z0>P?!h!@C-} zqI7@hJK}Y@V$Q+$#FWZ8&w5`}Dj&)|5XV*m`sgd^`TVg@iB-@hK7CaDuA+~!W8$+Y zkLiVd1eUt(6!A%d==Lwg2e4COa6Qk6uR*!NRWp;Pd__o0Ulo2$*h#qjD3iH7kkzWP zOc;E_E|d;Shk0Q?vA~Orr15>D!tTc5KqBPxhupDf#2XD1`4dI)K*%3X1VZ2#io1M% zm(LxDcw_2Bd(k)Il=Kq$a7|t-X4T?3t3(sw*f<;ugk8}l#bX$L$LrN7IhZ}441 zFe7T76La}_kzNw1mq`+sGvA1HO0qB3;z3!u(-D%rt}BIn{~&g%z~y&&yulC{nUP>P z90W5n5(^gvLSRDsB3@9mgQ2i1tVW1A@0ubN@^=Qa5BcDMpcZ?i(X3?YwL<>x5ax3_ zF7&8{s@RTNum4^x5*J=VY zxspLkA@kwgD)a$C>r(@y;Q6Y~I;W_ayu6y!-LWmwa-=5g6JD4pE#{wG7YiIMXXa`- zS7cAvtum$W#nP3An#ARox3M0f{9g34)Ul zlPl@7di4+&Kn8ua}h&MJD++#L4bOR{iVY3IJ^jqh)KTeUBJOQRm+ zW*+~suY>~4k#x68CUQuUE}svip2=K(svPF@#~^A+-Rl1Warfh3=TkK1%7;i6@g~Hj zFGfgbpc~>ZN5a$%ZTf*oW!Wx|N)Btr559~^s5=q$z7UtzLiv&b^ffdgp~*%vI%TBv zG?e$$1?9JVq-3I61RrGbFDBDyw4@{V(+D*3fho=c{?yJJueOpmJvW$8F zxMuRVms1bUR&i-s1;TZIiq;JbH;-3eDLo6{65$?L>3mBn^V8B*AZ$XeL(dV+NC92= zyo9O*fuQ#b*bnWv@}h)(S7&culxkOC(fM`KQ&3Mk8Jq_@x$|Y|(PX!S*Gu5saNec9 z;((k0@(K;@?9ri(5>Ix_@82YCN`BjFv$Ku3{J%HTlxQc)w@6(K5RS5NKoAaa^9`v# zn)sc0zf(#rUjC+(&OdlVO5r7MNJVn-9doMvmlr)?JaX4ud_(HYSHCGOte8OiQU_S* z;#6z`A^~EjEp@G11K5d>fLKE^)F8A<+I*LsP(y7F{N8LB^2GxQ@O5}Yo{&G3aC_Ze zU&!l;$9#!!BpM8;57~*W(i*KXFukq9!@@;ynT`O5>g5CXNv%|=8@>IhEq)gR=xyTpwNkyM`=yDDAK%CF?Wf+7x}f4n zg2?|K;AYnQ(%n!&f^hZw(f|Y@dPS5E9K0Zu`ap6NXJk@`K9SmU@u>7F!WMV&lQb=l zpF2v@5c>rqUitwkkI=6CKhlNd*6;jQLIb9Mv0UBW`JMD5@&elX(?3Wp`Gp^mNC+o) zT%d+zNtFF0{SEDCPKPfNxlnoQY2cMVUXu2}OQHe30*sP#KRZI1*v&7}Jvan$j8<2O z&5}f({u``2cvV_c(eK5p($-|zF8@;+h@6oomjAbOu%eN2O+v3bF{!iHB(y}4|JXaO zOWkD9QS#FM8IiD;%JO*05p~zWVI~S2dFxEMpj4MhizV3AH00l)BP!>ZG6@@m{>Uz= zVAfl5g)?e-ifkwg!4Ll%Pn)dQ6@@_%b!x#|_DnUKfp0w=hfrVCUF7k{L8&T=`9kiZ zM9fp<4|zbr3cA3v9rCLW*n3jsG}bg4^ZBBQqF5*d4*4QaFy`~S<33j+ROF4hJz$IZ z0zq(7sB`)LRJlEWDn%~h@-;2rZk{GT&q_07Rn{xi-jcK1PsM^%$4v5gONzs6^i=RF??1{Igk@cM52+X%k6W=JV2XKS2*VPC47FL zt0?3RsFV4+ngH`GIS)~Rn8PjgWZXQ8l4R7EQE4Mgd$KAp2R&Oxxrw`PmuJg&BPtLN zY}r758_FbR%!cw?P$sm?ssdk`jpSAM666$E{c`!oNWZ+=8-!CUl9P2u+z%1whv(T?8yf8S@~0#(kX7K%z=zNbJw zksk|$JfH+6q8@)J9Ct?{pzJI1gNYppdXa~PyZ#O$RlWKAq}$qk8s(c-Sz1Qs}~ z4WS~zFSRW}!XbpV4lK!K)K2b#;38zM-a&q^VzIw>kkQjY?BHZa8QoI^((+Dn_mmn! zND$(}Qt&~agOE&(PD1Fv7)%x67l^J{%*YPA2S`^xXYbR0o@t9%D;+?gO~Sh;t|2a;8;XVEMe05FqM`C^R7eSmfvVtV=rvkKK}xvt z`)Gnau{7;^dLF-gkGv6HI+%B_d>3s127>a&(u!%;<~aE`RzSXMx}x{?B5eBFg}xiHc5UT>d#D4K(V?Hmdxeu;+?F>9@1TWMdIoM@^yGk zQ>-3wNP#QxLzFRmwv6TyA;_I`B(ph-h(zU47OG*VM*&zF;qqD+2nzPt%2h1! zJMC7Df6E|72;V+^7CJCql2^mKJzxykJ6@8>*GThO5d@`r;LEhF0P0LGuXi?O4-EBG zYb}@mwO&T~>Wue_JQ>Gxzb1c0h_+%!gk9F$-911k-$J?u?+p9K9v_6PDP+7)#>EtBZ~ z`1w?DQ6V4tPfRPsQn}T%MEumm^J2ufWWW(e{r0 zauHkpjx$SSRd>1Me4cucpiW={Mg|p!2&Wqu5Eb8b$XN$DaNb3}J2|C(NAa)_6k`9A z?I2<~LEi{SRu701uMaFp@K1Unrc&f``Qu@CQ3wQ&I@^vNmS2zxyMV67JVF;?vhX64 zgpXMfi$HK=FGLR-iUY*i;%4!TR7IK~wU?fgjx6nv$-E`AK4^~WpReSDkEqbDbA zRQ2{dSpEfr7~CV!;#R&g#PE%r-Ao$9N)|kmYh*(GU~1Q)sgi3lOokXq&(F;=a$89Y z;CaT4h5Y}=BcS)j^!?0Vz;`m?h%ECtAIWZh`$uw1-smIQrHb=$biO-{NW|y9obs{6w{py}0Wr8~R>YPk*M${k1oCnTHoDsakl$&Mm z_+H+KKD-YHYK{44mtddv{VWfH3iueJWWUG|RB^lvzsTQ1HBIF7uQGN(8vlmhyO`Mu-B;CF8@QG17Z=_T6I4CN4W`~@wYq|`$Aco^&{}P&^4la;(@RH zB{${i*PRN|3a-ly7+bXLlv08VzKMJN-&(v4mh93kIXoX4_S%yTkNnB~HwB^k& zr>>WNm9E+s8!IA96V$mQ0rL~|-_fPH%Jb>)CBf#ZSIOmH_>>9Q6pvim$KP1w!ngU=igp^01g2emITa=FcU@HYJK9aU6DY7lMR!B@iMJAu##@Plm z^0M11$jIr-pV}heunauBof20?ZI0czs{(dSa~(aH zUJ9iE(c(|`Q!tE26W!2X!C(xbfAIiiG?Yo*xi*06seSovO3$j8T_!9R_6dKmW^6dH z*|&IFK}vf^qCL1?_1M3^nqo@MbGVAyk~QTEZdbge1C`E%MwrbqaF~Ls0b$CqcR}0g zGh9J!iz+S;r#*2pCGJ+b<&&PbSNJ_S-~o13EChFVSX?F^6#tgo!XhaSDbVkcafSi4 zX5MhHDC!D=B?ckiU?AvrM*|^9j*5B%p>Q zV@f@~{ZZu}=ogc1dFU|(ISRg2w>U-$!mJN-KUXHfya4+!X$C+zVYxC6CyiRguTKEDYICKY)O~J(k9k39zz;4{ zZpC$AAz)e*~-&ZR39Sx#4kh#(e;*V!G?Oepdy8A^4LwdD13wV+%) ze$m}n`WFs!TSk+3A??1uRjqB#NwV<1Qr#Kh1{WN)KdhJkU%&b2+5rUCED`) zyL(I;Gy4AN;c-IASg6jOt-4@C#!1_=pRvFK+*p#?jZXG zCGy*CQ%g&y-5;MeIy`P93^Hn@jvCaNy=KgvH-EgRK2-`l6xyps68@QPws1?;jMiMZ>d**FS0Jqz7Bd(xz(W5_u}qzN8PBy^_3iR4&R0a5?YEI!q}f#8 zjtrhm8-9K}_2#JbWlA=PgTegN9%Y}Z4a`-HUi`tmN)CT}uks792D^Np(v|oON|hRK zDPQ1N1dCfy4V=#gB`$GzOamXThS3&Nuu?cJ%dzd{6 zSzMrwK9e5ccQ0VWmCF+R!=Jk>XYr4YD3I#DLeTlIN0iQ)Y@ML%qBvSp3;}-c0sf}j zuYRc9L$*{O5bMo9RnXZ$jtBjyf+jgl=j>4ero&5*DG}T!VjQ1bQ1bcUlgdh{Ak#}f zrMy*99`FU=jQ*wa9JVGl{G~G5z_%U{wXM!Mq852)>Un}Jv8rRAfevDRub{h%m~8p? z3Z!Lp1|(=cRBpis%@tGlyFV!Tj@UdgLv3m2-JzUe{OFbRTt5GHC57AhY5BbHE@eu- z-|fRZrkD%zWFZSS5(v5i(S#@B^11w8U`t>)xm|AcA->`+WsEIfRO;bYQlg6fCBzhl zT~gLU1)*<^p9u<-2K@ccw4qoJnMew`Wk?Pk{S6k~`w9srKsdCAKgqxRc zm7WMff>>>)q6kEDpKtpW)?Qas(W)mBs)@Z_5!DS);e0Bo=-L<~)np@h-Jx<@J|t5y zd5zzcd_^R$+N%Gg7urL5WUNh|-YEP$y--!_(YuV_Ab`6-SJU#rmjjN=c*KjDU#>W~ ze!v|Y2uDM%2!tPf-UL9*j=1T&9R2B6lm<$Q=KvflKn^?t{RaJCE z!;XQ+|s&g2;oh=h3ip0m96@a*hs2C zCV!{CdfMp#K%K4XB0LbNk2g@i!ZPa6?>AHd)Hp+MZ>njCWi+i*1?s8f$NWeO6^V*4 z`f#Co!lA*J%3G?q8B~45OI?z>a=@p43-zE`XL9u_L=evURb(j+QU=uSdUbI6fLAb% zLEieJu#^|9NzLMk!KvB2ZAeY)9f2bokOUAZ3d5NXzc&KTZwQ+QB2j+~&Tyy?qg+qw z00Gbef>Ez4?hXVYy&KX5qOL$Bh-rCINIXzy+B<@(A5|$*ZnQS)&rqg$rnXhdkA+}J zZ>ORON3dhl!=JndGXHvvB^scHDdSY)|0oOee}S9H8n2>H z5PP@F$EyW66z$SqlX3Tn(_Xk=MN0!*UEjfvLrYUtatG6_j@@nM^ET7f&G;?4C#4y< z7Q*?jXQ*!_yM$b6wIB75L0gDeg(_p7M;nh+3J35i3xtdCShuGXlm4B`5d5K z#F>(m!wqTJh=l~Ao)~1C1&VxrZ#Wc$K#2!V3;7~(^X zBBoBT_pVXfOSR7msSsWSe})Uh)CzV4qKg~FX~I^qJ+He?T_uWD?ecZ%JSMU``ED?G zz+uF)i9h|2D;9PXUW9Kcis(cY{QjsX><_}Iu2{S%;fq9yf}Wy;9}YLb$+HA-^&p%j zi-pwD@ZALJ2W=^y{R+_ac)sU^QG^!h1AOQ!s$Z1~sKuAxg4lTb6?FiT2;pwwYlKyV zH=|Mkt6IFSu7#IGGo$VRS&+6tB{@eJy+Q4VpVG$WY@%H!7Jg+DkpSV;XPb%jIV7+} z9f(JGFO^vrN4c2Ox~k-QjEzw!gXq>CmCEsc!;l=(`*u zMfxw_rS{AnA_zm0W@mGDH!Fc;uRq0>VpQbfyW%y;BPA*=KoX|%E$R98uezF&v_xN) zbjZfJLU}q%Ru(e3biaxk5z*1$x75AKqU(SPmJrn*OV{&xy|>lJlOO@5NbQfyC06ju zQJ7};_eiV}e6PJn>n9+ceE%js@qyYsr9{5$<-x{g3h#VE&2b#6!#iSlasxkmLJiox zPO6I;A2~0*E^m8MP2~qqsM+@Kr&KeUk2bR;C0CWp$!a>&I3rh7dz7A5DKC!Xc)=Nx zxWv`8uN<-Um3jkn8w;2k7k*gO7E&)`P{H8vfhA)x=E= z(SJbo+1J&_pm#W-%6D8>yO|Q(DzL3md+~u~Y1%1Sd_Xw7Y!fJGeYj%q6sE1wp-M;F ziMN)tG1v_{=E@}v?1c978vdg(j!sh>!?Z4ZmZ*({3exeeh+3yoS!)2M_z=EH*6P~r z6fF~;hVcm=Na0(cYNmaksx4(OqYZ-iu+))l;Ay(HHbp!jJZv-od+Bbm0sqL^H9 zkBg2UI{^-A$9cA;HL`%%`(znyz=oX3pHEGz&e!T1$mDUwedgu;uX>mgp=32f%i(7X zZ6Q`PfMCEJQ|n|Ex0=^D%V+_X;=O#LSEXo@zv1srnqx!t}<*b@x-qJeP44eoh}b^0Lq>54?dK~P4$;kY`MSX8e+ zq(VC{Ra3~Fa-^GHXb-Kbtz#*o*k6!};6LXvL!}vJgyi5ZuuV@YaQg`3G;ma z&vtM`+wn6)(>*vo`kX&*qIKh`dD;S~xYQAXuDkQJ?=*B!9ZA;;`LUK7=z&ht<1X#f zit;BdKmuHKYtL6y?DkZ?w6_;&QAN~7Imnt8(o%T4kk&xcM(3$Ut#*8INUP5u32Ey^ z?H=xKrTr>ufIb%hu`h-+#cqF#wqFLs?jcCE#9QIySjND#fsVIn*aEGIy|}$L2=|5N zpW0CapEoiiFb@ha@YYd7fsZfk?VYp>60U`H&Ev~ib!3h^9U2j|gt!x6J1a5C&PJIgLYyinln7qC8j z;xp;4RI+-}^1aFW9jYZ{_9$4tCx&WuWi|~;e?ch)cwCk)O3moq++mtm23MNS%ZF*b zWvM;b#3{oy$ZZCK(e2K6YgI&DyQhI-bkIfKwv|?wPY-MBRPEj-iqV5#55u{bt`Y4n zQ490c5$!*sb~i8X37&%qQP}ELQSIK!tt^Za)JSDe5)M6J*yKz?LkrLm`y;i+q*_nV z&S4$W&c%ru(h-OTNE4i-9f30Ln7!*hjdHAMUoTG9P@^LuBu@d&^UD3&t587*@a~jK z1hBVH)q2vD(7lm0oi-2h?lbx2=`;*+>^GhRGB##vt18ASo2i+&leBH$KTO@7nE-h9 zMX`qJKN0o^v$X^G0n**}Ia<%$3}LSTzG!rP9{`Uu_@c##xLiCW{w?g4+;}WkIxGz2 zIRz<&4x98|g?#Y&bG6`2StUVlQ7{OZ36RnTr<2@KzdwfQL{WDj8j8bd_z2uZ5R8Wd z>KtO5n=&&B?fgfyiRjfp)aAP#)jp}1*4D?gGZp2X^BqhiQg~deQMpl97dRV*>-cJ+ zcCezO4;Rs<;ri^Ii?xU)YGZiAr?tmLZLGcFX|1rbhbc>eeKdRa2K%`9EOkly`PnKW zO{bgHT6Z}sCJ5!iSvc|=1JoY_2)|!g!Y(GS5qQWsw?!w&9c$6+hqPf&Ean35MI;LO zRI!*p5Qp49(2TvlxJSKVSbE{61-NCw2Zw~=t_EKeZf9^u!jX8w6LZIdv9S8!O>I4{ zkPB4lKm=}h2!TxUCA>vpf4V#14!hz}Pe`3kI#q{3V8wSmAgA%S3L*Brc)y&=Z#y7w z;?M4vbNJ;0a*TJ`FE{5y-j>%o>f6jY7`7b9JFL|Uh`|w~GPi4QK|PA`lK2~7rj;SjN~&aKS{I2$ zd51$l>m#_P*-dt7-{aN-aiKYh4n6G;Fc+KerLRE0&*UffIwXdcU)esb7gt-0t?h9` z)4EEH!HN(0B9Q(cj72fstO!z>z&GHB4TJECO5txl3=UiHkM2$@EPY3lFdXJo zy6ee+*kEp61_iHSK}rExw#^c*ggcne zN1bjjx~x4`sb)9-n=l7*Zcw6;x_yE7f(%WuY{w0z*f64H=#YF{=8;)X6adSk22U9dbujq*Y^Y^Q^662kz>b zD%-T0Pw;d!xSxV?NVH4@;-Nqw9tj5__bZ%GA9PEjjgs|G z7us(!@B}v~_t69l0ePYCKhgsvUn$O?{}5B%Kxwu%4*MYN#U_ zLRAHa^?*AR6zXfB;fIAZ0Fi!qOT9b0gDWn5F6+maT$Y>Pv+tieWVf$Ez)=43`zTG>2p?q^ric0R^ty$%k<&}5f8QV-;2IMtEB*8xwemUq(oaATU>A9v2+_xx@)w--&5CrMd&@l1V4 z2N&iF>xCmAUv<`ywSw!WvbyVi>(^6^P=#{da?JRVE#dm4$#J;07@x&Fpz_+8u{ z+(Um5Y>9FF^Im$h+$zEv0sj4z8LSPYu)n}Q7Bw*_juD>~-`x6Kz!J~cahpzdJnee( zI|#~f4g{%%bezD05KK4ka<&M4I%Rk1$i|4t+>4{WbbRb$uG@^v#$ifinl9Z!>f$dL05_KRb%x=N;B1H(1t%aRWw#mJocWkdTq9Rqb`FD+E0)gi2cBcC-RI5`p+p)F$)ayT4b2_Kcv^-`4jb`qMNMq zii@~Lj~hLCy0lYJ!sE8y_GZ^)3|L?2_cycn3lsHCeD*{=3UDLs(wd}qphSFHmz8 zp_oB#*%aYT;cJ%8LM#F%&xc}l*!5qBcBsxfJ*u|_l}K#JH$87O z=10CaQ}FRMzVt`4rhVj5JzJCnP#$R_*T?DU_%@TU!Mgw6!pZkZHYCy2=Ivg9udlvl zSoXrl^<~J?-1Pur;}_~*Rsw4g0SoKxU5oX5fjk^2PFYq7MZR)5l?mLtxZZ`!e=#^i z{i+s#+W;T~c>W5#qrGB<-dg0fKEn{*3O$v7yi$Lw^cg)Lhuzc4U++3$sodXTr?tfP7ZnAeLp-U4sQ{g!E8+M+inaN&zl+w^bX z1%c?P?fPd>?%xbyTBdiATUQrG3Br@Y?th%mh3g!IQI#ijuSiWQE^(;1#$k}?XyZ+- zT#xL(p~go+sf3{-i+ZjJ7Mvg)q&&;|c`R3H2P*njUNYA# z;2-VRpOl?9=`7dq!5cD2__eiSt_d2Ez?_brZwjYep4+d>eBWET%WiZ)UxZAOsPylH z`VI$@$-5v<9n#Sk0Z{8>?|xVBkDEZ~I{ZByqJTD_NdavPfZN{of!<37VY5(xOw=@aF)z64;Qp9sI2`i&)yejrPxQX1A`%u1`Aq*B%gAl!e@v3;niAj2Lp} zrbb^mWfL%B``7v%XtL1PLoPWlfUEJxf5vsv2==yL^gFR90>IEK&I=muYQ+Ztgg+hm z_hCJ=Hy`mg+>HQGy5>MBT-$`9uIry5$Vk0JY~WeNo$-U?STy5S@jFj17y?TsnMPAEIjpKAOn^_IHwej`#_*%zl7gGA%h ze28$0C+3UxfSJaxhzqdIefY8m;k3c_Vq+Dq5up*E3#!VyvyB(A0?~2%9K)@N+6><%^B=m6-79ECB51 zOO0hX21r)E>s_NQzxsqR4J(KSN{m}&>8!ZX&RBsvGL1j;lyOQFf_U~KprjBn_8E@{ zIOhT@m-*pz*=y%KZ3M++7hJnl&F6=obq2wey|&6h23pi=qo-^lZ^qL|!a|<06&+pZ z0;CIGqH7F)ei3A%g2yQi*$;7V*b51$a3fJLobdZ1;LU=A@o+xS=Tm3$!%yp8ClRRr z9Wq1_9x|Y!bU6B>uNiMuOzo-H4fF-lO1Et=o~i`Qs!i00RFRFFX+xbo z*kT;RU8ltKLtBk~6=i!JK=Q&i17qs+vc}6$aejMcg}t-PSVogT5?sB6p{lSOlv}9=>kYS^{Q!sB&WqK= z_TXRP;sNn{VK-X?=XUci8!J!+evp6u2aw$P%f=Q>sw!v=rK&vmr;)8{li+PNzTs89 zPDh_t{HH$i+pM2 z*Nu4y6yhd}6%(EMgp@C<=0PYsNTZqa5ym9{k20`@%MG(q3teNH=#qym1FmYBU1W9} zS2D~+dDEmvr6u5Ve@@y6YVB?~<$XjtEuEDvRs>FN3ZK{?WtjhFqBMcesA~S6DLssy z5cv_u6C%%(AeWHOt!6fsrQ=X~wVH{?X&_*7qMA7!=TBP4$2;LByMwh%h&S7?vyMO_ zOsZp6MMyxaU*Lx{kk9Iy#FYpYv(28MFB}Gca?atjR2Ji&#-`_{!?%!!;`YTnaEdtU z^SZ+UxR5yF55eW0a7R-D{H*F&dr4z+Gm0h(S&V97qJd6^q_5Cii(_K|+^@2_>6FM)rb=V1JsI?ieeJ7EOO z1=u^q=K|(vR97JI*o61!Vxopc<*cqIM(4;6@k3V=DGVJ$c6m1w)BEP8RY@Ol^X;Xm zrdEh&eotTX?VHq8xHT^k54qf~L^v1wRa3ODXxs+R-QkYdZw`9JG(*QuQ|kAic6=<9fz3Ra?)77 zfH$|oy{-8-P61Zj+v=>`=Y)p><0KznF-BT))AHS*hQs~S5xCA3Zs`ey;6~+8*az!P z1mRv~I1cA_7saB%ggV<5Wl!NrO+;$WcFY%tEeC~hgn61=`gD??7Tf| zvUxXBJ9QlU74*HPQ;?((=}?$a|J$dU-No9`EC@h1sBp+R5qfmjX(M?9H{{j1W|-TE z?hpaeW|^o7Qnx?NGEv;XI&%5@51Qy*1-i+FlM^PL*d>{iS8TopF9?IL7Mqa#<9vK% zw)qy6A^ZYY%FZ$IN}>`-;erdN&hx5s&4%1HSJioNt~rvwJJ-C6zm=N-mq~RdYqD-9 zGuLkah`A1#21&aIA2Yk~4v(7&tf(th=k4a3ok|y&;~0X^ZgOfdgH-(43p1*ynLWGCW65ru$yocFv=74z{w4D1V8+e;pIbX zT3&nbljdrc3~)ho--OG!dI(cs8%K%k1wLbi`7Tn($9&2Pb5wGYB}cb3&pG%K2Y`YbH4^?Gxz(-x5dHwWTgCLNoG z@Fz+F+F|*16LTAh8=iRG+*a9kgLwhC-Z&>gVaX=5hW+U#({I6hik=2v2N%x39cD>I zM;SY*BYJsuXC)TcJ9n9FDnXyKhj55SPT6asc|_$;x0y}3xzBt&Sz&MAXEr8zffy+r zG`GVGLa?t7niy4}uU@o3p}cwsfa{ba9+$W}sXCE`C zKm`G=?3mf3#(*1%`28Rif0UDk&+8vI`}2tzrnk;+kgLc4n`Es_%s*}}?mvMf>a$6S zx(cqYMhUw*`TSh+_xa@S3-G5L@coO)=QYXSwaMRqi>K|)pP7)YCv63R{LSa)Cz;@` z#81v9o17y7efSI0gDik3I_o`K9#Sb962TqQfZG- zKh%axBlCVUgP`A&{(avS;%^YKLRFpggTJquuT*Gef0~67NRRKYnYEBlKP<3^;rvZY zsU;BjToLa2ef5Nuz3ffv1tjtdds?Xb13Sg+!H-)Z)R~nnB_8X`C$zD0 z_)KQi;17vb7i@#1a89()z{K`;xn$8*cNEF!tyt*LpjhiW`@tA!t6E5##5cL3d6@))i;0uT0xK;!%zfh+Vyj@=b z>&~lUofI3V3EKchau!_W9u_XKrkEfMviK}Gz1u_ZZ5v)d1W+vz#eefF%S^9*DbpIx z_~lJ%K3v{Ct%mgzzvU%3HU+x>0P4H&E2GeqUII=0cY6L{p;;(O7fQ3NmLym(@9d6x z)&^KIF|KCytv3;hxE)7p!Ih$QvMo^C(5SY{vaO|X_IaYC2exixrAni;NzT*yMwX>u zWa~as2yJ+N7?~bLpV~FC$QsA6UUsg9tc1l0GgJgXrFEg*s6P97|@W8}zEiDMv3ReWlC(d9t6d*;e0E}DS&SjPH)-Ef8UHf5}%`OYM6{H@c3xW)b zB9GOD`-`k`*frklu&u~~AQL>yhvSR&d=|2P!m&Sngk#S5{+q@RSl!x5Uki2U?;{8g z3acPJ<+yMK;!?MOruGm7C(7ATi2O7Y2a0ncSn|I3gQQFO5X+8Bvms=%U;3K24_bBQ z3LB{w!EA#4bkK@|4L1=szaDS@r%|)2HVG9hQM^-ttkXM%XN6~B-xtbI{D42-+CqWJ zK0z8e$H%t;GDuE6qpj7AcW7sg#C2hat-PJJkfdZz8=&gxovas=vZx6^+5rN6qq|s- zLR;GWnq4jAIkfJJT^(}5Xk1x0t2F@%v87&*n*i0r0TkAs?pXmS(2japjX-uzw$Jpo zra<9-M~JrVYt^vl^tA>;)f6X5uwQR01+ZvpX+J9iO=w`DN8pFHK^<&W%EvDT6WZX^ z?6M)2h|{5Dm#jN5S~<)@6M`oG*)ZyXc<~3rtqNHQ&NgSascTQzdKE{*G${Q~$mwbx zAuv#vpG7P*La?E|GisHPD%$jZ(Cv#ySuL5)H;n?K1HFH=br~|`wy`I~AyPJM&&zPV z*<%l(D%^ng9&aIEBavM;9x!kCIBNn_5OzL!4@}w^4~(w;e8!)tHaDwU4&wi7Hr?>oV&BLbn`JHgt7n^b?IwOy>L-R~qI zO%>Qw@pdUsSOqIuB8rc2_hPFtf4q@p@DCQ>}I|X##%PR9JP3e_5!}kr`O_FRKecXuj?k3;q6ODg1$XR#*H0H{$F(t4md! zgbeGF+KLae7ol(nrqRKeVbp=#=0Om0IX&HK|Bo!*ycsaP{F%USz_zEmtY=!Cc+VNu zBT#|K7ktnNP{7TZR(+|}{1Ebr9Nua=O_U_Z!s%8wtGMm_U=~FA>lowld~FVYXc{0r zbx@6eFwN>#`hfKwWgNSftA+O8#g-;QwD)!aVAX`p5!S&pSJ><3TCXvw&wPWgss|QR zzs0b`S01rCLPvymRp(iqq`T&uyxnZrbArH;xmI_0O}N|k5vxn-qtk5OXpi0 z9`5FQo`fu&ZI4^MK)}urq!tiioiT zSzUSIV(VUbN!swP#nvzs0;J%$mRSd(Ou{{NxrII_l7ZhXClbfh5qmqgJ|IJP=^CTZ z-nP=}h4TIhL2`@3;O9+VrNCOp*70r6SZ`Ow>C`fM0D=^2Vl6?ap26RI2Dkw%75EZw z_bd?^jZtlt)scHv6TXxDE?aH&Xq>@132-a#&6l`4xAHnMKl%$YcK&l#9r#lfHKh$s zM&0!$8M&(3mGs%`E#Usv*mKs&oBSg|KOEwNd&pvN`xM0d5^yh!&ySai!XE?i#r$q{ zD)@)24W(-=23JMABS3cJ_Jkla5;@2Wk5nxzT|>Lh4xZ+xJht?G>UJ0CcR+QBCLIr6(flYAQ`_OJ+599Y*W3d_PpcnT7>F>9ei<&l~g=#cu$NMkW>hJ}) z0jn#5lbBiddq}9Hm;d!1(J3WdZQTkYu6Q^Q@IcTJ!sQ;weS`CwkZcUe znr=9L7mTQ5mLCV5QMWf9x4t0v!_p&=XkYd@$S~jtV+E;^*cpD1DL&2KvcUjwqr@i6 z8F>y88k(K57A0rOPoAL z2+*^cRLv*uPp`@QowmBM0gi}$<+KI4eWytgBAE*nUG(7mqAa61{4t6K6QO)zP7p<#kb7&{j)!re|yI21dWJkwfTxPW#})H>%CYm&->b{m*Sug7sRoQ+bQ2z zwb17Rp8!|R;itc~s_~)!v2Kxz2h4Bw&$_PA=*%}D>wieMAT{a#Ywb$lqo|Vp^vq-? z$@Il3i5v;{&D@g^K*AaBQw0q|CNl$s`#uz66cr%^ym)vaD2RBhx~>_N1i_0{L0M5j zMG##;M9|%pLsysYU)3E5Zh-lIzuklG^mNy|>Q&Wyuj*e%Tr=^2cLe7j#nX<*Tnq4k ztGD%-tB-1fbFk&81-9>2TtsGjxia0l&~l7YQ>CTM;-2wa@D}tyAX)7C&Q(O;Q`kfP zaR~ogzjsZ<16F$HzBi11a!1{I0*VB2{0CPAljq)e?4;|M6W_8W zz4sQZrk0*|JxC9Tfcp$DnwSyKoiVI7cH=thA_O91Iq~T~z@ZC&cil@5$S-ihUGN3$ z_8-?Hcwp@K{)ZUB-0pYf6U(#vUk8UwOFJ&=oI_@#kk@}sc^2(eaNK;qRJber(8QK1 z4js{)Vu?$>9zS6Ry=A#9XOP^&2_P*U534e5<53#{k}W?@6=bNmsrBSjxJQ526%%U# zL`&3{H{bziv=W8a$fUcNUITdx>rGnv?`kCf9nW8GET6@N)nr8zd3p*xBimy{Gr6T0 z-$q`A2RN5%C7QIAX<15ud}3!?`7rKLA{;#1$=?vw^&XjQR(|&ZuY4EoSwREv_Bt`i zC)4r;KejCQ%Wu=1;-1?dkUzyeOOkn+I9&KHD3cA#KURd~-Ug3;X0c7S&Xzult66fM z1khfoF~zKP5|UTW6t`!~H>8xLFPn7fvq46It!%TG6$!Zsh~q`4*j6If7F#;Y`BV(8 z>{{o@qyV^XUF17e-17=V^q2X7!|0m;gs0v`=RoO^fg5Y?;a)}ta`q8IJbtH z9j_Q6zl#s}RCjy{zz!-$%lpm0H{T?m$2W9T1c+)mz$%A0Yu+G;*bZU&A^H^@AdZK5 z=#3Q-V$Qe<2x=7jC&)kJC7#`1Pn18as=~n{9tE0v@rp^ZpL!Z~{uUSD9|90*iu_g8 zyQikgyrjjt6Jgf!ARc?-jYbQ&eH1;pdZGL#-sg_`Y@tjc*EBW=o0i+tNeKV&+fsfX zi|@MP(7ntWSeqWh&op1+70YFE7RGEs#XCGi1X&F{ct85G?P_@w(V^pmqpP_va09%_ zh1`WJM5|(Dy?lUPBGiuG*K;k<^15RFeW2iaN`4$aGBkJPQ!>)QWF-T>e}k-vwj1OV z)r_b@oS=l@F%ZRRlMhg@aHtAp6-FXJvk3*T9Sk(m+>P>fo|?oN^)}0IQN3wse%LJU zp?i|Kd!D7CG#rL2o|F3v#}7$OMe~u$!@Q3Y2bx6yo@Mg4$@B$j$+KJJuj!t)Cr-S^ zy~R>t_v`W(Ce`@Xt#St%UqmB?7kM)+npvA}EWUe5w1#}bndh`?X zS@Hll91PqoZ!|kN=|g!3h_D*@;zJ^0$f%FxfyA1WN2%eb%r7hffBlqumMi@AXYyaE zE{L3rSMHPh+SPjGp?p8!5spi^75V{rr1+_;mf8{lD%dCr`C*oN-N6W4s{$eT+*5kH zh&zM~pwroT?288^EU*vAL39e&)^UV-lyAR3!rj2)d;d}HXoJg+$(^Z7xEoSWa<4PT zojJ)}!dMQUpW?1EKKM}{Ods$|jZgFNG1b$jjj2i8bnpzf*_eK3 zsd>Mo%*XxVR3!6hVpV7_W(S4(J_Kw((ZHsVRAk(cN}JNF2~?2X@}5O1vldw1gD=`C z(+ZR3ky37DDEaNgt{R$@WjwZ1YN z_pBYA)3EvYLw$oh^;5+)3InyY6z#`3I$)d7Jhh9ZDMju5S%GLUjI z5ZK*A`I6`pnTnoDo?VAesZh5|4egfgbdWkyaT`0gk?ceb;0uPcfZ?pPfrB2fVSH{N zawCi>3s!V?lpq;`fHlFcZ#3-1kxRhsSR~H%R%W*bayJg+vjQQ1$m5~78YBh@B59=; zfzEgcA1j>K2Oaa{V^jMCfi^x9H5 zf7q`noq&Ip(p%!&3zUHn=qzS3qC6}t&mX!8wA;j#jWh-%;GL*uquau z*i`X^a$(l)S;qs(vSGqq5Eh@WS8l~eEGch$Qkg~+$pkt7r1C88x!sjdDgA_Fi>+Qg z%GcMA^Y!hvUU2Ug?hQ&%Z2Q?7ZHDm6?1K7e0^k)$Vrkhd_VWGo991dfSf z(OK(_<5F2 zEC*?ay<94-MC04`DiiIZR>U?eK7YURrL{rrPL7mTx!8Px>}cQ0fs>~4OvJ^s6My?m zy(V>bbk=OgMm(G(?t2ccn7&p?5<9+Bx&wW}VuK?ZvI4#nmk%hn@FLkErhcW|D2l#P zmY~*bGR!#0a*B2!=tR~xeIo^4F z0vSFBYJ8HRGjOmML;`XE6#)#$k8r_AtRPT;>;^uBo_di(ArcD%P%O)FJJ0@`&j5$4 zS>M#$%sz0SorU!MsFqoe1P zQN!)6SE~4_)k+-w*;}l{YTn*#B|>QInPm&{Z{xB#__t9R4w&o>%ckJpdS!qGk|vam z#eMxUfZ*F}m)(GW)5;?Fw^rHE_^Mx&=OLjCvv;VZk}<%Ops`hb72QdJg~zPw4)ZZi zV^uy<<^If1QU5|eq^cj`0{d?#F2;h+rQX4!f>r>5|3LUGP}CLXuOeMl=@^a!CBIXl zq7AKrg}Q32sYzsYPg5_fC$33X$&TX{!Lf9%4L%+eKXa|xfNVPM;R%_lTRi`?lq&L@ zqI9!Op&j7$v}P*JFk=!nQwNbL#O7+_mMYmy+?pdTxj^HkRw{?*GU2uzRFaSMgU~vv z@8F(Q)^{DL>eNN?icTsyvKoZ;xz+pWeG1%M>`}?aRWeoi+aJ8#B3jACD}Cxf zZUu?UMxD8NY#GhljgCvp;kuIn9k0w$`?DEGM$Wa}xWAd)nQrQzL7HYa4g=MHQe9Xy zJO4#Xpj`D~`jv#+>K-b|BNo#|JyjCZyqNE$+PMML^$mKfq^fwL?LUk z6>scveka!L0&L4OD^eTB-`?%)Vl5x1GAac#R7Qac1D4r*byy|)9o7l#cfMz~sVr27 zkOJY3&6voqlSo@##IN%@|B!i)A&14Ja)3IiaN++Qdz1ZUs850bi}mv}m;ev+!I>BC zNEqS)x-cSqYkb%J|bS& zCe*y-(H6^SxY_Rz>Nv+h!BNC(-_qK~SIEh;Y~_y`!^CqZ<8k9tS{I%-?pYciTx&e1 zO~}e8)FHJ{51tYkFY^x++r9eFJWxE<4*uDApX>dKY9OSM3KDO^dd+)PeF_A4q|d)< zFfIwl1Lf*2lW@Euu5x58nfbQ4SPS=ijT_6YmtJS`JZ4|NZV=$AZB-3-H=5loEBObm z&%PI-?b>d;$RA&C;~%LFNbaC+CeGtwU-%9SaAu-0@2X^i^F;FPROgs=6BRqvywp_b zoWhYC~tQRF_iMGTE;V8f}3f&9+rNk0H822NU>y&Fy~%f*U)YMUge*Zzhj zu9R0pSB8(Ow0Xml_S8|<|D+iKP@%qIlkO6WPN>&7?{1ou(i4umnH}{8;*}HX z0IPFF`44JWmR&S&hMxtC&;N&7QdM&1KULb7Wk!7epDOM8GV53T3(6@u{^HN-Ns`{DsU8Hb)*<^HM8O^s9Oo{Y_*0*=ZEg_TTDa^8u29sd@HPfcZ-B6)Lj% zA5yh9pj)KJANrfN6E~KRx=$C%;>So~%8F(Xf_^my)%=;!fHwZcEYL zrV*Mi-cQA;-q=*6MR`71qdg-4zuhEx#P(EeGXCOLSGcr1xi(!}n;RcHEHEb~YvO-=*3BSB<3eamBc9ztEfdbxi1kkz<$-g`>VxtAv$iVi5 z4{$k-x%9fB84)bz#o`O;PLUA_$xdmn1Gtme&L~7MXrRC`OEm6`AceQ<>y1T4mZXT; zSyJQp6P*!48<%t~c)=JMP)nnY0G2s&ZSBpfQ3w^mMoX!q(QX2Z&}ZW>}l&Hum)^-vlnLY6i@U}?9KpK(77Eoy z_qN=RJfXAxgQC;hX=V7)XlFak=8%@ew|lf+4jjSOx7hky@0E5*^$CYput4t&dsZ0&A5V4M2J&RQ=qAO{R@aC-c; z9F6f(xS3yc*Jw|XXW#N%?K$%$(Z7fGBJNq$DLuJ4bRVzisZFPm>Ci{}Bt^50z~=F6 zi}gwAw3Ka`E%c$<$hy`iEq`l}l7nGcV8sjXmE^9tUR1tpYanhPstv0uU|w%jTpp&8 z^x#fgJ50+ch6jJ%;Zs6%bzr|m>QPWwv z(vW_YuaW)ELU7p_qdv5b%(+D)Tb^6?O=it| z&(z5JX8I2np`TmN;%{-x2~9FfYeL_0^?$mJJ~DtXpI@Y5*TT>d`HPvG`CO`Yv9=n| znS(2eHSCD(QhyYWZg6%Gt)F&|5-s0S8;k2UIu()kp4>#NecE|{^FYK?7;}e_c+MR} zG6@8)yFIWX{K0I*#Y7xY!*VZ1fXCoXP9^hd7-HkZ&`m&Ld~bvEM)9^+Zz5K1KymkM zaxRM7muQPWF%s~-#`Pz zlI@X=+S;nB4}98?tF#{WzebJR#Eqnq;uV{;S~S``ddcm;N69a1^YDQCE7~4gwXeT? z;dL?a&)f|>H&R~V3e!1Nyz&*Th*3D(lxt%NeREm4wm@1S>~U?r)wwLbIIh)|9VW{A zKW+fZd(si?J#E5~iFUrKqKDq*Nyu||?K_5iq3ze09TRWqyKDyVqDb4Rt-(jMVgz`v z#$b4;Z(VWhuNtj$c_1Erk0%R{(!lq5w(-nUK458uu0*vz&`vlV&I#nYBv$R!>WR1Z zYUx(z#Q34Tnv?s5hUV7;Mvqe`Z~97OONk`M(1SD!xWhjq@R0V0wA)ZT&DD6tA*d_! z$4!R~c~7R-4c~CD@zADz%cEp$VSKB7N+Zd0_30xTE#TQvd+bq-3`AZwtH=1>nB&K^ zyJ;JO8!INDix!>I7BU4COd^ zRb~99>LgJ)Lv^L5HzqP{t^KI$+#34oky<*bFy`3-we|J%F%G6gpgxXkTGi1>Y7&>k zx7N{jveYC%BTeJlubYw>`rDjt#O zD73bysovOjU0L8#Qhlo`@_B~dtGu~B1tE`OY73n_pm(>>H#+e<^TjnS^j6}94mw#B zY{v}ksFUfzQs#U|eKXyYv0l+hzn@I#8N+c7ve>N;q9Dp$9zDqNiI(XL=ya=VKqukA zlj^SlCd((clY%-q&~WF}4(X&1sK4VCAzgA}S+cF0eyw!3_@bNsD%K+6zU%Z^YLcZt zR(6)3q-Wu-n~rm?EOAp$eK3)s$yL!)?4A>(DVmhkA_lMzTKh3_rB?HV2^IQ!y`U>%h5KOoYBWh z$rdrH6f2mn1NG)ngiOO7cAPa2&aqi=l5GuV*#>Z$O}1Jn@Jl)egxc?bRJ&i=1Jv5> z(iY&?K7+W4HP~WbimWa3k-cRC5N=0VM*-=!H6kl?DcL%dTxWrO+Z}mc+){>^K2Qge z#9-Z7e5bJ_cHUuLOU&yE^IB|POU>(Y^SaEu-fLbLn%DW}b)I>hYhLG=*V*QEmT@h1 z&NLs)Ft5|i>ooH^)x1tIuanK|B=cHiUd`+?&I#t<pb%fP9&U`S| zyxwA7$C%fA^Ln#+y~(_eHm{@1>k{+2K#Uu#?@KWw+MRcc?nCtJq%v{)5PfW~mF7!f z^9n3q%9-OFYF>vC?&qq>-v2*-=aR*qEbSt6 z8(fO*JmFgmiMZW>h()gIpgS0V%S|wX19;k{LekI>5H|ckjq=9=_Rc8Dc4)-HO&3c~ zP3O|%{cl=W2DEX8dJ`QX2t?Z%$sTiprq}c(`lD~zM1i>Qad?fL^w*6_O@j!qs?GkG z46I8M8vexZ6a0~q(Pc4mN6r-YMN-qXOPbhmf>=B=*4oxJd?2!q=3e{9mq*^%(9!ryK6ZS7fHEmYIqq;wOM2?PpV^WViVIABPrd84>^Qa(0pkeI^@@GMAL zq!Dt%Wh4tuUOqecH+;I~)a*$Y`at?Pk2~R&f3Kk_m1^l@0X#*n^)P_%no4g;g*ZOl zDD(%l_Qm<76OiJ=nljo*3j#2ogy5sByUabDAXY?SA5Jh4n~3x~VGn8rzcYa6MzbTq zXnyJV>f}RzoGHbZnJTI22K}q;*yfkUs?!g+qe1NNW=Fy}b;*i)kRBW;wAlqf9Ri?( zH|FsMqFL}zud~V^pIuxCOoJ<=svlDt1$|pp_autpWFN8JD8L`Q@&Xe5AF18|1XU-L zvVFz_X8&BpnQ}~NVfFg|S1fPwWs(UZd7xRqRdDa}OT*Rc?@2J3;LUM^d@%wAuIQ36 zrnI1X1K_LSOC+x*QU)3YTqSdSaX7zpT=fP-5}eju>KgQ~h8jMmbZqtdW3#imQ1SJ< z+^ohFHVU{}B1%dwrlFU+Qe`E)V0|P^L4yM`oQ`p%Xq1;tsQL1 zkx74t;0cT8dh2(P>VP{_zr?V~?0q0t-0i20se4g_f!A5sl#km|Vks*Wt$ORDOuf~h zruR$6mW>mXl0hIP1KY_6I2o8Ix%i4s%=x9$s?$&O zxVZKjlm1oU=<-Xa7FVaA=-Fedj|}>uKmoG-!Mi;Sj5~KM3V1vp@aZGSJLh)eSUbOT z$|dA$$XrugQ|g+)VT1nF;FWUwJi7fr4y@Hzs3w`18PWt<_sA0ujcYcrj{KruK<4ZOA9Pd zLek!EFBwrb1(A+`>v?QVYOt+Wd09I5Dm-T9=7P0KaIYjw;`Qdlp7Qe zZ7sQ_tcVgNA)wbLW^{Kp5WPD)Ym2QfxH|D2MQCEJK^-$J!Ev|u1@TcutB#}@h2@Mg z3fte!7zso=*S0p7uD2}2NEF#iZY-N|A#&OwUYLuhxm)MDx|)K&f1=XLo=x`lsWVGc zF6zLhpL5Y~TK`!xplmupEYL|X?c(-J(50oOQLeqlXwdJqONz>-5%fuhSk)Dgr>nYg zxIcsEobTpp)wCu(n=zG_d_^Na!q-#jYZ^`J;E@UXRM=@1G3w9f@5{W{(pDcCbvoVE z=;SpPTN~>Q(i|kQKA=!|LcSd<%sFDzS}u+@So*y6M--fdjGe6u30_T zu&ZHTYm}9`vs~61S^>R1I^IOpjz~y!(A#2LkJaEe);lph-jSkGl4YZ%H*GQ&k14$k zGV;QD2TNZh1@aaEOBl)rW`Zvg06HLCosrqz3zH$s?STh%(DIDOl)(XHu8anvu?V1| ze3bhFsxJ_UxwAt+c8-L{l-^48lg;I3Axw_yn54y*10_vrS<5H@>&z?WbRAQ=uzCR~ zH%eH2G1x>g03UncjsOn@cc1{M$kD89!1@KDRHC+yZxH?BjOJJK*Bev1pn3s`W<0Tc z|Ekdd93a+|56DOdRE&H;`K9wwz=aigBs;NikH*Ih`bc6FhT_ilAZHCU7NGiK9wZ<3 z5=;RgUSR{yt4{t>w4vh5!wn6(+oXRL421mBxz*{zfe$VAyMrOjnlSPvAr(>pXtPk_ z-cTSLIuO8$;b4~fmf8RCPPw87Nq*^^>h!&_#5SheN=Egs3}0Lv&KgrX8}u*CfJEWS zA*hT3kRd1*E)2uLEZ~WFqcMM>yC4z-Rs`~@`m+6@{L)#M)c+D;HRAF?TxpLQ^snYM zLt;6}a+k$&mz0MzAiHe9)zC>W_qCT?S2kC?xWiQhL57oyy1f$XxCr)V z8&v&+rP?cT^$uf%@}?&z}>WKM|YH3-JHO$GKu}4N5OB)mmEPEuVCKAu))JP1+96 zFY8j3N0 zIi+B7Y~ti->ozzn%Hzl8)~o%(dw5Pw9GIXEu=hf#ZtKUPcdM5Q&A@GZXR@QD2NKMl z%Frg7goh>=_Gb6Pkoo!GzvfgkxOz5YsypAelyqEarFDNDq&NfgPCfB@XN}hVd^DGo zqqXW%bH!ZK*gSgXuI02=cNuH-{#F8s`4G5AEseww@r>KyV}sco(4XzeM&de;+hgyDh8_HM z)xO%*DNA`b8a=k}m69$i0lwrzEoT@#mVcw>68dnhGrY}mPu3llSoW%#yEVsHL+t!p z@0th3bY;T8)OTH>B6*TSogzj7zTlN}-i=vdtzJN)=)jiat%yFXDzK*43-#v*WKs;LVt(w)``qpz zA&A14JCeUbs;(6ijftFd!IjisczG|?;*fXD3dC#0qdG1G8aq^$toj#as^U?Gy(xnqMttWq#?R>hvQC%I2QUjOx4L3>CFh4K34B>Le>E!>mB#siQZ7 zSaq}zoi=FgdS6y>)~MW|#g3+>D9M(TewUD1+edHF@&89E{$n4#mPATnl2JFYClwxB zp$%FyNMf=Q(l4W6gDV}Pe=|7EK>NVstAAo~y*10xIq>$yT5;#mA4+moCW+-u4c8lZ zndK+QyEW4VM;n9>yJ$!p7C`=Vu*Rh$mdB-u7C(k1LyoOf8<@kACR%LJ0%*|Xt)sk^ zGC{s%rpytnveu+%stDV(9{7ra%laoK?CiZ!FSld0^Ad#!5lGeT`gp&;FIb7B^r^_( z2>*AVS)=rtzbYBL(k0-bPn$60-7`GFWVbb2X^@ge9ddu1wKBDVfh9;xQBVA9fvbaA zl}vjNyw%_?e{@21y<0M5WeVY(Uz<`)-IFHj9Cp#62RTj+PY`6iX37w*T&>6UJWL}g qS(jU^%U`LLRbo}{n6Xl;nhj~Hh$}<;m~> delta 34340 zcmZ^M31Cmh^Z46+_j{kuCm)iCGl?sSAmR>n-$~rmRn$!!aTIk|P(@J$c06ojrdXoMlG6$DbYsMwl9`=&}1rHLEL=y~&<5f{@^|NT!q#r{i= zYP_XT$;gsL1VJbwG>z$&(z|Q%l+;fKcI@3d@{<9bOAH#A(mSqmzkcZ%jSJMY(>)?f zM#kg+PL+~Er~kW3pT4PGhki0}fL-<73MF9O1S6!VjWrA8J&a|Ap6_FX=>`3vk^ezc zTIlw_P&vHswp1Lh4GId0w_6&R(x>ZxJ5p3=5rgD9D)jyjqJA~}xjuUb5t;K}vUKj7 z+AXE~paEUuI(B{+u&>bYeOw&}4eZ;!Yiisl?~ahv6Y9SY*0gr@S`BK)_34s@>@)0H z0ip5xU=2F<>T0tmu4A7pK;+u<|F)Xguvzo!_3OWj55ayF>b;MyW~0Wf6C2ij7u8y! z6@#hcNVEo7WNWdV*nO zbEKt|&;_e7Ndy1jppf(_%{&`lHIov8=pTYax(Z?$I+z?LT{rqCe@LXRYEC^pWlALy z0ogG;LL6%CS6Zo%pub&IFP~V>a0H)Bd zNu=e*&-Q$-f^=9dA~q36rpF)durc_^Jqn4(rK0KEj<@m|$haIrWdVPB2s6PKB88b> z(_{3x7=<-Pq{kocv=M&EM`71g8cv17Fa&E#j&eKywHlS)``Jamm#h^e;gCRnqO-&j z!lCqR34!!qPELm4%E;#ar)v4I5k;?^)-F1iURx^<(>M7-(nsG9O22xlJsZcryJFIZ zp8g8Qoj(a>pyZiP`MA#%=R|sO_E!d4a3{*h`7P|@?0Lq^je!fLIpDw7W73OUTIH0( zna*-dy6^Hz27Tc2RVT4=%aw{;YV_NOS=p`ItStS)ouhV6$i}UA$8wa-?qxEd$ordE z;_m&eED`uCQOYJv6Vi3D7OnTgt zuUWSGC??(W?*fkN-$aluDrKN|1yUa59R7%ni=KBPjM_{3d!iJ{VwC?(iL$CxAem&t zOCkl)4M_gk0{K{&EtaRnXZgu^$U{hfPzecvC4>Z6)2Wm!x7TyjKT6ktSu%$aMx&EjRg=OFjhUA#+)7AP-6O^Xd1P#E`aRnlJ_Y zYs4@pnwM0BA;p8DASxg6S#$Fe!u$GtesYgh|Gofu!P_iI`pU4o5($RNMTo=XU6D^QvwN)Bm2nTIB}AM8FVmRMvu~GVqtq?i7UmE_W#HS{8Lmc3gNz>1i0SP$OlQjpz?4a zfh5DQ50I7WFH$ik1J&Wr-hp#*iZE)6Qput38N^ zWo6VyDGF326(Czx0uBK!{5-;GIlneJ{vP(_b;xBmW8Dp1v$oVF*Wab)6~=^J4ap6T ztq~c)8JF}SVM-bDwQy4pS`aqvAkdQF6#1Rz^3J9Q>1(l+*i~pF&V|@kBo!)tMD{`J zdZdaqM-mkRKMxk8;HfPB0DQO6;B)Vjp=1#vvDay+FbpNYp2eV+kYYd|PT0z71%{I{oZi2t ziZKwIM%FRFdufQ%sd9A;+0Q4#Sn>l?gfQZBa)1@L8c+7HREaOhD%N6D#sqT0!*b93 zjCj4XT-9@77P-a>j?E_LSSkP{(c=)g0wXi{BgI&#IERe2=FTN~D0Iv23s>Zi9i6vi zq#mV+lb!UXTWS>a{)24DSU`I4t_@$9)eWe>hkbgNT2-7wY9rAonazOG5(i|=?M|$pCL6)(u z5C4Yzz*3dIB@6lJb(~Q8fW-y$Y2g!Yo=MXVD7tS5VIqk^Li6d_6&{33KJI@LnBfS+?T=6vd(Z!kL4995& z&XBrV_Fn{92odOKVo!uAi%9{7TqRvu#RpeOZF}|V0+H(xL91(|Y*6mhz61Jn?A;}0 zKunPlX(b_IJ?cNZ#BJfHYh(a#_c~5`r-OxmBR{@1mu62Ok`anF5dNeK+rjgvFHe|l z0*w^ELrv}IY1GpAA8za3%ngdd<=slUK{ALHKGlDnWZWVJIPJsEAtT$}BP&=tgYD1C z_laY%g}|lzgqf~Z;4gBYlWogGGL`i;7#|Q^wlylI&x~2+Spv8v@bLnbIMw(tS#q znS+Zy?azBwRi-;xaj-&nv(#;cZg)~vK&36Y;whYs?q$WVveAPqbv`>~%d8a$rkyy( z_)y9e3z#|FDuZ=e1UzAs8A50s&;}fBp8WJQL#C`njZ&-tUCutcfA5I_vmjmQd|H8m zv=k%G>>`wN_-+w;k^z^AqA~D1ma<7}1&UH`|AAK4?agmx-FipM(eoT@dD_{_QPjXL zwy8~Lvd{Dyh_Of=$`!)pI_{VQb!jpvL!{hiBK?FDZd(=yhf#a z30;vj#Ndp9bg`p6Lt*m}8U^P^(9sO8&q(?sOPyYe0?~F99mhUT4n+p#`HW6+KCQrK zw2*;)cx%z+oJ60$my!G><&!WfV=^tQzgO6P7X9vu9VD**Ik@=lIwHJeL) z@8!U}dGywMAc6U`(fd}(X?v;ewUnN5;if&*4QtdgD!qrw3VcnIvudLKpu^*DkTTuB zqgULTjuG+MDk}b`=4x8nt!YKBr@7uMhSN63=56?IM>o<2@57y3fdo$Z$&n$Ks+u)0 z<3D4@Rf!hQq9rz`!KURj8WMND*MesC+C{_OZy~VTo(0FvIFsASj`AH|Hz-PxFFWb> zN4=P??xe59;5yd%F&#uf`j;Nh*iYX`?@=P*2=!z&U|U?)sH6Ww)nl}eOB>tp*Wk?R zplFzQik^EH7tB9RZ@!y>D;Hg;yqg2oaiq`Y%XGh+>ogg-LWgr@ka&&jBQUPhyDpSt zf5!brS7QcRaZ$59k^+h(<_g+RUyAW!7jcew*!~aVP%FT)o3xlU^>;crsC=lwk5-&CA4*WW;~@E`Ox+~qtmhf=+20S&z{q6 zY)U?TPPv&GctI1xa|jg${ON{$e#gRQ_BAp-^0*lW6<*TT)}5C$hsbw8O>Bp1eb^cz z7Mm%uRR~(%qA|0ci(6SM2Z{IxONHJ>4&0$`L8d4sMP(O05`^8tzqr@?n9RW4*>l^A zBYZ?>(F61qqp$E4#7bfp=#b>|z^5t5|AWasFMO?v?U;#_|AsG)%4@BE>-lOBt51@z z9Dy^*zT%doiCg#@TW*XOH@qIV@k3s>SG#>YWd(vn(IHp}s9|z6JoCHI2eOIWj5?{~ zs&^;5TaXznCTT9=K>O2|Pj)tx5EmkTWz7i{dr(HG;QTbB(R3Ji3Rz47f?!pJ)8I4A5iFnxqT_VL|P%%<0Z!N^X zM9FRd!lOwPshUwp+{vwfB?4`pm+>yD!zIPv8B2DT${KuWk#cD{UPk0=!tn-T4e*r{ z`Kr3RTo!bBQRb`Cua(>mAFAx8y3JCs>s8$L0#(I5PJ8LFE(S8IiL>6DT@$K{Y%2g| zYlz%YlpckH&s|fT>eRCWHAREB=kzIeqPT>O;7FqQo1F@Q(-p*MxSPZqwF1eahY#p{ zeFhVZDnz`_>Xkc<3+KItBJ;&qfkvXqO`LPBT^Bd zQ+o$z;r>)KIZe)uaEp(PWT_C?H;NAx*IF@aKQKBAFwISc4MkGl8u2j#t~7X7yJQTke%sVKaSj6HXqn1*3n5B+=rr0`Us34#U@hPhhu}G@nty|Z23tHNR=dTDl#GSByA`{-Sl1~%Qc{YeWL3r<5qBeVUXmDV?q0FBV+RI9*&{gn zavl{wcE`N+DHe~R&N{~niK*m9r4K;~cKYgX^j1MwzHSyydQe< z8ZPbU*0+GJP3 zr6E@0uJX*HQW8jOJh{B@ns0L!SSf8dX@ zzN#e+;d3%~1Br{|s|E~vFkEgZF@eP;PinylSLj2xZ*M+y0k5{;c~+n$Hm+q8rV9A8 z6MUOUIK*m7Jj5q?bttn|DLKvnWLqz( zB_DcJAGe!&s&s@^xt+@8!V2`2T5v$+4Vp3a`%BEn05AKyXwMAb7_7`sq$HeiwX_!S zQHX3n#kyLoHMN#DK+G5+$sAA4k#5CDBc+S1!Rw>AeBp8_&EkNa(dMUe(^I!)oAGbgEYs@bvBVz)=Kj+!&XHZ z>yRvxm$VbWSI?hYLp#2t*cFcj-kD@SqQ-cUA@#7@{Uq%c*&z3=M62#p z(7ZxdT8|E-OXy)?I(;e@5-Ny|?SmjFwMQx=+Vg2^9QqaI)39fSTFg4KPb$q16Gly@ zQHpTFnRC@!%TaJ+x$1+lpDE!`WraEeUanAEfp!N!7k#6SpS|BzJ}u&H@SEvK&e3+W^baQ^Z)rSZYkUId zZg&X3L2SH28>eJRZ8gfE7~CW7aTMX?1yMTsriDhV-UWg@`W(Nj!qbYRnC+u3o1XC0 zK~Tm4sb1ZTsv%XQSQ0Gs!@q?C_-l!9oCs)m#p7|%4wREf+L(@}>*!U{kHqZfkoC9? z?EZlzC#6!b>?BU$^ixs^=yXbow|1S9B8ejQbQ)@3M~^bZL0Dmf^KH9K4EZvCZ*#t1 z7qPhqea}eM;BknUANz`4xw?C_67oI=;7wsaI=Jg(oRv;;9dh&Hds3pojZ5xq*?ReH zk>lgoaz*OKCr#OFj_~0ujfR)kq_M7`Ia7G^b?Jbcfs&_NTYr<e#Mfe5*dWXtv^ zAQzIWK#`~3HRQ4NppZ<7k8~S{UL}$;4ugwGkBlgJX%NHotPHw0E|it~xn2CEoZO!k z{aem$Bv4+i;}vV*cBs2RO?rSNfWJ#nV`!fs>rz^Sag`w(p*dk%xU9g$1bG4c`Io4} zpB3cKVQ-`#8BhdiwWcC|I{F%SKGk5{2l9ukO*DE@{NYGNxeLs&4+QOUvEZx*8I|NN zwp$Q4P9ZR~GLrg9sEUCBRphM<@BJ!BrQh`+Bwbaxn?jXetwMEVi7z4_B+5HkgF#91 zUY5F<#PiT8Ap2y7=3--+Tb@5OkypR7aUI=M?#XEJswoyYV+k~qlk=kPn=I@Qc+dkb zB2Q^C+JVlb`-N5zxy4hC8IUKR1;tRM6dZ0Tmthv< zJa*d~hx&hN2YC#a!j!IZNx0je8qmI*JerA3nKsCgT;1ibxa@S7o5?uMgZjubB?jT| zkAiWj|69QrG(hgfiXII>nX?7l3Vb4WR9I$-Vb~`}>cd(i?mWZgDI8`va*>s!r(#K) zk@9j@rMn01k_w|xUYvTjM#(#z0<^p2tAx@H*1|No2PRTr>KHkv6){%+L-I&MrXc2{ zHOL4d6XTgy!NQ5iC7epF;NwYhD#LYklDwZa68KWC?MeH1T!&nGBQ2kv=7nmd%~1Gs zlpG3+TKY9;-_oDRD&^=S$K;n}LGJjbv>v{1$e^x6QaYw~ABz8zqZ0+W)8bdfa!;7u z))z|pLe9x@ULRX{?I00HIiGK)M9I`i=A9oDWlf(fUy_ileW%NFsV0`A{m63RB6j+) zlvZn8cSu_*=ZAJ6C-bg?TpN1NmIpfBNgp4JWCS@}tTC=DjGidxgVB>@4UWx1C`YeF z&z9@JrJ3%0?T7^#b7V;hl87dV33N9G#%RJKTyFhx5sH&G?KWz|Vpn}SY>!kln7>*! z;p4C6k~jfL6}vPV7Olo6Ft5Cn)_7ds_w%@~Bk;9cTT1IZE;YT~*<7$>j~oO$mLcNS z8x;>sTPD}yQ*D`CTO2U17gYGrpNzBE*7U(>Bg>s86?qx{zh=447N6Sx*QYsf1pdNH!kHJ zSVyvK*nIy7Y=r=0YLoV`ehHG@p=!cXB;#_q3f~a5*dZ2yVc*I8bn$dePZ3C3B~NC9 zyuAulhh5DItd{%nDpl6=Oz>}TRnYGnWM-FR(0~nybwZ)qX16#b(@ovV%$fxOIln^j zkn!?PdACA4M9F$GjM^nf!q2s32ExToR(-^u}=Gzx(A1+ajo;|-$b`N=f#(B&# zs~q4RfzX4voH&4Y4$AJS;UPK8pK(OaWNeED{iysk1Koa9<{sg|F_bb*%+;K;J>KrN=KX+ z3|fz)9yqkd)6Uwt##2?ycp&E|3|P6M7lC#UIB6Z|gQnt}b&D2OD+tpa)!%Q1KOY?X(x1(mvP1ER86KrM zZ?=0j#g0XAJdowHD>Hcyvn#FxAXpixuu&xER=9(tL>?t8d?>F{fv?0Ef|ySk<03P| zm6fdCzHsHbgBA@DMW?KVGx?R@>{JLGEufs|#RZih9a<4w$uAXCt}wu3#g*GEbtq1` z!~shvXbL-v?y)iqPcZB)tK78nLSRQZmjE zzt{p(c2BG=EtPNh;0Lu+*0}UeY_E)A)i1ZtV&2C}GcU_b?}_{7#6Db#z)V&4umbf9 zwFsDf73MENPl{5Ui^hwA$OC6+_Xa7yGU!t&h~~v$<$FHTPZd|qoc`D-0+oj<8yFxR zrf@IK>_25}U^q5_-N!jPTxr4^h)7dd#5d?;6z;f_I@!=;IWz>uDmX{EZ&ZWghgB8g z#~B(a3UfbKI$I0IDYHGSleMQSKe)umGeeon`tkD&jAl(wP1wW^8v?D)eblel-5r}N=jc1CY#|kCII+v{%&XuBFA$Os4G$_=H2_48__siT-eA?|y3EZx1x$>KjKD;PbxeTW*% zw^niVC5p{jr7_oK5$lz`4CdeU?(olUK$$~kBj84*6{l15W>@@_Oobc5XEPZfax$Qx zcg|)i6yA!&b42CYRupI_b#)smG#evq*{&oNYAw7C#pi+eXch~tX_k)_ebVUsDZvcn zo=N_)aJsm2j_dcpz9<~qp*-HQ%vk=bihY>d1$Nf+uY`q({$m(Op+&*IB>xpSm+X(u z*pnrft@pb0DX|ZQ)9Kpj`Mx5M?`NEq_NNv2S!v2g9C-xijx(fJN8GVrJjzl?nqx{m zPi%JKmVkbucf3T*M?^yC;rDzmjqMZOFsOY|iGcf;lpYY((##9fE-K}r%lM!OD!;L| zTvX-}C*N9hSuse)RRw)lP6vu!WAqCK_Ohe{n@xyL^W z3v7duPn0zx+Y^ou_u0yC9Ps1UN)34QQfY+^pm`Ss170ch-NtUbQo1olAeikT zbh;4nMw!TR18LlXWJ#7cAoSvSk4UY63TjV~~3J+2Jiin0zKqAr)x>W$A2OBeX_ z!l2xE8rfx$T$Nkv4^c_w)73EJt0RsXSGnP7s3xjC7^woHidJg1@exq1yqp(0h^hyE zCu%iTxja>CXGm&(LdE>BUQ+W~Rb@20F;o-!nCb^iCIY64>hQRl%ge(aFsKAWOWv6`ojqy_@e1A)g5M!5^ZYhiTOmS;Sxw_-wJ<(q6Yhp?#H_8iI$S_Kz*3n7)g)6AJ`h9? z20WDzW@RJAAg-`F1@;$GyFvaabuMANthLRQ4 z$5fgD{qLhJJ*c3{R<#e*Jrat0H$lur8<9ltxAL`;(mozv8IE=I53#U&3Sn(}Yp6VM z&MJnTd09i9>l9d-HP!o~-Fs49jkC_yRSjN4O;mTXy7v;*Lo9VEN!`M#ZA(@I-1wi| zK;>&fmxk&Bdrme|XL96?)OH*ywuuWxo2uM0={6tN8KarX4T#KUY7Ut-^iM17uH)A` z+FHF}r=s9k8}*>gzQVBWBehoU2|_E9Adm#qru9f;(h6749;6=`LPkU6WU>%8Ka~o@ z<+f@YINMfj3=`U^@shoRfGzFR`cBI7v{%azmaO%$nvH7Goj%ozd>7=%#Vi#E4N&SP0r6DcJ2!?*0krRtLtGzhg;<~EbLMXS3;Huh9 z-o%YYCsmECA z=os~9mdYHfCPf7aPXyr!Y3~G4#bOv$9PLEyTN(D@3ky=4W4oz}dE(eAT;_RD3``oQ zCcuo(DR)$@5AnxYF`ujbMTU9X1a}adCaTwX-Xz9voL66}VSL=rCnHUr4ql()PVwVY zdCJ=MmD& z*a2yHV3Us~Vzj(-e8U!IJ7`Vl%5U;G2kiDf0PYUNNZ!U{@dnT1(Lu#P92?XMK25{a zN8hR|_;LFy`$W5(vuRk84V`mU(}F5M;sFe?*p(JE0}pm^k}nz)WS8t?-Lf`MyGEn_HwLd)R zViblm->G#yqsDj1sTbCA>76P1Q(o#hpxsKfyt`zqS*eCV_$u{tM%DeRP_ArIK#zmk z*~^q`$<6MJ)iYIYWAC#a7iPenv4MbEM%`1^F0? zMdM$k$c+7}BqfAk)V{D?xJ$xFP109L&$@Y)_%6>lx4wIOlm&P3?1)FP{fxFnpLV_q zu;;!M4qMy$w!wE(jC|IfcD_n96YFi(BL1=A$awVs|YSWaZ`bRZBt;m5(@e1QB8N|uP~L_0&%po2pzNI1%jzh z)rs6Bi+z>V1D}LFctcq$i$nZ6xQ&OIm=|roHG? zx}Kg#jS(r<5mUsk#0}zEai(ZNMM-M{KS)|Jm@aAgtS>Qzp(z^9eupZty2iy%G&H_+ zTsO46TzpIoLoQC5?P2yzrKmM!j_(sO!>@TcJt9LjE@!W^QWtY@Gu?9N%Rbe$S&3>xrp<7E4LQ$}MgJC|qW0|PHfG|{+X z6q~w?y4W;JE}Ln%J!8`Up<67raHYT8C=7rxS~5O{U<8fk?z^Mfx!il*PGbfH{MJ58 zia*wxakL+{LWxW4sIik&kayw6r)VdQ83%B&6KB1Z*;(_8c27d!ayQL&?RD20+xt6D ziiU?&AwpdNe})Kagi9nFsZH!li95;fG?OQizr z<1ft&g7Keg+x@nWUm}Vi=BJIxXG8&iKkaWdwM)m;!C%yZt#$kn@S=&|-Za@qe?i;O zv`^jC?58@+w1>t6v~KW6e=Sw3krM?czup)6S5tFB!_NM?Y*`IX(PDCo|EU)|)7cRu zM?Qr7pJ?M9mSL2THq*WxQ1Uyi0W27%ownE3aBJFdZJw<|I-b+(L&7i&mR~edTgQ}2 zGRzyLo#kn3+h4o6x0!>xKvY3$!OIzs?;OVLRt(D`oRs_~BMS$aX4UbK}B9M11-iv6| z&A*<+*>MVf;V1l?4#r=JheFK>S`}zC zQ7eP4Ci*>0zh{J5$0ukJI7LFR_2>rh3!t^`>7$`NBhb8X)n;GHcnx=E0xbYlmtkL? zf3KU?lrObeHf2iYL(24>qVYQ|NR+7#QS5wc`&U{oepw?K2G7c(7R;ONq#d$u2d#GA z?85)=EY&tqXQF>0>>OIBaq~5=b!d)OnL?%c+8*W~dbnOGL3+c^<$9Tn1zL<_Mur2j zE)Gsj$0@U5;air?S)@6a!jEd$yTw`>4#C2^F_Cz$%wF_ zo2=6EbLeE)wpy$C0q!|-3zG!=S&IQQe-l3`iMyMAcwul2*-vg`ux@dT7#U8N(_Qp3 zeIthB%<3c#6enZAaiuk=imtEG!bK#oPzB&}vJS zLWG$D{_JoqGBTh1%x=Ov_u$`NMfvXSJ$Tz7c8uccMKBg<+YT*`*~|8)g`YLWJYRiMczgLrB_g*cY;`Q2< zSCnFKVvjbH+vU~mFspS~w}c&1S`;4c)k;xR92Le%#jO1Mv}qy^68V`7uMTKi8DaQ^ z&C3V1nT!GrtR086CN^0e;cb6JyKYOtQLWfJqGN47rX@>-{OHHD-xI;f%-<8SPxt9d zVJE+d@{etVHTRZe2QuYPYdx&PC$*ZAqd%bEdgMdfb9Mu)Ta(T^JV8D*w648MCu8*H8nHRsqn661 zFNB#jXtWiTO)q4V1jjiRj)v>YF>Q;wwLLb$F9sx8)uuZ+$3SQ0H?c*up!! z^<3K9ieg+5{jfc`V|6?OWU9ZYzA8(1jW4Mme`nn3ad_L{U@6@`OtS}QZ7Z$6cG1}* z(ktp~vCN5Ta;^(stf=ou-$f2b^QTD?ug45HT1i@3pMqAw(22?dIgtW zc7^dt`kx#kSx>dc>QFhUf&Rcvt3hb6{LoOhgTa}WZKOYZX9O=B>-*T})h7DmcM2k# z>Gma2x91`FcFJS{JUd8Ei0pv@>u;U*y?kmBYjSh_XU%PNaXWpVn_k~u-^tU~wvY9% z_yxmcn46*>cY%KDsoRs(=8mN6`Zi26s(gxrZ_vS8*?KTU zZ%y@!lWLpbw zmw*yq;?`aKQa?xKa@OT9^(Y?p5;jFY!kp6oPSJm19@Yy}F^_reztUH`Ot!W6>5=#_ z!ujdCb2AE;E7*QWuL93!=tG<~t!e4{NJ(5rhgkkOHsx-?g1P!-N<1)Po?f0vu(OzC zFY`hI6q>J>w+hbJ%hI>q1+@{HkB1jB{IO0Uq5P-q)wmVtJ&jDaA}HexbX>Xou)%Y^Usy>BtW+#mFlZ$GWgYxTK&peXN; z-j=}H4f^)CpH}8Z{fxvYUJ{%@<3pSDU2cZ6bF)^WoZR26+kwW&7-#v`AnZFN-JQ(0 zxlYjI_JBU~-D$UYr@o)JX0N3CLol}L!Y&=0JZsu+y(~X%IlD*S#<=y}UVSr54cez~ zV!W*Ovu=A(*$Vu#-iga_eOPh;tJ$iAnIfwW>dq$9u{;j_rz^j_C)#UIIMV89mds!E zZ)_ca;>z5-h|bn9o6hTYScU_$?Sj6@aZd84R{o(ozBm49-O%k9bQtJOea>6rmw=r6 z@RH8K+sF|v*=z5-)An6``nwc^htB-Amjzh$x1(De#Ts~|*Rdrn#F8KDzlxf4%XYw` zPqz91f4-tUq}!C-MXTTr!iYIYeKHtd*VrT6!OccB+Jge!NAHT+#RPFE#5~pKz^z*N zBFBP8KGUPL#TsLHEasi3`bNY+5iR)g5km%EsrOz-+;aY`{LE2#X<~> z(98mg)D=s2Lr25R2l*Ci22^>jpR`eog0=}}q({1g>0^0ibkkAz>{EXz#Vd@hU+4uW zUXEY1#VlYgexdiKjL+e(k*H1>(c{-TbIfDRsQ!g5^^Pw2N;VvioXZ5oa6GiQiBOGA zwp2JP@@Zm3!xr6ejW}DMFE@;%jQ*$wO{2DCI}-fHR9L$psGuZ16`#VAr&3Pqgx~lE zgR})0mfd&~!n_F5=lu70`!|dWHVQGH`I=y(7;}_g3^s<^y>wRkU$^1QHV1MUV9Qi) zBb%#ot!;UX?09l*Lp{P@&T@Y)+~5~8lda4MBMC!MY=wJK35Hr2eoka~kc$?zrWGd3IQq6Q29o0Hrk6J+J58Ze#7Ob77V6bD zRKHp2zwXvCwlE!=Sr^B^?jiU!B{vip;i(2a8yYXHb`6Z9BCFWAiSZ*tR-`GW zoa5OCO%2OIW@R=rKD77I{tt~g)INl1UysJY*_K8zJk{AO2%9lrtvC2u8LOqVx)afr z)XJzu;N?Jc@lcs-DiDwcscy~RCN+jd4@ zJ8y}GT6WI>ATQ?&+1im**fRlPU5dFMi9~!t}7eGV~r$V4m^m&AAD#=xGQuA-#DWHlo@CAf(?5#eu*t_U%Z&G zb(~QF3e_~it!keeD|tZJ$caWBsQ#tVjmcl7Nk*NF$wn%%=~FV(8)q$=VsyhmP!hn6 zI|*2xX~tS2rInu;VQrsoL~!by4l~hln{EteGIMyEQ3;-;8$JvN3)P*c4eeMJ7G`XR zvRS=m<7~x?_huPu%1QHA!u%`3VEb4iC#fwAMkaKF#JNTfxID+`E~Pb^SP)8nh2qnD zu3^IQ-u@~!Bck9;kQSX=@*(RxS2>xhE)#{atC%R9d#U?Uaqh+P2-O%;_1q)A*SI zdUjzS9KchXQSy!5I2;E$bGK1QW-#Ib-aK4CU>st3Ck`S8yPmb}kWohxv*ELA>T#nW z#3pD#P~kY%ICH`%24kZA5muF7jK?CY*YcF%7_x;S?`f1gXLw6a8u1Wu#u&_n?u?O? zyXE_*qx@`?Ou!eXa-TJtKwrO6=s#-$oH%QIC9V@Ngz22|LGW_E7_8vGzd_&rhI2+7 zJ2Hje&l@M*b>kKXxo!Wp4qq@zQRXwuc?4Ie3YU%9EDGgQ`_s5$usBMLx-_yvNUs@d z*!{-xEq^n9GN^RR=2~+q-2qb247D!aH@Z_cEm}N8COO(Q&)){W#!==5 z3XA#3n8etV`N&98f`oqr;UDk~)bc^}8nKkEBu$tTqfTe;pFeY(aaQy{Mgk9)AMgxm z?BFf^96RGI*)N|PYn(g`!Zb>Vs8a8~Hgef23LgWUL0ZzAz`)_zHjROpBE5&r!QIgnpIHNJydPM{EGM zsmUU#Fp|?8$zy$zWpfkj*3WzJVn`LmOlNv&@GHdXQO)^Isg-Z4A zfexq5G8s9|5`5k?&u8+vRU{mH>QMhwF4SasBFxE*1(^}%43WwbG>*rmzEVNcEA~Lc z%lTSW&@^Rq`Qr@|+Yqqudo(9o<-+DshA%wIh*=fyi%qKDP#SWSg)+JW=!5NL2lLT+ctZLWW}|I?{x&E z_k?3mwzS!ovDPYWqO-&yPM)&Zu%nD#ltp22I(nrXcG=0xEN|Aeujl6$<_q`}5YFKX zw>j|Lv{vY{Uw}{fo+FRYy&X9XHKL*vRnmzc$t7=Yl7Kq5oIA4d>G{5G`|5SB6 z8A+*aPGBR>QO9N6Uv*IEoxO2pT@$sqLz=vaCR-NZMIw^x?UcNd#A#+_CYyx`l|9zA z`sQFvgs_F9N<%Y;HMXJI3bVpl_)g>crUzv&BBQbC*BSn@t<5z|B`a-Ek{xs6LL1(u zmHCk=@{WXkXdmdbGaY|eVL02)WGf*YZEv#J8}xga_>^*17t>3x$7?SgQKlT$sxL6k zp=c)@w==M_oy?t1fwir(neNz9MFyF1kk-R2L3CK(!)$Mxohjy1v>c*odooYhj2!z8 zBKn$xZ4^-u=xf@sZj%Tsd59L}s8lnNiSPMTvyN7Kl8{sXK+6TwC;7u*c&h1#&Ib+L z_!cm|;J<`#`GlsLb;S=SerDU0`QdVJtnT!*ejl?=75l_5uh3AypI*XvJYm}*ToGQ9 za8iXd!G|}ek@e&p6=_jC>KKG!CK>c3K4z2`WwEz74d2+P($6dh^ZJ=9HQ5s(>y^=P zoIc1OW_9duE~JVa;)Lv^z0fgE>J!rp?jeUV@mwT`1!+CfUs!~Lor*TgB*nH_3c;ws z{#sxRGSQA{Hc5ttKj4TL_Vat8^`|%^vENZ}WC$ME^s8-z_@pnGL_Qpv)^d^rRmU*k z28{G!DacA&n+N9~!r<;rO=~>~U&pSDhh`Zca7p8m%XJa$>kvF8wp;I2f-fEF9!-$huZ;w(`IG zk6g7Je)J0248^}Pd-M76m5Ep2oDkXgS>^#oD$i{5XGZHAv&|h&p0ydwa#`lvQzwsS{}SwfQ|e#HjR> ziZ5hcxAKnn|4uTNn?#aC4Ntk`E_4VCI_t~nn0T=1sT2j5SDJUY!6rMKc@13@w!sD$ zzcbnP9z3heN^Hwtf0bF^!^}E4+<{uY%B;o8{#<3|weLdXnY>(#!^Al-c~_fd3h|tB z{C9c&JAwbk&_y&72d*~1piCu3{(xHA8Trj0%ngphw6?7^pYzq%mtj_dT$VZFt@g7r zEpvdwjgNPtT5FqWt2JwKra6R2c~O~wvBj)_@88zgVzz}h=zM`4Tg=Cr9G*wk%c8be zI@%wsqOQ&->lMJi4P)2uY&C-&(R{Pb{8^JL+9e3sq%-8k2nNbEN2m?&L&9W|m4N4<+P9Ls(1bd4DgJ+`6- zelgJSI<^bM>@w?Q>@jCkRjgcG(F<3FCbQK%FgaQESbO%FbrdG?;YZAaOd?+#L78=K ze_ESxvc5WM&T(dvm3hoek__iWbq5}+P6pphr8z!^g-nb)2AT%Wo6e>lbxlE9i}Yc-MoOzb zDaKlJ(JaZgS>0}<z;M{=!Q-N~E6l!KCtz zSq1&~1cgEOt7ctBzmeC>I%1Umu?~iRQ8D>gw42M+hkHeYQ1_*1->iXjvDqvm!<&?Dvt2;4TE%do+) z?XFn^hTSpwjo24=P|W@`@ha}PKTUiH<(|2n-!<>>4|2+(T8Ss7vmXnAr%#Z6b{>ZN zo9?yxlMhkchP^T;GU)D~`*0KU+MLHIbN98ml~;LVw&S;giz*(zSNT`*u=~OIG_7Z# z6Q&Xjbq!BR_%i~Jm#+K~q{2AUDk0#59Rax!N#&X98-1-58ht$ z^w-&xt5(Fr&kds?BG!}6K=;OaoI7m@TGaCg-=~$!kYeCMNzXKF1WyE_A-$C6bJh*# zwtKJAo>W#8l~KmChX?kYDeK`+=wjN1k$AE(SM1mtXNt9^1=|9_J{y-O1x_m^*tk zPSw*rJWm+WucUa+xQ$-!>2YJ5dU^1y#(iim1akKAa7n(|$72VkIGwzo>dADl;I%hT zLmonycc|wWBlzo~9_NxAz6~|ZbAkg7_Y`+RYeV4W1b5(Cm7Oa;Cr{RvpwIkzW+X(7vK|lo+=#Lv($5mN!hEVyj8r?j6^ZN_OLs25ct}Y zDEUcs0sP~XTu?k-Rw;c3vCBPoq2RZkVpjDPo*kmyMhJv{=V6zC;O=*xUs$`xR(Z~` zRA99yIb@F@?D@~@nvS1fm4%noDv25EJiloS{K+QIHI}-w8L8x`hQpbj3r@<)40z%= ze_rgswj7FF+35mWKjPqT??P@e^|9O2f)808j&r2`5zh*SQ9A0`#Zu>v^2+!Oxu*fg zo#&M25G#Im%ERv^UODYKz|o#T+9=LH=`aI)qqSIw_XM@ae>1{wz}0jTiC3;X<@j*q zKU|93{(}z!&fMU6_<)?}11`AwJw`Bmc)f8Qrj8#<9~1l zJAjv-@cyF4^!&-1o|Wlc#{@hYW)Lrb<0K#T?qIbaP%pow6%f63ayb83OPC;B#Ai># z#CW{HWB-8zlDCh239B~T!@DamPWE!ml2`H8wx71&U~liDVd)kn40d$z81PwPj}9{x zZ!Pwo#$5#`WJksODQgu!`EBnls&|gUGQaZTd$P-X-Y?i^r68~Uc)mR~GyL8p&Xabb z-c6Y6D7eZwyzHF(3lsKJ@}&-bBfEN`Y^LnVcS(undmuR!YO+-FUh&Ij47^ zRVJ4=p3cte<#+Hd<@MU9kk}SR+2!*(x2Zy)Sh#m1+x?uj@#l{4PUWnR@WweF%V2n! z-+PJm_s;^@yv-4e{`J=2{PY*`?q$Wdig>xA{W}V2@hsNs-m@<1#fTJ#l`+M=T*K*c zULJ(|dmP7LZ7Jcc#{t7idL7jfg7<{nDi=z5*=-N}$40#IJSzWnH5bph>Rx`y_D~Jh zWxOw0(_0TDd^-_}9o7>7o%k`#LR)_oEwpX5yk2lzu+LAMm*H%f$xD-4q7fZc+grn0 zP}@6`WYqJ9bCp)IiI>M-8%^DTXOzL=8qK^}*LJ)~9<2k}fl?!R!PCLJ1NTBttX&$}I?d z=1nXRBs>=Ee}CrPr&vhL+h>`D#}K*SEX`b^rT;PJL+Kp;7%M8xyGLdCUrq4x%Qv?s zvf+fluan$117CU*jbMDi+5Y!q@Ua^IWbYUh?KpT)Imm!I&w?VM+7#s2fyv(1keE#h zw|Y>7DW_lP&i~09{)QOD zFZY%sA|&mj<*hnl%bDQDTl6|xc$yn)q#;+H6zW4GM38fmS=X3v%)x#gMdbrj* zfxkS{;wLYEu&8K;cPld!Uu1Z{cRGa6k9%wQSf+m;Dg!jv@x}aoUiTfPpS|eNaxz;V z@UCHl$a&Dq?a9GIQ93<`yzDxL6*%PmlDFFW7Z;}R362c@KH;?(hQ<56@z$TedOwgL z+Zw5nHTbOe5)JZ|z#v$>O@`-;=;qFV{GmaG6vci~tQSmQjaR|@Ey7z0J1=;vnKTq_ zvz`LJ<-9^6MVIMA7YdF`VEXV%2H^G0~g zjt(x`^TY$4pWqudQ4hVL{D7t5KW_VZpSZYhJz+fwhQPnxM6DMYWviAP;Cbr(HT}0j zx#8JUZ|$byv#Fs-%coPb;opy@dhqYR|6f~I9#&Pg#qT-ya0iCHHeOIbMim7I90L&t zKm{2BL~*vn=sDmJq9#5w>7k}{ua*xs8kTc|0~#+*a>?>Nku2#erz}M)gWgLm%Swyy z*<0uAeQv$?z5B;K=i6uRwf9Qyeh5sGSRrr6yS&ILkJD1>p zn-k%x%%3@nkYu213jTL-<>9}-D_h**fA!0P8-Q zv~XfSjVATt^B8hh*m5j)wbfw?+0NrsrIN$On6+>$S<2sgH;f~SC}_9wkY3OLC(W(({==6^eEd&9C}g!}8eYA{ zif2gAH)IoI0M36{FOR+y>61eQw%gepa#gH7atS(I>0|Yx=|mvfWXvEMPKutW`%G*u z`giwCg13!aMK|Wik$5Udnw5$Ul2JFU=e{17N2@&`*4@!)5ZOc4lOMfR@W_*6& zd23tkiTGzS0S(RB-u|px1-XV3p8z$TvOJ^rlh$ zR~ksG{uL)QVjS6#SE4d3<2dKfQQc_JzuqX4sZDNTfZ0aBZP2<@hi4u7o-iuy`Uy|$ zcgX9a1(Y?Dk9ZfDb((0-#(KjW+CoG<&tuw? z`CcJKTzS3x3gIel^>0_maWRUo`+|JR1+eH#qB(%-0vLFMl!?|br^ zkyX?WIB)2~@0lOSbzT(rT^!@=YTb|Iw!S5Iu)6z1%(Z2|h*@ddJTwF@{FSWdX)gYR zYu~<|WUWRFaYs7I1Pvmf)nsawYUG7g1l~a_g|lDP$Izmb=Q#VTwGt%rk+f?Pg-FRS{7fm!gN5b zr-FyUBs>v@h;hk`+Mre=Ifxy#!Fvc(=N!n^*x{79%sE7Zv&cmu;Qr6SW(Qh>(LG?K z4=nN(w|r@r-NvxtKEUDJnA-=mmWYr{?Di<|Q)5@tE0U^E7%B$$On*4Sl_0)dKqv#^ z7vfZCcfiX+t`5jhh?K6aTj4g2Jo##RAZ!vcM!wK(Bp5I66Rn^&80PS903mRaSJI6T zXx53T?(TrDj&}9KFo>5!z0j;_&XU(jM@`@0tLImq=}cjRBVidQR2&6q=2T}gdu}sb zz=JM^K};X>9aKku)zP%=x8D`9sdsgWzdB<{dA4(m=6mO06Myx>qiKD6Pf%@)X7-vBpJfcrz$~(|^AXLMjn4**qab~fcBnV|2 zhQn5NIuVLF#ft-w&Y}^pfoJ8F1Zm#=ofEWU>JIy3nnfd!EvcjlJoa0aG$vdMGkMJ@ zxjb`2`n(lO=dCDQGQV(%&r&Xr^_46$31;$Icq<8rSiXFk3@>Sc0jx3w1i)rSDhSoR z?^wYgUitwgTEz`Wg9~=Rn({IgnasI(S6VybHqbnnjOGYu0ekzH8DU8CU?MN}N}Qfp z=tNk{R}hCMLWVifIf+SC$no@v;LDD`Kofb~VQCN(WBwHDwCPOJdm}w=97(r03uk@G zW1aay_MRw!LMDyy>dLz2!!Ry@+4-Cg2P2bUeD!4HMIRqdOojtI?QBpa=AJScJR-eW*d}8JOYQg+2_|sFbQ9HJqj8=W-*H{gpaVE ze=dY``oa+p?H9poybv{yBMIGVn)xT#tA|z3gI5KGdEg~huqzj!v^t-F)jZj=B}hPD zERI_yNL815l9fV~uGT}d{Tg_M$9w%L%t+^Z=33ap!|2mc!^7N9Lq-=rsoA6fHfU}u zcw1+y)XXA%;eG7a?R+b}s#zJp(%0jmj^t+4P?VffjxzIT8@ehsD})_rRyrFhpj-^f zE7oI)n>L{+_1SsqW+Shm1=?D%5-VPpth*Avv1#QAWQ~7C7WCLFssuA=QAc^|VQK{u z3}&!S7@Qd(q;8h9%fuotlO&c>i#o>LweTAo^fFk~13MvA3`Cu~um|d_AK4AY3iU%I z=G+5I_y@8V>y+2pUc5t>XSi+{9mH;`=-1Y3Kg{J}+xO$(r~9jB9WZDOJ+3kIifGBI z`xWRSw5p#UHHJ#Ot^lvpIVPB9S#KCE&Q>o9huD*_NJsODZGcBL6rarvP$o@giVCY_ zuMz566#{%Uo8a^sqPux1sJ26PvDg9Z`YEGX?`$@@=$zBYl+L$bi@{!F3n03c&OrNn zyd4Cv7tg>>-Uzeb$9AkI@%;e%Ho`|(rX8r{YFJ8b+K(|CI%psqkK)RiAa=VrA(0mrFk zg+Cju)c2l;|9f!A&t^F!EZNdaG*GSZ!t1*&mLnlv_Mz-!3w0=Q^DTAi5evmTzjf-@ zY>11d$&YQf(|tTF&ztV%Z;B6waq0Td0KuYqAEo5&N0;(A&-u{t9C(U-UTQ_<5iyHL^7f9ry!(S{kkQj)faF{LVuiczR~GU}+x!f6H1 zk46}@+z~Vbr=}SQ<}QVyPt8WWY?fxSQ9Ws~CQk%O8tRH?QBjmnA8bn$`u}X|L1%Fm z?)RW+wj|eZ&6Mn5?+*6q#tL7gV|gwYqG?RD;)5f2{?h&T4*k(?Y+m%5KCmA&1(nCU z5?SRA%s{uQufRl+<|dp87MFH}Z}?-P<%lwcD8L7;-!}B3i}5%BTM|t*_pH`vx?k5u z1h8(=v=2KxfO2&;%ZZ~qIo$_wR7?Txfpi-t${j@4Xh#ap4yN&91NEe4tRM9d6{`A{ zhv*hQC@hGlRs79AfinF~bth2SitE=CqiJ`%1NyVk^i5o}vggOpQXfS^d;1J|m-Ms5 z{DUkJ^^wDI0xeHbG96F->HY`Mn zMH|I5DO3$v1$ZHdcIS(VV?e8{e~y>)Of2ssudysWn|5`4ZvLnFj`=>GTbd(Jlk?20 z%$HH6a8;sw>tZbs&&g~~4*kR~9B8bGEIF5sL3GD;x%AP8riw^8;+ZR+dEzhkdF>ryJY7+qbt;vRvV-`o#YwhGOcX^{im~ zQh{bv9_uO)%RoJZ>i9;%?C4gS%sbtbK{O^p3CB|HN@*PF;-!+(5sd*WsC`y=d4_8e zs~b;;v%Cx5;mnzi$d9^z!-;g7${N#Xfo9U}W!HRoBzcPoaiDG1mDqr(9QMp&*Hoi& i5naha!_cAGf^Q~MMclTN&xOl^6Y}4jsJHh}hx8w4!pS!P diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 2aa7980..c6f9b47 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -254,6 +254,7 @@ PROTOBUF_PROTO = qt/paymentrequest.proto BITCOIN_QT_H = \ qt/log/log.h \ + qt/log/stateinfo.h \ qt/cmessagebox.h \ qt/addresstablemodel.h \ qt/bantablemodel.h \ @@ -354,6 +355,7 @@ RES_ICONS = \ BITCOIN_QT_BASE_CPP = \ qt/log/log.cpp \ + qt/log/stateinfo.cpp \ qt/cmessagebox.cpp \ qt/bantablemodel.cpp \ qt/ipchaingui.cpp \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index e4b8a3b..db5a00b 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -101,9 +101,9 @@ class CMainParams : public CChainParams { * a large 32-bit integer with any alignment. */ //Version 0.6.3Update2 - pchMessageStart[0] = 0xea; - pchMessageStart[1] = 0xe8; - pchMessageStart[2] = 0xe7; + pchMessageStart[0] = 0xda; + pchMessageStart[1] = 0xd8; + pchMessageStart[2] = 0xd7; pchMessageStart[3] = 0xdc; nDefaultPort = 15166; nPruneAfterHeight = 100000; @@ -184,13 +184,14 @@ class CTestNetParams : public CChainParams { consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000000000"); //Version - pchMessageStart[0] = 0x0a; - pchMessageStart[1] = 0x08; - pchMessageStart[2] = 0x07; - pchMessageStart[3] = 0x0c; + pchMessageStart[0] = 0x2a; + pchMessageStart[1] = 0x28; + pchMessageStart[2] = 0x27; + pchMessageStart[3] = 0x2c; nDefaultPort = 25166; nPruneAfterHeight = 1000; + genesis = CreateGenesisBlock(1507824000, 1861207187, 0x1d00ffff, 1, 30000000); consensus.hashGenesisBlock = genesis.GetHash(); diff --git a/src/dpoc/ConsensusAccountPool.cpp b/src/dpoc/ConsensusAccountPool.cpp index e1c79fb..33500c3 100644 --- a/src/dpoc/ConsensusAccountPool.cpp +++ b/src/dpoc/ConsensusAccountPool.cpp @@ -465,6 +465,9 @@ CConsensusAccountPool::CConsensusAccountPool() } else if (Params().NetworkIDString() == CBaseChainParams::TESTNET) { + //trustPKHashList.push_back("887f8428ddb84e05d3d32b86fb5a15a4d4bf9a0e"); + //trustPKHashList.push_back("3560c2305ad91fa6ce64176e6f7b121205a396eb"); + //trustPKHashList.push_back("e164855857c576b5963b385cc2a1ee33fb949feb"); //TestChain trustPKHashList.push_back("e34450364c0b7569162998f7c0082f880df76946"); trustPKHashList.push_back("e16d04b1cf614a3c897c30ed97fd21f11b017161"); @@ -815,13 +818,9 @@ bool CConsensusAccountPool::checkNewBlock(const std::shared_ptr p LogPrintf("[CConsensusAccountPool::checkNewBlock] The end of the session of the snapshot is equal to the starting time of the current block meeting, with the phenomenon of cross-wheel leakage\n"); } } - else - { - bReboot = false; - } //find the cache block - uint64_t meetingstarttime = meetingStartSnapshot.meetingstoptime; + int foundcachedcount = CACHED_BLOCK_COUNT; if (meetingStartSnapshot.blockHeight < Params().CHECK_START_BLOCKCOUNT) { @@ -848,6 +847,7 @@ bool CConsensusAccountPool::checkNewBlock(const std::shared_ptr p { LogPrintf("[CConsensusAccountPool::checkNewBlock] The number of meetings recorded in the block is inconsistent with the number of meetings in the snapshot,PeriodCount=%d,cachedsnapshot PeriodCount=%d,The number of people removed from the blacklist=%d \n", pblock->nPeriodCount,cachedsnapshot.curCandidateIndexList.size(), accounts.size()); + /* if (Params().NetworkIDString() == CBaseChainParams::TESTNET) { if (blockHeight >= 32050 && blockHeight <= 32070) @@ -859,6 +859,7 @@ bool CConsensusAccountPool::checkNewBlock(const std::shared_ptr p } } else + */ return false; } @@ -871,11 +872,24 @@ bool CConsensusAccountPool::checkNewBlock(const std::shared_ptr p consensusList.push_back(tmpaccount); } - bool result = CDpocMining::Instance().GetMeetingList(meetingstarttime, consensusList); - if (result) + uint64_t meetingstarttime = 0; + if (!bReboot) + { + meetingstarttime = meetingStartSnapshot.meetingstoptime; + } + else + { + meetingstarttime = pblock->nPeriodStartTime; + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] reboot meetingstarttime =%d", pblock->nPeriodStartTime); + bReboot = false; + } + //bool result = CDpocMining::Instance().GetMeetingList(meetingstarttime, consensusList); + //if (result) + if (CDpocMining::Instance().GetMeetingList(meetingstarttime, consensusList)) { //Print the public key HASH index value of this round sequentially - LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] startime %d meeting pockager sort:\n", pblock->nPeriodStartTime); + //LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] startime %d meeting pockager sort:\n", pblock->nPeriodStartTime); + LogPrintf("[CConsensusAccountPool::GetTimeoutIndexs] meetingstarttime=%d blockstartime=%d meeting pockager sort:\n", meetingstarttime, pblock->nPeriodStartTime); std::list>::iterator consensusListIt; for (consensusListIt = consensusList.begin(); consensusListIt != consensusList.end(); consensusListIt++) { @@ -1072,6 +1086,14 @@ bool CConsensusAccountPool::verifyDPOCBlock(const std::shared_ptr for (const CTxOut& txout : tx->vout) { + if ((txout.txType != TXOUT_NORMAL) && (txout.txType != TXOUT_CAMPAIGN)) + { + LogPrintf("[CConsensusAccountPool::verifyDPOCBlock()] coinbase TX has wrong Type reurn false and MisbeHaving\n"); + + rejectreason = PUNISH_BLOCK; + return false; + } + if (txout.GetCampaignType() == TYPE_CONSENSUS_RETURN_DEPOSI) { uint16_t pkindex; diff --git a/src/dpoc/TimeService.cpp b/src/dpoc/TimeService.cpp index b381bd3..21b1c68 100644 --- a/src/dpoc/TimeService.cpp +++ b/src/dpoc/TimeService.cpp @@ -532,7 +532,7 @@ bool CTimeService::WriteTimeServerFile() "timeserver=ntp2.aliyun.com\n" \ "timeserver=ntp3.aliyun.com\n" \ "timeserver=ntp4.aliyun.com\n" \ - "timeserver=ntp5.aliyun.com\n" \ + "timeserver=ntp5.aliyun.com\n" \ "timeserver=ntp6.aliyun.com\n" \ "timeserver=ntp7.aliyun.com\n" \ "timeserver=time1.aliyun.com\n" \ @@ -542,9 +542,25 @@ bool CTimeService::WriteTimeServerFile() "timeserver=time5.aliyun.com\n" \ "timeserver=time6.aliyun.com\n" \ "timeserver=time7.aliyun.com\n" \ - "#Shanghai telecom\n" \ + "#Shanghai\n" \ "timeserver=ntp.api.bz\n" \ - "timeserver=ntp.gwadar.cn\n"; + "timeserver=ntp.gwadar.cn\n" \ + "#North America\n" \ + "timeserver=ntp1.cmc.ec.gc.ca\n" \ + "timeserver=ntp.ucsd.edu\n" \ + "timeserver=ntp-1.mcs.anl.gov\n" \ + "#Russia\n" \ + "timeserver=ntp.psn.ru\n" \ + "#Korea\n" \ + "timeserver=time.nuri.net\n" \ + "#Australia\n" \ + "timeserver=ntp.adelaide.edu.au\n" \ + "#Europe\n" \ + "timeserver=ntp.obspm.fr\n" \ + "timeserver=ntp.univ-lyon1.fr\n" \ + "#England\n" \ + "timeserver=ntp0.uk.uu.net\n" \ + "timeserver=ntp1.uk.uu.netn\n"; if (boost::filesystem::exists(strFilePath)) { diff --git a/src/init.cpp b/src/init.cpp index 41c4d5b..1d54ac8 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1609,6 +1609,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } } + // As LoadBlockIndex can take several minutes, it's possible the user // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. @@ -1634,6 +1635,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("No wallet support compiled in!\n"); #endif + if (IsArgSet("-pswd")) { + pwalletMain->curstrWalletPassphrase.reserve(100); + pwalletMain->curstrWalletPassphrase = GetArg("-pswd", "").c_str(); + } + + // ********************************************************* Step 9: data directory maintenance // if pruning, unset the service bit and perform the initial blockstore prune diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index c491360..b05075a 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -739,8 +739,8 @@ bool AreIPCStandard(const CTransaction& tx, CValidationState &state) if (txout.tokenRegLabel.accuracy < 0 || txout.tokenRegLabel.accuracy > 8) return state.DoS(100, false, REJECT_INVALID, "bad-Token-Reg-accuracy(must be 0-8)"); - if (txout.tokenRegLabel.totalCount > getTokenAllcoins(txout.tokenRegLabel.accuracy)) - return state.DoS(100, false, REJECT_INVALID, "bad-Token-Reg-totalCount(can't larger than Ten billion)"); + if (txout.tokenRegLabel.totalCount > TOKEN_MAX_VALUE) + return state.DoS(100, false, REJECT_INVALID, "bad-Token-Reg-totalCount"); if (pIPCCheckMaps->TokenSymbolMap.count(txout.tokenRegLabel.getTokenSymbol()) > 0 && pIPCCheckMaps->TokenSymbolMap[txout.tokenRegLabel.getTokenSymbol()].first != tx.GetHash()) @@ -788,9 +788,11 @@ bool AreIPCStandard(const CTransaction& tx, CValidationState &state) tokenoutCount++; break; - default: + case 0: continue; break; + default: + return state.DoS(100, false, REJECT_INVALID, "can't-support-output-Type"); } } diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index a6e4697..c72fbd0 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -435,12 +435,12 @@ class CTxOut } break; - - default: case 0: READWRITE(coinbaseScript); - break; + default: + return; + } READWRITE(*(CScriptBase*)(&scriptPubKey)); diff --git a/src/qt/cmessagebox.cpp b/src/qt/cmessagebox.cpp index 72ecd52..8f91dc5 100644 --- a/src/qt/cmessagebox.cpp +++ b/src/qt/cmessagebox.cpp @@ -1,6 +1,7 @@ #include "cmessagebox.h" #include "forms/ui_cmessagebox.h" #include "dpoc/DpocInfo.h" +#include "log/stateinfo.h" CMessageBox::CMessageBox(QWidget *parent) : QDialog(parent), diff --git a/src/qt/ecoincreatedialog.cpp b/src/qt/ecoincreatedialog.cpp index 324a54c..22f615a 100644 --- a/src/qt/ecoincreatedialog.cpp +++ b/src/qt/ecoincreatedialog.cpp @@ -192,6 +192,7 @@ void ecoincreatedialog::on_CreateeCoinButton_pressed() ui->tiplabel->setText(tr("Token label size too large.")); return; } + int accuracy = ui->comboBox->currentIndex(); QList recipients; SendCoinsRecipient recipient; @@ -208,16 +209,32 @@ void ecoincreatedialog::on_CreateeCoinButton_pressed() recipients.append(recipient); fNewRecipientAllowed = false; QString add1 = ui->chooseaddBtn->text() ; - uint64_t amount1 = (uint64_t)coinNumText.toDouble(); - bool was_locked = model->getEncryptionStatus() == WalletModel::Locked; - if(!model->CheckPassword()) + uint64_t amount1 = (uint64_t)coinNumText.toULongLong(); + + if(amount1<=0||amount1>1000000000000000000) { - ui->tiplabel->setText(tr("password error")); + ui->tiplabel->setText(tr("coinNum (0,1000000000000000000]")); return ; } - if(amount1<=0||amount1>10000000000) + int iaccuracy = accuracy; + uint64_t iamount1= amount1; + while(iaccuracy--){ + if(iamount1>1000000000000000000) + { + ui->tiplabel->setText(tr("coinNum*10^accuracy must no more than 1000000000000000000")); + return ; + } + iamount1=iamount1*10; + } + if(iamount1<=0||iamount1>1000000000000000000) { - ui->tiplabel->setText(tr("coinNum (0,10000000000]")); + ui->tiplabel->setText(tr("coinNum*10^accuracy must no more than 1000000000000000000")); + return ; + } + bool was_locked = model->getEncryptionStatus() == WalletModel::Locked; + if(!model->CheckPassword()) + { + ui->tiplabel->setText(tr("password error")); return ; } // If wallet is still locked, unlock was failed or cancelled, mark context as invalid @@ -247,7 +264,7 @@ void ecoincreatedialog::on_CreateeCoinButton_pressed() ui->tiplabel->setText(tr("time error")); return ; } - int accuracy = ui->comboBox->currentIndex(); + QStringList data; QString coin_name = ui->coinname->text(); QString coin_md5 =ui->coinmd5->text(); @@ -260,6 +277,8 @@ void ecoincreatedialog::on_CreateeCoinButton_pressed() data<prepareeCoinsCreateTransaction(data,add1,currentTransaction, &ctrl); m_sendcoinerror = model->m_sendcoinerror; processSendCoinsReturn(prepareStatus, @@ -400,8 +419,8 @@ void ecoincreatedialog::slotTextChanged(const QString & text) QString textContent = text; QString numtexttemp = textContent; QString textContenttemp = numtexttemp.replace(",",""); - uint64_t maxLength = 10000000000; - uint64_t amount1 = (uint64_t)textContenttemp.toDouble(); + uint64_t maxLength = 1000000000000000000; + uint64_t amount1 = (uint64_t)textContenttemp.toULongLong(); if(amount1 > maxLength) { int position = ui->coinNum->cursorPosition(); diff --git a/src/qt/ecoinsendaffrimdialog.cpp b/src/qt/ecoinsendaffrimdialog.cpp index bab07bb..8901d74 100644 --- a/src/qt/ecoinsendaffrimdialog.cpp +++ b/src/qt/ecoinsendaffrimdialog.cpp @@ -43,7 +43,7 @@ void ecoinsendaffrimdialog::setMsg(QString name,QString num) m_num = num; std::string strname = name.toStdString(); int acc = model->GetAccuracyBySymbol(strname); - QString exp("10000000000|([0-9]{0,11}[\.][0-9]{0,"); + QString exp("1000000000000000000|([0-9]{0,19}[\.][0-9]{0,"); exp+=(QString::number(acc)); exp = exp + ("})"); QRegExp double_rx10000(exp); @@ -170,9 +170,6 @@ void ecoinsendaffrimdialog::on_sendecoinButton_pressed() ctrl.nConfirmTarget = 1; } - double val=strNumEdit.toDouble(); - - printf("%.8lf \n",val); std::string Txid = m_name.toStdString(); int acc = model->GetAccuracyBySymbol(Txid); @@ -182,23 +179,18 @@ void ecoinsendaffrimdialog::on_sendecoinButton_pressed() if(pointplace>0) { int afterpointnum = strNumEdit.size() - pointplace-1; - printf("%s \n",strNumEdit.toStdString().c_str()); strNumEdit.replace(QString("."), QString("")); - printf("%s \n",strNumEdit.toStdString().c_str()); - printf("intacc %d %d %d intacc \n",pointplace,afterpointnum,acc); acc =acc-afterpointnum; } while (acc>0) { strNumEdit+="0"; acc--; } - printf("%ld intedit \n",atol(strNumEdit.toStdString().c_str())); stringstream strValue; strValue << strNumEdit.toStdString().c_str(); - uint64_t intNumEdit;// = strNumEdit.toInt64();//atol(strNumEdit.toStdString().c_str());//strNumEdit.toLonglong();//static_cast(val); //(uint64_t)val; + uint64_t intNumEdit; strValue >> intNumEdit; - std::cout<<"prepareeCoinsSendCreateTransaction "< color: rgb(51, 51, 51); - 11 + 19 diff --git a/src/qt/forms/ipcdetails.ui b/src/qt/forms/ipcdetails.ui index 5d5d1f8..20d72ca 100644 --- a/src/qt/forms/ipcdetails.ui +++ b/src/qt/forms/ipcdetails.ui @@ -46,7 +46,7 @@ 0 0 776 - 931 + 935 @@ -63,7 +63,7 @@ - 580 + 590 16777215 diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index e38989f..91c568d 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -17,6 +17,7 @@ #include #include #include "log/log.h" +#include "log/stateinfo.h" #include #include "logondlg.h" #include "dpoc/DpocInfo.h" @@ -204,7 +205,10 @@ bool Intro::pickDataDirectory(logondlg* plogondlg ,std::string clienttype) m_notestpath = dataDir; if(clienttype == "test") dataDir+="/testnet3"; + + LOG_SETPATH(q2s(dataDir)); + STATE_SETPATH(q2s(dataDir)); m_datapath = q2s(dataDir); if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || @@ -235,6 +239,7 @@ bool Intro::pickDataDirectory(logondlg* plogondlg ,std::string clienttype) if(clienttype == "test") temp+="/testnet3"; LOG_SETPATH(q2s(temp)); + STATE_SETPATH(q2s(temp)); m_datapath = q2s(temp); LOG_WRITE(LOG_INFO,"fs::exists in if 1",dataDir.toStdString().c_str()); /* If current default data directory does not exist, let the user choose one */ diff --git a/src/qt/ipcdetails.cpp b/src/qt/ipcdetails.cpp index 54b0b11..538b316 100644 --- a/src/qt/ipcdetails.cpp +++ b/src/qt/ipcdetails.cpp @@ -106,6 +106,7 @@ void IpcDetails::resetinfo(int index) ui->label_t7->setText(back.at(6)); ui->label_t8->setText(back.at(7)); + ui->label_t8->setAlignment(Qt::AlignLeft); ui->label_t9->setText(back.at(8)); ui->label_t10->setText(back.at(9)); diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index b5ebae4..0999f59 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -1453,9 +1453,14 @@ 提示:需要缴纳手续费及100ipc的交易费。 - coinNum (0,10000000000] - 数字资产数量范围 (0,10000000000] + coinNum (0,1000000000000000000] + 数字资产数量范围 (0,1000000000000000000] + + coinNum*10^accuracy must no more than 1000000000000000000 + 数字资产数量*10^精度不能超过1000000000000000000 + + The recipient address is not valid. Please recheck. 接收人地址无效。请重新检查。 @@ -6466,7 +6471,7 @@ Is Bookkeeping,Confirm exit? - 正在记账,确认退出吗? + 正在记账,退出后财产可能损失,确认退出吗? @@ -6553,7 +6558,7 @@ Copy &Link Location - 复制链接位置(&L) + 复制链接位置(&L) &Paste @@ -6586,6 +6591,10 @@ &Copy 复制(&C) + + Copy &Link Location + 复制链接位置(&L) + &Paste 粘贴(&P) @@ -6622,9 +6631,9 @@ 复制(&C) - - Copy &Link Location - 复制链接位置(&L) + + Copy &Link Location + 复制链接位置 diff --git a/src/qt/log/log.cpp b/src/qt/log/log.cpp index c7edfc9..4c558da 100644 --- a/src/qt/log/log.cpp +++ b/src/qt/log/log.cpp @@ -127,14 +127,12 @@ void Log::CreateLogFile(bool newFile) } else { m_file = fopen(logFile.c_str(), "a+"); } - if (m_file == NULL) { printf("create log file [%s] error\n", logFile.c_str()); return; } fprintf(m_file, "%02d:%02d:%02d log start..........\n", now->tm_hour, now->tm_min, now->tm_sec); - } void LOG_WRITE(LOG_LEVEL level,const char* str1,\ const char* str2, \ diff --git a/src/qt/log/stateinfo.cpp b/src/qt/log/stateinfo.cpp new file mode 100644 index 0000000..26f93db --- /dev/null +++ b/src/qt/log/stateinfo.cpp @@ -0,0 +1,152 @@ +#include "stateinfo.h" +#include "log.h" +#include +#include +#include +#include +#include "util.h" +#ifdef WIN32 +#include +const std::string DIRECTORY_SEPARATOR = "\\"; +#else +const std::string DIRECTORY_SEPARATOR = "/"; +#endif + +//static Log g_log; +static stateinfo state_info; +stateinfo::stateinfo() +{ +} + +stateinfo::~stateinfo() +{ +} + +#ifdef WIN32 + + +static void CreateDir(const std::string& path) +{ + CreateDirectoryA(path.c_str(), NULL); +} + +#else +#include +#include +#include + +#define MAX_PATH 512 + +static void CreateDir(const std::string& path) +{ + mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +} + +#endif + +void STATE_SETPATH(std::string path) +{ + std::cout<<"SetDataPath"< +#include +#include +using namespace std; +#include +#include +#include +#include + + +class stateinfo { +public: + stateinfo(); + ~stateinfo(); + + void Init(); + void write(const char* str); + void clear(); + QString read(); + void SetDataPath(std::string path){m_datapath=path+"/";} +private: + void CreateLogFile(); + std::string GetDataPath(){return m_datapath;} + std::string m_datapath; + // QFile* m_file; + QString m_file; + int m_day; +}; + + + +void STATE_WRITE(const char* str1); +void STATE_SETPATH(std::string); +QString STATE_READ(); +void STATE_CLEAR(); + + + + +#endif // STATEINFO + diff --git a/src/qt/passwordsettingwidget.cpp b/src/qt/passwordsettingwidget.cpp index 52ff2e3..d4750ca 100644 --- a/src/qt/passwordsettingwidget.cpp +++ b/src/qt/passwordsettingwidget.cpp @@ -31,6 +31,8 @@ PasswordSettingWidget::PasswordSettingWidget(Mode _mode,QWidget *parent) : ui->passEdit1->installEventFilter(this); ui->passEdit2->installEventFilter(this); ui->passEdit3->installEventFilter(this); + + LOG_WRITE(LOG_INFO,"passwordsettingwidget::passwordsettingwidget",QString::number(_mode).toStdString().c_str()); switch(_mode) { case Encrypt: // Ask passphrase x2 @@ -78,6 +80,8 @@ void PasswordSettingWidget::setModel(WalletModel *_model) } void PasswordSettingWidget::on_pushButton_pressed() { + LOG_WRITE(LOG_INFO,"passwordsettingwidget::on_pushBUtton_pressed",QString::number(mode).toStdString().c_str()); + if(mode == Encrypt){ if(ui->passEdit1->text().isEmpty()){ ui->label_error->setText(tr("please set password")); @@ -134,6 +138,7 @@ void PasswordSettingWidget::on_pushButton_pressed() { if(model->setWalletEncrypted(true, newpass1)) { + LOG_WRITE(LOG_INFO,"passwordsettingwidget::Encrypted",QString::number(getTag()).toStdString().c_str()); if(2 == getTag()) { Q_EMIT openSendCoinsAffrimwidget(); @@ -180,11 +185,11 @@ void PasswordSettingWidget::on_pushButton_pressed() { QSettings settings; iPasswordErrorNum = settings.value("PasswordErrorNum").toInt(); - LOG_WRITE(LOG_INFO,"iPasswordErrorNum",QString::number(iPasswordErrorNum).toStdString().c_str()); + LOG_WRITE(LOG_INFO,"passwordsettingwidget::iPasswordErrorNum",QString::number(iPasswordErrorNum).toStdString().c_str()); timenum = timeService.GetCurrentTimeSeconds(); if(iPasswordErrorNum >= 5){ QString locktime = settings.value("locktime").toString(); - LOG_WRITE(LOG_INFO,"timeService::GetCurrentTimeSeconds",QString::number(timenum).toStdString().c_str(),"locktime",locktime.toStdString().c_str()); + LOG_WRITE(LOG_INFO,"passwordsettingwidget::timeService::GetCurrentTimeSeconds",QString::number(timenum).toStdString().c_str(),"locktime",locktime.toStdString().c_str()); QString strtimenum = QString::number(timenum); if(locktime > strtimenum){ CMessageBox msg; @@ -200,6 +205,8 @@ void PasswordSettingWidget::on_pushButton_pressed() } if(model->changePassphrase(oldpass, newpass1)) { + LOG_WRITE(LOG_INFO,"passwordsettingwidget::changePassphrase",QString::number(getTag()).toStdString().c_str()); + settings.setValue("PasswordErrorNum", 0); ui->label_error->setText(tr("Wallet passphrase was successfully changed.")); if(2 == getTag()) @@ -220,7 +227,7 @@ void PasswordSettingWidget::on_pushButton_pressed() ui->label_error->setText(tr("Wallet encryption failed,The passphrase entered for the wallet decryption was incorrect.")); } }else{ - LOG_WRITE(LOG_INFO,"no lock"); + LOG_WRITE(LOG_INFO,"passwordsettingwidget::no lock"); } } else diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 8a3a371..ca81dd6 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -28,6 +28,7 @@ #include #include "config/bitcoin-config.h" /* for USE_QRCODE */ #include +#include "log/stateinfo.h" #define SEND_CONFIRM_DELAY 3 @@ -147,6 +148,7 @@ void SendCoinsDialog::on_AddAdsButton_pressed() } void SendCoinsDialog::on_GoSettingBtn_pressed() { + QString a = ui->ReceiverEdit->text(); QString label = ui->labelEdit->text(); QString b = ui->CoinEdit->text(); diff --git a/src/qt/tallyaccount.cpp b/src/qt/tallyaccount.cpp index 69d7b48..fdbe8fd 100644 --- a/src/qt/tallyaccount.cpp +++ b/src/qt/tallyaccount.cpp @@ -15,10 +15,12 @@ #include "dpoc/DpocInfo.h" #include #include +#include "log/stateinfo.h" using namespace std; #include "cmessagebox.h" #include #include +#include "log/stateinfo.h" extern bool g_bIpchainShutdown; class TextNet : public QObject @@ -60,6 +62,9 @@ TallyAccount::TallyAccount(QWidget *parent) : m_time = 0; ui->tableWidget->verticalHeader()->setVisible(false); ui->label_punishment->setText(""); + + + } TallyAccount::~TallyAccount() @@ -67,6 +72,17 @@ TallyAccount::~TallyAccount() killTimer(m_nTimerId); delete ui; } +void TallyAccount::SetnewInfo() +{ + QString info; + info =STATE_READ(); + LOG_WRITE(LOG_INFO,"state",info.toStdString().c_str()); + if("STATE = OFF" == info) + { + ui->label_punishment->setText(tr("Quitting, please be patient.")); + ui->pushButton_outaccount->hide(); + } +} void TallyAccount::setinfo( WalletModel::keepupaccountInfo info) { info_.Add_=info.Add_; @@ -100,11 +116,13 @@ WalletModel::keepupaccountInfo TallyAccount::getinfo() } void TallyAccount::on_pushButton_outaccount_pressed() { + WalletModel::keepupaccountInfo accinfo = getinfo(); Q_EMIT next(accinfo.Add_,accinfo.Coin_); } void TallyAccount::timerEvent( QTimerEvent *event ) { + if(!CDpocInfo::Instance().IsHasLocalAccount()) { return; diff --git a/src/qt/tallyaccount.h b/src/qt/tallyaccount.h index 7de6bdf..6dc4806 100644 --- a/src/qt/tallyaccount.h +++ b/src/qt/tallyaccount.h @@ -19,6 +19,7 @@ class TallyAccount : public QWidget explicit TallyAccount(QWidget *parent = 0); ~TallyAccount(); void setinfo(WalletModel::keepupaccountInfo info); + void SetnewInfo(); WalletModel::keepupaccountInfo getinfo(); void setModel(WalletModel *_model); void setfinishinfo(); diff --git a/src/qt/tallyapply.cpp b/src/qt/tallyapply.cpp index c9fc4e0..ae8ba5d 100644 --- a/src/qt/tallyapply.cpp +++ b/src/qt/tallyapply.cpp @@ -6,6 +6,7 @@ #include "guiutil.h" #include #include "log/log.h" +#include "log/stateinfo.h" #include "intro.h" TallyApply::TallyApply(QWidget *parent) : @@ -85,6 +86,10 @@ void TallyApply::processSendCoinsReturn(const WalletModel::SendCoinsReturn &send ui->label_errmsg->setText(tr("PaymentRequestExpired")); break; case WalletModel::OK: + + STATE_CLEAR(); + STATE_WRITE("STATE = ON"); + LOG_WRITE(LOG_INFO,"tallyapply","OK"); ui->label_errmsg->setText(tr("")); default: @@ -96,6 +101,8 @@ void TallyApply::processSendCoinsReturn(const WalletModel::SendCoinsReturn &send } void TallyApply::on_pushButton_apply_pressed() { + + m_error = ""; if(!model || !model->getOptionsModel()) { @@ -273,6 +280,7 @@ void TallyApply::on_pushButton_address_pressed() } void TallyApply::resetinfo() { + ui->coinEdit->setText("20000"); ui->label_errmsg->setText(""); ui->pushButton_address->setText(tr("select address")); } diff --git a/src/qt/tallyoutaccount.cpp b/src/qt/tallyoutaccount.cpp index f2011d1..85b92d3 100644 --- a/src/qt/tallyoutaccount.cpp +++ b/src/qt/tallyoutaccount.cpp @@ -1,17 +1,18 @@ #include "tallyoutaccount.h" #include "forms/ui_tallyoutaccount.h" - #include "optionsmodel.h" #include "ipchainunits.h" #include "wallet/coincontrol.h" #include "guiutil.h" #include "log/log.h" +#include "log/stateinfo.h" TallyOutAccount::TallyOutAccount(QWidget *parent) : QWidget(parent), ui(new Ui::TallyOutAccount) { ui->setupUi(this); ui->label_errmsg->setText(""); + } TallyOutAccount::~TallyOutAccount() @@ -121,6 +122,9 @@ void TallyOutAccount::on_pushButton_OK_pressed() } else { + + STATE_CLEAR(); + STATE_WRITE("STATE = OFF"); fNewRecipientAllowed = true; Q_EMIT next(true); } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 30b7a58..a7862c1 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -334,7 +334,7 @@ class TransactionTablePriv } } - LOG_WRITE(LOG_INFO,"G",QString::number(mulMaplastdellist.size()).toStdString().c_str()); + LOG_WRITE(LOG_INFO,"G-mulMaplastdellist",QString::number(mulMaplastdellist.size()).toStdString().c_str()); for(multimap::iterator irt =mulMaplastdellist.begin();irt!=mulMaplastdellist.end();irt++) { diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index ce50222..3bea0fc 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -7,7 +7,6 @@ #include "ipchaingui.h" #include "walletview.h" #include "settingwidget.h" -#include "passwordsettingwidget.h" #include "sendcoinsaffrimwidget.h" #include #include @@ -29,7 +28,7 @@ #include "walletpagebuttons.h" #include "log/log.h" #include "transactiontablemodel.h" - +#include "log/log.h" WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui) : QFrame(_gui), gui(_gui), @@ -71,12 +70,10 @@ WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui) noWallet->setAlignment(Qt::AlignCenter); walletStack->addWidget(noWallet); - } WalletFrame::~WalletFrame() { - LOG_WRITE(LOG_INFO,"WalletFrame::~WalletFrameWalletFrame::~WalletFrameWalletFrame::~WalletFrame"); delete sendpage; delete recvpage ; @@ -123,14 +120,14 @@ bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) WalletView *walletView = new WalletView(platformStyle, this); walletView->setBitcoinGUI(gui); walletView->setClientModel(clientModel); - walletView->setWalletModel(walletModel); + this->walletmd = walletModel; this->clientmd = clientModel; - walletView->gotoOverviewPage(); walletStack->addWidget(walletView); + walletStack->setCurrentWidget(walletView); mapWalletViews[name] = walletView; - + walletView->setWalletModel(walletModel); // Ensure a walletView is able to show the main window connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index b4441d7..cbebd38 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -22,7 +22,6 @@ class WalletModel; class WalletView; class Settingwidget; -class PasswordSettingWidget; class SendCoinsAffrimWidget; class AddressTableModel; class InfoWidget; @@ -93,7 +92,6 @@ class WalletFrame : public QFrame InfoWidget* ChainInfoPage; - PasswordSettingWidget* PasswordSettingPage; SendCoinsAffrimWidget* SendCoinsAffrimPage; sendhistory* sendpage =NULL; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index f0566cd..5149593 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -301,10 +301,9 @@ void WalletView::setWalletModel(WalletModel *_walletModel) setdialogPage->setModel(_walletModel); pTallySelectAddressPage->setWalletModel(_walletModel); pTallyApply->setModel(_walletModel); - pTallyAccount->setModel(_walletModel); pTallyOutAccount->setModel(_walletModel); - ecoinDialogPage->setModel(_walletModel); + pTallyAccount->setModel(_walletModel); if (_walletModel) @@ -433,6 +432,7 @@ void WalletView::gotoSetPage() } void WalletView::gotoTallyPage() { + try{ if(!CDpocInfo::Instance().IsHasLocalAccount()) { @@ -445,7 +445,11 @@ void WalletView::gotoTallyPage() LOG_WRITE(LOG_INFO,"CDpocInfo::Instance().IsHasLocalAccount true "); pTallyAccount->resettime(); if(tallyDialogPageStack&&pTallyAccount) + { + pTallyAccount->SetnewInfo(); tallyDialogPageStack->setCurrentWidget(pTallyAccount); + + } } if(tallyDialogPage) setCurrentWidget(tallyDialogPage); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 79e00c2..d1d93e4 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -80,6 +80,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendmany", 1, "amounts" }, { "sendmany", 2, "minconf" }, { "sendmany", 4, "subtractfeefrom" }, + { "sendtokenmany", 2, "amounts" }, { "addmultisigaddress", 0, "nrequired" }, { "addmultisigaddress", 1, "keys" }, { "createmultisig", 0, "nrequired" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 30996c8..e046585 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -545,6 +545,9 @@ UniValue createrawtransaction(const JSONRPCRequest& request) multimap::iterator m; CAmount nVinValue = 0; CScript strScriptnewout; + + set > setCoins; + setCoins.clear(); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); @@ -588,6 +591,11 @@ UniValue createrawtransaction(const JSONRPCRequest& request) CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); rawTx.vin.push_back(in); + if (pwalletMain->mapWallet.count(txid) == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txid can't found"); + CWalletTx& prewtx = pwalletMain->mapWallet[txid]; + const CWalletTx* pprewtx = &prewtx; + setCoins.insert(make_pair(pprewtx, nOutput)); } CAmount totalvalue = 0; set setNormalAddress; @@ -750,8 +758,8 @@ UniValue createrawtransaction(const JSONRPCRequest& request) regLabel.hash.SetHex(TokenHashObj.get_str()); memcpy(regLabel.label, TokenLabelObj.get_str().c_str(), TokenLabelObj.get_str().size() > 16 ? 16 : TokenLabelObj.get_str().size()); regLabel.issueDate = TokenIssueObj.get_int(); - regLabel.totalCount = TokenTotalCountObj.get_int(); - regLabel.value = TokenTotalCountObj.get_int(); + regLabel.totalCount = TokenTotalCountObj.get_int64(); + regLabel.value = TokenTotalCountObj.get_int64(); regLabel.accuracy = TokenAccuracyObj.get_int(); out = CTxOut(nAmount, scriptPubKey, regLabel,strtxLabel); break; @@ -776,7 +784,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) setTokenAddress.insert(make_pair(address, tokenlabel.getTokenSymbol())); - tokenlabel.value = TokenValueObj.get_int(); + tokenlabel.value = TokenValueObj.get_int64(); tokenlabel.accuracy = TokenAccuracyObj.get_int(); out = CTxOut(nAmount, scriptPubKey, tokenlabel,strtxLabel); break; @@ -788,7 +796,8 @@ UniValue createrawtransaction(const JSONRPCRequest& request) out = CTxOut(nAmount, scriptPubKey); break; default: - printf("Unknown Transaction Type, handled as a normal Tx\n"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txType out of range"); + } rawTx.vout.push_back(out); @@ -802,15 +811,16 @@ UniValue createrawtransaction(const JSONRPCRequest& request) CTxOut newout(nvalue1, strScriptnewout); rawTx.vout.push_back(newout); + + if (!(pwalletMain->DummySignTx(rawTx, setCoins))) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "DummySignTx erro!"); + } unsigned int nBytes = GetVirtualTransactionSize(rawTx); - CAmount fee = pwalletMain->GetMinimumFee(nBytes, 8, mempool); + CAmount fee = pwalletMain->GetMinimumFee(nBytes, 8, mempool) * 2; //Add a change if (TxType == 4) { - std::cout << " [createrawtransaction ] type =4 ,nVinValue = " << nVinValue << " ,fee= " << fee << std::endl; CAmount newoutvalue = nVinValue - totalvalue - AmountFromValue(10) - fee; - - std::cout << " [createrawtransaction ] newoutvalue = " << newoutvalue << std::endl; rawTx.vout[rawTx.vout.size() - 1].nValue = newoutvalue; } else @@ -819,7 +829,6 @@ UniValue createrawtransaction(const JSONRPCRequest& request) rawTx.vout[rawTx.vout.size() - 1].nValue = newoutvalue; } - std::cout << "[ createrawtransaction ] Done !"<< std::endl; return EncodeHexTx(rawTx); } diff --git a/src/validation.cpp b/src/validation.cpp index f975355..5cea302 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2384,39 +2384,39 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Check the block hash of the first block - if (pindex->nHeight == 1) - { - if ((Params().NetworkIDString() == CBaseChainParams::TESTNET)) - { - if (NULL == pindex->phashBlock) - { - LogPrintf(" [connectblock error] phashBlock = NULL! \n"); - return false; - } - - if ( pindex->GetBlockHash().GetHex() != "014c41d9c6448c2d6a065a6788498c9fe639a33b121bf4ab43ca84b9696a2d53") - { - LogPrintf(" Check the first block hash error %s \n", pindex->GetBlockHash().GetHex()); - return state.DoS(100, error("ConnectBlock(): failed connect to Height 1"), REJECT_INVALID, "bad-block-hash"); - } - } - else - { - if (NULL == pindex->phashBlock) - { - LogPrintf(" [connectblock error] phashBlock = NULL! \n"); - return false; - } - - if (pindex->GetBlockHash().GetHex() != "47d1788fa6497f868f78ddf579b2e7d8aca60f554d425ead9533af02ed35136d") - { - LogPrintf(" Check the first block hash error %s \n", pindex->GetBlockHash().GetHex()); - return state.DoS(100, error("ConnectBlock(): failed connect to Height 1"), REJECT_INVALID, "bad-block-hash"); - } - - } - - } +// if (pindex->nHeight == 1) +// { +// if ((Params().NetworkIDString() == CBaseChainParams::TESTNET)) +// { +// if (NULL == pindex->phashBlock) +// { +// LogPrintf(" [connectblock error] phashBlock = NULL! \n"); +// return false; +// } +// +// if ( pindex->GetBlockHash().GetHex() != "014c41d9c6448c2d6a065a6788498c9fe639a33b121bf4ab43ca84b9696a2d53") +// { +// LogPrintf(" Check the first block hash error %s \n", pindex->GetBlockHash().GetHex()); +// return state.DoS(100, error("ConnectBlock(): failed connect to Height 1"), REJECT_INVALID, "bad-block-hash"); +// } +// } +// else +// { +// if (NULL == pindex->phashBlock) +// { +// LogPrintf(" [connectblock error] phashBlock = NULL! \n"); +// return false; +// } +// +// if (pindex->GetBlockHash().GetHex() != "47d1788fa6497f868f78ddf579b2e7d8aca60f554d425ead9533af02ed35136d") +// { +// LogPrintf(" Check the first block hash error %s \n", pindex->GetBlockHash().GetHex()); +// return state.DoS(100, error("ConnectBlock(): failed connect to Height 1"), REJECT_INVALID, "bad-block-hash"); +// } +// +// } +// +// } //end @@ -5476,8 +5476,11 @@ class CMainCleanup CFeeRate txminfee(50000);//DEFAULT_TRANSACTION_FEE bool VerifyFee(const CTransaction& tx, CAmount nneedFee) //Check rates { + CAmount nFee = nneedFee; + if (tx.GetTxType() == TXOUT_TOKENREG) + nFee -= 100 * COIN; unsigned int nBytes= ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - CFeeRate curtxfee(nneedFee, nBytes); + CFeeRate curtxfee(nFee, nBytes); if (g_bStdCout) { std::cout << "txhash---fee-- : " << tx.GetHash().ToString() << " " << nneedFee << " " << nBytes << std::endl; diff --git a/src/validation.h b/src/validation.h index af92369..dce77bc 100644 --- a/src/validation.h +++ b/src/validation.h @@ -57,6 +57,7 @@ class IPCCheckMaps; #define IPC_ISSUE_VALUE 96000000 #define TOKEN_REGTIME_BOUNDARY 1506787200 //The earliest date of registration of scrip: 20171001 time stamp #define TOKEN_MAX_COINS 10000000000 //A total of 10 billion yuan will be issued +#define TOKEN_MAX_VALUE 1000000000000000000 #define ENABLE_TXLEVELDB 1 diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 09bfb05..d0401e2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -861,7 +861,7 @@ UniValue decodeinfobyaddress(const JSONRPCRequest& request) "\nArguments:\n" "1. \"address\" (string) The wallet address\n" "\nExamples:\n" - "\decode the address\n" + "\ndecode the address\n" + HelpExampleCli("decodeinfobyaddress", "\"address\"") ); CBitcoinAddress address(request.params[0].get_str()); @@ -1132,7 +1132,7 @@ UniValue listreward(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw runtime_error( "listreward \n" - "\listreward .\n" + "\nlistreward .\n" "\nResult:\n" "\"\" (string) the list.\n" "\nExamples:\n" @@ -1979,6 +1979,103 @@ UniValue sendmany(const JSONRPCRequest& request) return wtx.GetHash().GetHex(); } +UniValue sendtokenmany(const JSONRPCRequest& request) +{ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 3) + throw runtime_error( + "sendtokenmany \"fromaccount\" \"tokensymbol\" {\"address\":amount,...}\n" + "\nSend multiple times. Amounts are double-precision floating point numbers." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) DEPRECATED. The account to send the funds from. Should be \"\" for the default account\n" + "2. \"tokensymbol\" (string, required) Tokensymbol which you want to send\n" + "3. \"amounts\" (string, required) A json object with addresses and amounts\n" + " {\n" + " \"address\":amount (numeric or string) The ipchain address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" + " ,...\n" + " }\n" + "\nResult:\n" + "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli("sendtokenmany", "\"\" \"tokensymbol\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + string strAccount = AccountFromValue(request.params[0]); + string tokensymbol = request.params[1].get_str(); + if (tokenDataMap.count(tokensymbol) == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Can't found 'accuracy' of the Token"); + + int tokenaccuracy = tokenDataMap[tokensymbol].accuracy; + std::cout << "tokenaccuracy = " << tokenaccuracy << std::endl; + UniValue sendTo = request.params[2].get_obj(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + + set setAddress; + vector vecSend; + vecSend.clear(); + uint64_t totalAmount = 0; + vector keys = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, keys) + { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid IPchain address: ") + name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + int64_t nAmount = TCoinsFromValue(sendTo[name_], tokenaccuracy); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid token amount for send"); + totalAmount += nAmount; + + bool fSubtractFeeFromAmount = false; + CAmount nipcAmount = 0; + uint64_t ntokenamount = nAmount; + CRecipientToken recipient = { scriptPubKey, nipcAmount, ntokenamount, fSubtractFeeFromAmount }; + vecSend.push_back(recipient); + } + + EnsureWalletIsUnlocked(); + + // Check funds + uint64_t nBalance = 0; + pwalletMain->GetSymbolbalance(tokensymbol, nBalance); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + std::cout << "totalAmount =" << totalAmount << std::endl; + + // Send + CReserveKey keyChange(pwalletMain); + CAmount nFeeRequired = 0; + int nChangePosRet = -1; + string strFailReason; + bool fCreated = pwalletMain->CreateTokenTransactionM(tokensymbol,vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); + std::cout << "33333333 " << std::endl; + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + CValidationState state; + if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), state)) { + strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason()); + throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); + } + + return wtx.GetHash().GetHex(); +} // Defined in rpc/misc.cpp extern CScript _createmultisig_redeemScript(const UniValue& params); @@ -4408,11 +4505,11 @@ UniValue loadwallet(const JSONRPCRequest& request) "\nArguments:\n" "1. \"filename\" (string, required) The wallet file\n" "\nExamples:\n" - "\load the wallet\n" + "\nload the wallet\n" + HelpExampleCli("loadwallet", "\"test\"") + - "\loadwallet the wallet\n" + "\nloadwallet the wallet\n" + HelpExampleCli("loadwallet", "\"test\"") + - "\loadwallet using the json rpc call\n" + "\nloadwallet using the json rpc call\n" + HelpExampleRpc("loadwallet", "\"test\"") ); @@ -4512,7 +4609,8 @@ static const CRPCCommand commands[] = { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} }, - { "wallet", "sendtoaddress", &sendtoaddress, true, { "address", "amount", "comment", "comment_to", "subtractfeefromamount" } }, + { "wallet", "sendtokenmany", &sendtokenmany, true, { "fromaccount", "tokensymbol", "amounts" } }, + { "wallet", "sendtoaddress", &sendtoaddress, true, { "address", "amount", "comment", "comment_to", "subtractfeefromamount" } }, { "wallet", "setaccount", &setaccount, true, {"address","account"} }, { "wallet", "settxfee", &settxfee, true, {"amount"} }, { "wallet", "signmessage", &signmessage, true, {"address","message"} }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ecd0900..d236097 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2465,7 +2465,6 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const void CWallet::AvailableNormalCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const { vCoins.clear(); - { LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) @@ -3041,7 +3040,6 @@ bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& bool CWallet::SelectNormalCoins(const vector& vAvailableCoins, const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const { vector vCoins(vAvailableCoins); - // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) { @@ -3060,6 +3058,7 @@ bool CWallet::SelectNormalCoins(const vector& vAvailableCoins, const CA CAmount nValueFromPresetInputs = 0; std::vector vPresetInputs; + if (coinControl) coinControl->ListSelected(vPresetInputs); BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) @@ -3079,6 +3078,7 @@ bool CWallet::SelectNormalCoins(const vector& vAvailableCoins, const CA } // remove preset inputs from vCoins + for (vector::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) { if (setPresetCoins.count(make_pair(it->tx, it->i))) @@ -3452,8 +3452,10 @@ bool CWallet::JoinCampaign(const CTxDestination &address, CAmount deposi, CWalle scriptChange = GetScriptForDestination(vchPubKey.GetID()); } + + // Take the first input script as the destination output used for the coin change address if (bFindChangScp) - scriptChange = scriptChangeFind; //Take the first input script and make the change script + scriptChange = scriptChangeFind; CTxOut newTxOut(nChange, scriptChange); // We do not move dust-change to fees, because the sender would end up paying more than requested. @@ -3884,8 +3886,9 @@ bool CWallet::ExitCampaign(CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& scriptChange = GetScriptForDestination(vchPubKey.GetID()); } + // Take the first input script as the destination output used for the coin change address if (bFindChangScp) - scriptChange = scriptChangeFind; //Take the first input script and make the change script + scriptChange = scriptChangeFind; CTxOut newTxOut(nChange, scriptChange); // We do not move dust-change to fees, because the sender would end up paying more than requested. @@ -4961,6 +4964,7 @@ bool CWallet::CreateNormalTransaction(const vector& vecSend, CWallet // Choose coins to use CAmount nValueIn = 0; setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) { strFailReason = _("Insufficient funds"); @@ -5021,8 +5025,10 @@ bool CWallet::CreateNormalTransaction(const vector& vecSend, CWallet scriptChange = GetScriptForDestination(vchPubKey.GetID()); } + + // Take the first input script as the destination output used for the coin change address if (bFindChangScp) - scriptChange = scriptChangeFind; //Take the first input script and make the change script + scriptChange = scriptChangeFind; CTxOut newTxOut(nChange, scriptChange); // We do not move dust-change to fees, because the sender would end up paying more than requested. @@ -5437,8 +5443,10 @@ bool CWallet::CreateIPCRegTransaction(std::string& strReglabel, const vector 0) { CTxOut txsout(recipient.nAmount, newscriptPubKey, stokenlabel, txLabel); - txNew.vout.push_back(txsout); + int ntokenchangepos = txNew.vout.size() - 1; + vector::iterator tposition = txNew.vout.begin() + ntokenchangepos; + if (ntokenchangepos > 0) + txNew.vout.insert(tposition, txsout); + else + txNew.vout.push_back(txsout); + } + break; + } + + + unsigned int nBytes = GetVirtualTransactionSize(txNew); + CTransaction txNewConst(txNew); + dPriority = txNewConst.ComputePriority(dPriority, nBytes); + + // Remove scriptSigs to eliminate the fee calculation dummy signatures + for (auto& vin : txNew.vin) { + vin.scriptSig = CScript(); + vin.scriptWitness.SetNull(); + } + + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); + // Require at least hard-coded AllowFree. + if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) + break; + } + + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); + + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + { + strFailReason = _("Transaction too large for fee policy"); + return false; + } + + if (nFeeRet >= nFeeNeeded) { + // Reduce fee to only the needed amount if we have change + // output to increase. This prevents potential overpayment + // in fees if the coins selected to meet nFeeNeeded result + // in a transaction that requires less fee than the prior + // iteration. + // TODO: The case where nSubtractFeeFromAmount > 0 remains + // to be addressed because it requires returning the fee to + // the payees and not the change output. + // TODO: The case where there is no change output remains + // to be addressed so we avoid creating too small an output. + if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount extraFeePaid = nFeeRet - nFeeNeeded; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + change_position->nValue += extraFeePaid; + nFeeRet -= extraFeePaid; + } + break; // Done, enough fee included. + } + + // Try to reduce change to include necessary fee + if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { + CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; + vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; + // Only reduce change if remaining amount is still a large enough output. + if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue -= additionalFeeNeeded; + nFeeRet += additionalFeeNeeded; + break; // Done, able to increase fee from change + } + } + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + if (sign) + { + CTransaction txNewConst(txNew); + int nIn = 0; + for (const auto& coin : setCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + //The added Token type input also needs to be signed + for (const auto& coin : setTokenCoins) + { + const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + SignatureData sigdata; + + if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + else { + UpdateTransaction(txNew, nIn, sigdata); + } + + nIn++; + } + } + // Embed the constructed transaction data in wtxNew. + wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + + // Limit size + if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + { + strFailReason = _("Transaction too large"); + return false; + } + } + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + LockPoints lp; + CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + std::string errString; + if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + strFailReason = _("Transaction has too long of a mempool chain"); + return false; + } + } + return true; +} + +bool CWallet::CreateTokenTransactionM(std::string& tokensymbol, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl, bool sign) +{ + nFeeRet = 0; + CAmount nValue = 0; + uint64_t curTokenBalance = 0; + uint64_t TokenValue = 0; + GetSymbolbalance(tokensymbol, curTokenBalance); + + int nChangePosRequest = nChangePosInOut; + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0 || TokenValue < 0 ||recipient.nvalue < 0) + { + strFailReason = _("Transaction amounts/tokenvalue must not be negative"); + return false; + } + nValue += recipient.nAmount; + TokenValue += recipient.nvalue; + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + + } + if (vecSend.empty()) + { + strFailReason = _("Transaction must have at least one recipient"); + return false; + } + if (TokenValue > curTokenBalance) + { + strFailReason = _("The Tokenvalue is too big,you have not enough Tokencoins."); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + txNew.nLockTime = chainActive.Height(); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + payTxFee = CFeeRate(DEFAULT_TRANSACTION_FEE * 2); + std::string txLabel = ""; + TokenLabel tokenlabel; + memcpy((char*)(tokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(tokenlabel.TokenSymbol)); + tokenlabel.value = 0; + if (tokenDataMap.count(tokenlabel.getTokenSymbol()) == 0) + { + strFailReason = _("Can't found 'accuracy' of the Token"); + return false; + } + tokenlabel.accuracy = tokenDataMap[tokenlabel.getTokenSymbol()].accuracy; + { + + set > setCoins; + set > setTokenCoins; + LOCK2(cs_main, cs_wallet); + { + std::vector vAvailableCoins; + AvailableNormalCoins(vAvailableCoins, true, coinControl); + std::vector vAvailableTokenCoins; + AvailableTokenCoins(vAvailableTokenCoins, true, coinControl); + + nFeeRet = 0; + // Start with no fee and loop until there is enough fee + while (true) + { + nChangePosInOut = nChangePosRequest; + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nValueToSelect = nValue; + if (nSubtractFeeFromAmount == 0) + nValueToSelect += nFeeRet; + double dPriority = 0; + // vouts to the payees + for (const auto& recipient : vecSend) + { + TokenLabel nowtokenlabel = tokenlabel; + nowtokenlabel.value = recipient.nvalue; + CTxOut txout(recipient.nAmount, recipient.scriptPubKey, nowtokenlabel, txLabel); + txNew.vout.push_back(txout); + } + + // Choose coins to use + CAmount nValueIn = 0; + setCoins.clear(); + if (!SelectNormalCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + bool bFindChangScp = false; + CScript scriptChangeFind; + for (const auto& pcoin : setCoins) + { + if (!bFindChangScp) + { + scriptChangeFind = pcoin.first->tx->vout[pcoin.second].scriptPubKey; + bFindChangScp = true; + } + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + //The coin age after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + const CAmount nChange = nValueIn - nValueToSelect; + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) + { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + // Take the first input script as the destination output used for the coin change address + if (bFindChangScp) + scriptChange = scriptChangeFind; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + { + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) + { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(dustRelayFee)) + { + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; + } + } + } + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(dustRelayFee)) + { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + if (nChangePosInOut == -1) + { + // Insert change txn at end position: + nChangePosInOut = txNew.vout.size(); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + strFailReason = _("Change index out of range"); + return false; + } + + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + // + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + for (const auto& coin : setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + + // Fill in dummy signatures for fee calculation. + if (!DummySignTx(txNew, setCoins)) { + strFailReason = _("Signing transaction failed"); + return false; + } + //Join an input + //Find the appropriate utxo input in the current sybmol balance + int icurinsize = txNew.vin.size(); + setTokenCoins.clear(); + if (!SelectTokenCoins(vAvailableTokenCoins, setTokenCoins, tokensymbol, TokenValue)) + { + strFailReason = _("Can't select enough TokenCoins!"); + return false; + } + bool getVinscriptPubKey = false; + uint64_t TotalvalueVin = 0; + CScript newscriptPubKey; + for (const auto& coin : setTokenCoins) + { + if (coin.first->tx->vout[coin.second].txType == 4) + TotalvalueVin += coin.first->tx->vout[coin.second].tokenRegLabel.totalCount; + else + TotalvalueVin += coin.first->tx->vout[coin.second].tokenLabel.value; + + if (!getVinscriptPubKey) + { + newscriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; + getVinscriptPubKey = true; } + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + std::numeric_limits::max() - (fWalletRbf ? 2 : 1))); + } + if (!DummySignTx(txNew, setTokenCoins, icurinsize)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + //After finding enough input, you need to compute the add token change + TokenLabel stokenlabel; + { + memcpy((char*)(stokenlabel.TokenSymbol), (char*)(tokensymbol.c_str()), sizeof(stokenlabel.TokenSymbol)); + stokenlabel.value = TotalvalueVin - TokenValue; + stokenlabel.accuracy = tokenlabel.accuracy; + + if (stokenlabel.value > 0) + { + CAmount nstokenamount = 0; + CTxOut txsout(nstokenamount, newscriptPubKey, stokenlabel, txLabel); + int ntokenchangepos = txNew.vout.size() - 1; + vector::iterator tposition = txNew.vout.begin() + ntokenchangepos; + if (ntokenchangepos > 0) + txNew.vout.insert(tposition, txsout); + else + txNew.vout.push_back(txsout); + } + + } + + unsigned int nBytes = GetVirtualTransactionSize(txNew); CTransaction txNewConst(txNew); dPriority = txNewConst.ComputePriority(dPriority, nBytes); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a8bac91..153e439 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -142,6 +142,14 @@ struct CRecipient bool fSubtractFeeFromAmount; }; +struct CRecipientToken +{ + CScript scriptPubKey; + CAmount nAmount; + uint64_t nvalue; + bool fSubtractFeeFromAmount; +}; + typedef std::map mapValue_t; @@ -883,8 +891,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //Create token reg trade bool CreateTokenRegTransaction(std::string& strReglabel, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); - //Create a token trade - bool CreateTokenTransaction(/*std::string& txid, int Index*/std::string& tokensymbol, uint64_t TokenValue, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + //Create a token trade -- Support only for one address transaction. + bool CreateTokenTransaction(std::string& tokensymbol, uint64_t TokenValue, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + bool CreateTokenTransactionM(std::string& tokensymbol, const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); //end bool address2pkhash(std::string& address,uint160& pkhash);