From ee9edc8829a707ebc4b6e195182e82f2098fbfea Mon Sep 17 00:00:00 2001 From: lateminer Date: Sun, 7 Jan 2018 19:17:53 +0300 Subject: [PATCH 1/8] Make Lore work on testnet, first attempt --- src/chainparams.cpp | 109 ++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0cc6cc8e5..cff842c91 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -20,22 +20,39 @@ using namespace std; static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) { + // Genesis block - CMutableTransaction txNew; - txNew.nTime = 1393221600; - txNew.vin.resize(1); - txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 0 << CScriptNum(42) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); - txNew.vout[0].SetEmpty(); + // MainNet: - CBlock genesis; - genesis.vtx.push_back(txNew); - genesis.hashPrevBlock.SetNull(); - genesis.hashMerkleRoot = uint256S("12630d16a97f24b287c8c2594dda5fb98c9e6c70fc61d44191931ea2aa08dc90"); - genesis.nVersion = 1; - genesis.nTime = 1393221600; - genesis.nBits = 0x1e0fffff; - genesis.nNonce = 164482; + //CBlock(hash=000001faef25dec4fbcf906e6242621df2c183bf232f263d0ba5b101911e4563, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=12630d16a97f24b287c8c2594dda5fb98c9e6c70fc61d44191931ea2aa08dc90, nTime=1393221600, nBits=1e0fffff, nNonce=164482, vtx=1, vchBlockSig=) + // Coinbase(hash=12630d16a9, nTime=1393221600, ver=1, vin.size=1, vout.size=1, nLockTime=0) + // CTxIn(COutPoint(0000000000, 4294967295), coinbase 00012a24323020466562203230313420426974636f696e2041544d7320636f6d6520746f20555341) + // CTxOut(empty) + // vMerkleTree: 12630d16a9 + + // TestNet: + + //CBlock(hash=0000724595fb3b9609d441cbfb9577615c292abf07d996d3edabc48de843642d, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=12630d16a97f24b287c8c2594dda5fb98c9e6c70fc61d44191931ea2aa08dc90, nTime=1393221600, nBits=1f00ffff, nNonce=216178, vtx=1, vchBlockSig=) + // Coinbase(hash=12630d16a9, nTime=1393221600, ver=1, vin.size=1, vout.size=1, nLockTime=0) + // CTxIn(COutPoint(0000000000, 4294967295), coinbase 00012a24323020466562203230313420426974636f696e2041544d7320636f6d6520746f20555341) + // CTxOut(empty) + // vMerkleTree: 12630d16a9 + + CMutableTransaction txNew; + txNew.nTime = nTime; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 0 << CScriptNum(42) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].SetEmpty(); + + CBlock genesis; + genesis.vtx.push_back(txNew); + genesis.hashPrevBlock.SetNull(); + genesis.nVersion = nVersion; + genesis.nTime = nTime; + genesis.nBits = nBits; + genesis.nNonce = nNonce; + genesis.hashMerkleRoot = BlockMerkleRoot(genesis); return genesis; } @@ -43,12 +60,6 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi * Build the genesis block. Note that the output of its generation * transaction cannot be spent since it did not originally exist in the * database. - * - * CBlock(hash=000001faef25dec4fbcf906e6242621df2c183bf232f263d0ba5b101911e4563, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=12630d16a97f24b287c8c2594dda5fb98c9e6c70fc61d44191931ea2aa08dc90, nTime=1393221600, nBits=1e0fffff, nNonce=164482, vtx=1, vchBlockSig=) - * Coinbase(hash=12630d16a9, ver=1, nTime=1393221600, vin.size=1, vout.size=1, nLockTime=0) - * CTxIn(COutPoint(0000000000, 4294967295), coinbase 00012a24323020466562203230313420426974636f696e2041544d7320636f6d6520746f20555341) - * CTxOut(empty) - * vMerkleTree: 12630d16a9 */ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) { @@ -82,8 +93,8 @@ public: consensus.nTargetTimespan = 16 * 60; // 16 mins consensus.nTargetSpacingV1 = 60; consensus.nTargetSpacing = 64; - consensus.BIP34Height = 227931; - consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); + consensus.BIP34Height = -1; + consensus.BIP34Hash = uint256(); consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowNoRetargeting = false; consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 @@ -92,10 +103,10 @@ public: 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; // never -// consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 0; // out of time +// Deployment of BIP68, BIP112, and BIP113. +// consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; +// consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 999999999999ULL; // never +// consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 0; // out of time consensus.nProtocolV1RetargetingFixedTime = 1395631999; consensus.nProtocolV2Time = 1407053625; @@ -120,17 +131,6 @@ public: nMaxTipAge = 24 * 60 * 60; nPruneAfterHeight = 100000; - /** - * Build the genesis block. Note that the output of its generation - * transaction cannot be spent since it did not originally exist in the - * database. - * - * CBlock(hash=000001faef25dec4fbcf906e6242621df2c183bf232f263d0ba5b101911e4563, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=12630d16a97f24b287c8c2594dda5fb98c9e6c70fc61d44191931ea2aa08dc90, nTime=1393221600, nBits=1e0fffff, nNonce=164482, vtx=1, vchBlockSig=) - * Coinbase(hash=12630d16a9, ver=1, nTime=1393221600, vin.size=1, vout.size=1, nLockTime=0) - * CTxIn(COutPoint(0000000000, 4294967295), coinbase 00012a24323020466562203230313420426974636f696e2041544d7320636f6d6520746f20555341) - * CTxOut(empty) - * vMerkleTree: 12630d16a9 - */ const char* pszTimestamp = "20 Feb 2014 Bitcoin ATMs come to USA"; CMutableTransaction txNew; txNew.nTime = 1393221600; @@ -184,23 +184,36 @@ public: static CMainParams mainParams; /** - * Testnet (v3) + * Testnet */ class CTestNetParams : public CChainParams { public: CTestNetParams() { strNetworkID = "test"; + consensus.nMaxReorganizationDepth = 50; consensus.nMajorityEnforceBlockUpgrade = 51; consensus.nMajorityRejectBlockOutdated = 75; consensus.nMajorityWindow = 100; - consensus.BIP34Height = 21111; - consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); consensus.powLimit = uint256S("0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.posLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.posLimitV2 = uint256S("000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nTargetTimespan = 16 * 60; // 16 mins + consensus.nTargetSpacingV1 = 60; + consensus.nTargetSpacing = 64; + consensus.BIP34Height = -1; + consensus.BIP34Hash = uint256(); consensus.fPowAllowMinDifficultyBlocks = true; - consensus.nProtocolV1RetargetingFixedTime = 0; - consensus.nProtocolV2Time = 0; - consensus.nProtocolV3Time = 0; + consensus.fPowNoRetargeting = false; + + consensus.nProtocolV1RetargetingFixedTime = 1395631999; + consensus.nProtocolV2Time = 1407053625; + consensus.nProtocolV3Time = 1444028400; consensus.nLastPOWBlock = 0x7fffffff; + consensus.nStakeTimestampMask = 0xf; // 15 + consensus.nCoinbaseMaturity = 50; + consensus.nStakeMinConfirmations = 50; + consensus.nStakeMinAge = 1 * 60 * 60; // 1 hour + pchMessageStart[0] = 0xcd; pchMessageStart[1] = 0xf2; pchMessageStart[2] = 0xc0; @@ -210,15 +223,13 @@ public: nMaxTipAge = 0x7fffffff; nPruneAfterHeight = 1000; - genesis = CreateGenesisBlock(1411111111, 216178, 0x1f00ffff, 2, 50 * COIN); + genesis = CreateGenesisBlock(1393221600, 216178, 0x1f00ffff, 1, 0); consensus.hashGenesisBlock = genesis.GetHash(); - + assert(consensus.hashGenesisBlock == uint256S("0x0000724595fb3b9609d441cbfb9577615c292abf07d996d3edabc48de843642d")); + assert(genesis.hashMerkleRoot == uint256S("0x12630d16a97f24b287c8c2594dda5fb98c9e6c70fc61d44191931ea2aa08dc90")); vFixedSeeds.clear(); vSeeds.clear(); - vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org")); - vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); - vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de")); base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); @@ -236,7 +247,7 @@ public: checkpointData = (CCheckpointData) { boost::assign::map_list_of - ( 0, uint256S("0x0000724595fb3b9609d441cbfb9577615c292abf07d996d3edabc48de843642d")), + ( 0, uint256S("0x0000724595fb3b9609d441cbfb9577615c292abf07d996d3edabc48de843642d")), 0, 0, 0 From 070424672bfcdc4f85666d9216d209959b2dace1 Mon Sep 17 00:00:00 2001 From: lateminer Date: Tue, 9 Jan 2018 22:41:38 +0300 Subject: [PATCH 2/8] Add -respendnotify option, new RPC data, reg tests https://github.com/bitcoinxt/bitcoinxt/commit/f2a6c79aa17d71443c063fd3c17ca8cf4dbbf706#diff-b4d9f378d56095dfaad2a3802d633a39 --- qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/txn_doublespendrelay.py | 138 +++++++++++++++++++++++++++ src/wallet/rpcwallet.cpp | 22 +++++ src/wallet/wallet.cpp | 10 ++ 4 files changed, 171 insertions(+) create mode 100644 qa/rpc-tests/txn_doublespendrelay.py diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 796e5a3a5..f620f1388 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -81,6 +81,7 @@ testScripts = [ 'receivedby.py', 'mempool_resurrect_test.py', 'txn_doublespend.py --mineblock', + 'txn_doublespendrelay.py', 'txn_clone.py', 'getchaintips.py', 'rawtransactions.py', diff --git a/qa/rpc-tests/txn_doublespendrelay.py b/qa/rpc-tests/txn_doublespendrelay.py new file mode 100644 index 000000000..f349c53c0 --- /dev/null +++ b/qa/rpc-tests/txn_doublespendrelay.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +# +# Test double-spend-relay and notification code +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from decimal import Decimal + +class DoubleSpendRelay(BitcoinTestFramework): + + # + # Create a 4-node network; roles for the nodes are: + # [0] : transaction creator + # [1] : respend sender + # [2] : relay node + # [3] : receiver, should detect/notify of double-spends + # + # Node connectivity is: + # [0,1] <--> [2] <--> [3] + # + def setup_network(self): + self.is_network_split = False + self.nodes = [] + for i in range(0,4): + self.nodes.append(start_node(i, self.options.tmpdir)) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[1], 2) + connect_nodes(self.nodes[3], 2) + return self.nodes + + def run_test(self): + fee = Decimal("0.01") + + nodes = self.nodes + # Test 1: First spend + # shutdown nodes[1] so it is not aware of the first spend + # and will be willing to broadcast a respend + stop_node(nodes[1], 1) + # First spend: nodes[0] -> nodes[3] + amount = Decimal("144") # We rely on this requiring 3 50-BTC inputs + (total_in, tx1_inputs) = gather_inputs(nodes[0], amount+fee) + change_outputs = make_change(nodes[0], total_in, amount, fee) + outputs = dict(change_outputs) + outputs[nodes[3].getnewaddress()] = amount + signed = nodes[0].signrawtransaction(nodes[0].createrawtransaction(tx1_inputs, outputs)) + txid1 = nodes[0].sendrawtransaction(signed["hex"], True) + sync_mempools([nodes[0], nodes[3]]) + + txid1_info = nodes[3].gettransaction(txid1) + assert_equal(txid1_info["respendsobserved"], []) + + # Test 2: Is double-spend of tx1_inputs[0] relayed? + # Restart nodes[1] + nodes[1] = start_node(1, self.options.tmpdir) + connect_nodes(nodes[1], 2) + # Second spend: nodes[0] -> nodes[0] + amount = Decimal("40") + total_in = Decimal("50") + inputs2 = [tx1_inputs[0]] + change_outputs = make_change(nodes[0], total_in, amount, fee) + outputs = dict(change_outputs) + outputs[nodes[0].getnewaddress()] = amount + signed = nodes[0].signrawtransaction(nodes[0].createrawtransaction(inputs2, outputs)) + txid2 = nodes[1].sendrawtransaction(signed["hex"], True) + # Wait until txid2 is relayed to nodes[3] (but don't wait forever): + # Note we can't use sync_mempools, because the respend isn't added to + # the mempool. + for i in range(1,7): + txid1_info = nodes[3].gettransaction(txid1) + if txid1_info["respendsobserved"] != []: + break + time.sleep(0.1 * i**2) # geometric back-off + assert_equal(txid1_info["respendsobserved"], [txid2]) + + # Test 3: Is triple-spend of tx1_inputs[0] not relayed? + # Clear node1 mempool + stop_node(nodes[1], 1) + nodes[1] = start_node(1, self.options.tmpdir) + connect_nodes(nodes[1], 2) + # Third spend: nodes[0] -> nodes[0] + outputs = dict(change_outputs) + outputs[nodes[0].getnewaddress()] = amount + signed = nodes[0].signrawtransaction(nodes[0].createrawtransaction(inputs2, outputs)) + txid3 = nodes[1].sendrawtransaction(signed["hex"], True) + # Ensure txid3 not relayed to nodes[3]: + time.sleep(9.1) + txid1_info = nodes[3].gettransaction(txid1) + assert_equal(txid1_info["respendsobserved"], [txid2]) + + # Test 4: Is double-spend of tx1_inputs[1] relayed when triple-spend of tx1_inputs[0] precedes it? + # Clear node1 mempool + stop_node(nodes[1], 1) + nodes[1] = start_node(1, self.options.tmpdir) + connect_nodes(nodes[1], 2) + # Inputs are third spend, second spend + amount = Decimal("89") + total_in = Decimal("100") + inputs4 = [tx1_inputs[0],tx1_inputs[1]] + change_outputs = make_change(nodes[0], total_in, amount, fee) + outputs = dict(change_outputs) + outputs[nodes[0].getnewaddress()] = amount + signed = nodes[0].signrawtransaction(nodes[0].createrawtransaction(inputs4, outputs)) + txid4 = nodes[1].sendrawtransaction(signed["hex"], True) + # Wait until txid4 is relayed to nodes[3] (but don't wait forever): + for i in range(1,7): + txid1_info = nodes[3].gettransaction(txid1) + if txid1_info["respendsobserved"] != [txid2]: + break + time.sleep(0.1 * i**2) # geometric back-off + assert_equal(sorted(txid1_info["respendsobserved"]), sorted([txid2,txid4])) + + # Test 5: Is double-spend of tx1_inputs[2] relayed when triple-spend of tx1_inputs[0] follows it? + # Clear node1 mempool + stop_node(nodes[1], 1) + nodes[1] = start_node(1, self.options.tmpdir) + connect_nodes(nodes[1], 2) + # Inputs are second spend, third spend + amount = Decimal("88") + total_in = Decimal("100") + inputs5 = [tx1_inputs[2],tx1_inputs[0]] + change_outputs = make_change(nodes[0], total_in, amount, fee) + outputs = dict(change_outputs) + outputs[nodes[0].getnewaddress()] = amount + signed = nodes[0].signrawtransaction(nodes[0].createrawtransaction(inputs5, outputs)) + txid5 = nodes[1].sendrawtransaction(signed["hex"], True) + # Wait until txid5 is relayed to nodes[3] (but don't wait forever): + for i in range(1,7): + txid1_info = nodes[3].gettransaction(txid1) + if sorted(txid1_info["respendsobserved"]) != sorted([txid2,txid4]): + break + time.sleep(0.1 * i**2) # geometric back-off + assert_equal(sorted(txid1_info["respendsobserved"]), sorted([txid2,txid4,txid5])) + +if __name__ == '__main__': + DoubleSpendRelay().main() + diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 11b054125..2a68252c5 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -94,6 +94,10 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); entry.push_back(Pair("walletconflicts", conflicts)); + UniValue respends; + BOOST_FOREACH(const uint256& respend, wtx.GetConflicts()) + respends.push_back(respend.GetHex()); + entry.push_back(Pair("respendsobserved", respends)); entry.push_back(Pair("time", wtx.GetTxTime())); entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); @@ -1394,6 +1398,12 @@ UniValue listtransactions(const UniValue& params, bool fHelp) " 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" + " \"walletconflicts\" : [\n" + " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" + " ],\n" + " \"respendsobserved\" : [\n" + " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" + " ],\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" @@ -1588,6 +1598,12 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) " \"blockindex\": n, (numeric) The block index containing the transaction. 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" + " \"walletconflicts\" : [\n" + " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" + " ],\n" + " \"respendsobserved\" : [\n" + " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" + " ],\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" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" @@ -1672,6 +1688,12 @@ UniValue gettransaction(const UniValue& params, bool fHelp) " \"blockindex\" : xx, (numeric) The block index\n" " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" " \"txid\" : \"transactionid\", (string) The transaction id.\n" + " \"walletconflicts\" : [\n" + " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" + " ],\n" + " \"respendsobserved\" : [\n" + " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" + " ],\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" diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bd8b19330..457780de8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1205,6 +1205,15 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD boost::thread t(runCommand, strCmd); // thread runs free } + // external respend notify + std::string strCmdRespend = GetArg("-respendnotify", ""); + if (!strCmdRespend.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::replace_all(strCmd, "%t", hash.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } return true; } @@ -3667,6 +3676,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) 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("-respendnotify=", _("Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet 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)")); From c3bddf47b253cc08f541bb073a8623d6029f46fb Mon Sep 17 00:00:00 2001 From: lateminer Date: Tue, 9 Jan 2018 23:25:37 +0300 Subject: [PATCH 3/8] UI to alert of respend attempt affecting wallet. https://github.com/bitcoinxt/bitcoinxt/commit/ba478d229b39b4f11534fa930b5d8b6cbb97a6c0 --- src/qt/guiconstants.h | 4 ++++ src/qt/transactionfilterproxy.cpp | 4 ++-- src/qt/transactionrecord.cpp | 8 ++++++-- src/qt/transactionrecord.h | 23 +++++++++++++++++++---- src/qt/transactiontablemodel.cpp | 24 +++++++++++++++++++----- src/qt/transactiontablemodel.h | 4 ++++ src/ui_interface.h | 3 ++- src/wallet/wallet.cpp | 14 ++++++++++++++ src/wallet/wallet.h | 4 ++++ 9 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 54b1fc7ec..268d39666 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -33,6 +33,10 @@ static const bool DEFAULT_SPLASHSCREEN = true; #define COLOR_TX_STATUS_DANGER QColor(200, 100, 100) /* Transaction list -- TX status decoration - default color */ #define COLOR_BLACK QColor(0, 0, 0) +/* Transaction list -- has conflicting transactions */ +#define COLOR_HASCONFLICTING QColor(255, 255, 255) +/* Transaction list -- has conflicting transactions - background */ +#define COLOR_HASCONFLICTING_BG QColor(192, 0, 0) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 7981eb7c9..fca95eece 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -25,7 +25,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : watchOnlyFilter(WatchOnlyFilter_All), minAmount(0), limitRows(-1), - showInactive(true) + showInactive(false) { } @@ -41,7 +41,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); int status = index.data(TransactionTableModel::StatusRole).toInt(); - if(!showInactive && status == TransactionStatus::Conflicted) + if(!showInactive && status == TransactionStatus::Conflicted && type == TransactionRecord::Other) return false; if(!(TYPE(type) & typeFilter)) return false; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 5e1a3a2f2..f9626e2e9 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -195,6 +195,8 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = chainActive.Height(); + status.hasConflicting = false; + if (!CheckFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) @@ -238,6 +240,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (status.depth < 0) { status.status = TransactionStatus::Conflicted; + status.hasConflicting = !(wtx.GetConflicts().empty()); } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { @@ -246,6 +249,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; + status.hasConflicting = !(wtx.GetConflicts().empty()); if (wtx.isAbandoned()) status.status = TransactionStatus::Abandoned; } @@ -261,10 +265,10 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) } -bool TransactionRecord::statusUpdateNeeded() +bool TransactionRecord::statusUpdateNeeded(int64_t nConflictsReceived) { AssertLockHeld(cs_main); - return status.cur_num_blocks != chainActive.Height(); + return (status.cur_num_blocks != chainActive.Height() || status.cur_num_conflicts != nConflictsReceived); } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 97aed1059..63d396337 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -20,9 +20,17 @@ class TransactionStatus { public: TransactionStatus(): - countsForBalance(false), sortKey(""), - matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) - { } + countsForBalance(false), + sortKey(""), + matures_in(0), + status(Offline), + hasConflicting(false), + depth(0), + open_for(0), + cur_num_blocks(-1), + cur_num_conflicts(-1) + { + } enum Status { Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/ @@ -53,6 +61,10 @@ public: /** @name Reported status @{*/ Status status; + + // Has conflicting transactions spending same prevout + bool hasConflicting; + qint64 depth; qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number of additional blocks that need to be mined before @@ -61,6 +73,9 @@ public: /** Current number of blocks (to know whether cached status is still valid) */ int cur_num_blocks; + + /** Number of conflicts received into wallet as of last status update */ + int64_t cur_num_conflicts; }; /** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has @@ -138,7 +153,7 @@ public: /** Return whether a status update is needed. */ - bool statusUpdateNeeded(); + bool statusUpdateNeeded(int64_t nConflictsReceived); }; #endif // BITCOIN_QT_TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index b29ecf834..ea29865ce 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -166,8 +166,12 @@ public: 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. + Q_EMIT parent->dataChanged(parent->index(lowerIndex, parent->Status), parent->index(upperIndex-1, parent->Amount)); + break; + case CT_GOT_CONFLICT: + Q_EMIT parent->message(parent->tr("Conflict Received"), + parent->tr("WARNING: Transaction may never be confirmed. Its input was seen being spent by another transaction on the network."), + CClientUIInterface::MSG_WARNING); break; } } @@ -187,20 +191,21 @@ public: // 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, + // If a status update is needed (blocks or conflicts 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()) + if(lockWallet && rec->statusUpdateNeeded(wallet->nConflictsReceived)) { std::map::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) { rec->updateStatus(mi->second); + rec->status.cur_num_conflicts = wallet->nConflictsReceived; } } } @@ -247,6 +252,7 @@ TransactionTableModel::TransactionTableModel(const PlatformStyle *platformStyle, priv->refreshWallet(); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + connect(this, SIGNAL(message(QString,QString,unsigned int)), walletModel, SIGNAL(message(QString,QString,unsigned int))); subscribeToCoreSignals(); } @@ -380,6 +386,8 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const return tr("Payment to yourself"); case TransactionRecord::Generated: return tr("Mined"); + case TransactionRecord::Other: + return tr("Other"); default: return QString(); } @@ -577,7 +585,13 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTooltip(rec); case Qt::TextAlignmentRole: return column_alignments[index.column()]; + case Qt::BackgroundColorRole: + if (rec->status.hasConflicting) + return COLOR_HASCONFLICTING_BG; + break; case Qt::ForegroundRole: + if (rec->status.hasConflicting) + return COLOR_HASCONFLICTING; // Use the "danger" color for abandoned transactions if(rec->status.status == TransactionStatus::Abandoned) { diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 6932646e1..048b64451 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -104,6 +104,10 @@ private: QVariant txWatchonlyDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; +Q_SIGNALS: + // Fired when a message should be reported to the user + void message(const QString &title, const QString &message, unsigned int style); + public Q_SLOTS: /* New transaction, or transaction changed status */ void updateTransaction(const QString &hash, int status, bool showTransaction); diff --git a/src/ui_interface.h b/src/ui_interface.h index ef72ced16..a7628ca0f 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -22,7 +22,8 @@ enum ChangeType { CT_NEW, CT_UPDATED, - CT_DELETED + CT_DELETED, + CT_GOT_CONFLICT }; /** Signals for UI communication. */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bd8b19330..b38e77b63 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1196,6 +1196,20 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + // Notifications for existing transactions that now have conflicts with this one + if (fInsertedNew) + { + BOOST_FOREACH(const uint256& conflictHash, wtxIn.GetConflicts()) + { + CWalletTx& txConflict = mapWallet[conflictHash]; + NotifyTransactionChanged(this, conflictHash, CT_UPDATED); //Updates UI table + if (IsFromMe(txConflict) || IsMine(txConflict)) + { + NotifyTransactionChanged(this, conflictHash, CT_GOT_CONFLICT); //Throws dialog + } + } + } + // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 159cb9806..f87feb20c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -535,6 +535,9 @@ public: MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; + // Increment to cause UI refresh, similar to new block + int64_t nConflictsReceived; + CWallet() { SetNull(); @@ -566,6 +569,7 @@ public: nLastResend = 0; nTimeFirstKey = 0; fBroadcastTransactions = false; + nConflictsReceived = 0; } std::map mapWallet; From 72875a21160bef55b39a7420dc1462cba2de0ccb Mon Sep 17 00:00:00 2001 From: lateminer Date: Wed, 10 Jan 2018 21:52:38 +0300 Subject: [PATCH 4/8] Relay double-spends, subject to anti-DOS https://github.com/bitcoinxt/bitcoinxt/commit/cef37115001abc70094b0fccd649212f9c4c9efd#diff-7ec3c68a81efff79b6ca22ac1f1eabbaL929 --- src/init.cpp | 1 + src/main.cpp | 98 ++++++++++++++++++++++++++++------ src/main.h | 3 ++ src/primitives/transaction.cpp | 9 ++++ src/primitives/transaction.h | 3 ++ src/qt/transactionrecord.cpp | 4 +- src/txmempool.cpp | 1 - src/validationinterface.cpp | 8 +-- src/validationinterface.h | 8 +-- src/wallet/rpcwallet.cpp | 2 +- src/wallet/wallet.cpp | 60 +++++++++++++-------- src/wallet/wallet.h | 9 ++-- 12 files changed, 150 insertions(+), 56 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 97b29a8fb..b362f1450 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1439,6 +1439,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); + InitRespendFilter(); StartNode(threadGroup, scheduler); // Monitor the chain, and alert if we get blocks much quicker or slower than expected diff --git a/src/main.cpp b/src/main.cpp index 3cf9db618..eae3b38a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -561,6 +561,14 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector= nLimit*10*1000) + return true; + dCount += nSize; + return false; +} + +static bool RespendRelayExceeded(const CTransaction& doubleSpend) +{ + // Apply an independent rate limit to double-spend relays + static double dRespendCount; + static int64_t nLastRespendTime; + static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100); + unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION); + + if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize)) + { + LogPrint("mempool", "Double-spend relay rejected by rate limiter\n"); + return true; + } + + LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize); + + return false; +} + bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, std::vector& vHashTxnToUncache) @@ -1066,13 +1109,26 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions + bool fRespend = false; + COutPoint relayForOutpoint; + set setConflicts; { LOCK(pool.cs); // protect pool.mapNextTx BOOST_FOREACH(const CTxIn &txin, tx.vin) { + COutPoint outpoint = txin.prevout; + // A respend is a tx that conflicts with a member of the pool if (pool.mapNextTx.count(txin.prevout)) { + fRespend = true; + // Relay only one tx per respent outpoint, but not if tx is equivalent to pool member + if (!doubleSpendFilter.contains(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx)) + { + relayForOutpoint = outpoint; + break; + } + /* const CTransaction *ptxConflicting = pool.mapNextTx[txin.prevout].ptx; if (!setConflicts.count(ptxConflicting->GetHash())) { @@ -1105,8 +1161,12 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C setConflicts.insert(ptxConflicting->GetHash()); } + */ + } } + if (fRespend && (relayForOutpoint.IsNull() || RespendRelayExceeded(tx))) + return false; } { @@ -1215,22 +1275,15 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // 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(); + static int64_t nLastFreeTime; + static int64_t nFreeLimit = GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY); - 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 - if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000) + if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize)) 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 (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) @@ -1434,8 +1487,19 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C } pool.RemoveStaged(allConflicting); - // Store transaction in memory - pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); + if (fRespend) + { + // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM insertions + if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0) + doubleSpendFilter.clear(); + doubleSpendFilter.insert(relayForOutpoint); + RelayTransaction(tx); + } + else + { + // Store transaction in memory + pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); + } // Add memory address index if (fAddressIndex) { @@ -1455,9 +1519,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C } } - SyncWithWallets(tx, NULL); + SyncWithWallets(tx, NULL, fRespend); - return true; + return !fRespend; } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, @@ -3149,7 +3213,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx, NULL); + SyncWithWallets(tx, NULL, false); } return true; } @@ -3208,11 +3272,11 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx, NULL); + SyncWithWallets(tx, NULL, false); } // ... and about transactions that got confirmed: BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { - SyncWithWallets(tx, pblock); + SyncWithWallets(tx, pblock, false); } int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; diff --git a/src/main.h b/src/main.h index d2998c841..3438510a9 100644 --- a/src/main.h +++ b/src/main.h @@ -169,6 +169,9 @@ extern CBlockIndex *pindexBestHeader; /** Minimum disk space required - used in CheckDiskSpace() */ static const uint64_t nMinDiskSpace = 52428800; +/** Initialize respend bloom filter **/ +void InitRespendFilter(); + /** Pruning-related variables and constants */ /** True if any block files have ever been pruned. */ extern bool fHavePruned; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index a75c5f452..c3577dad4 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -89,6 +89,15 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { return *this; } +bool CTransaction::IsEquivalentTo(const CTransaction& tx) const +{ + CMutableTransaction tx1 = *this; + CMutableTransaction tx2 = 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); +} + CAmount CTransaction::GetValueOut() const { CAmount nValueOut = 0; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 9a71ff72e..50fdc0d6f 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -272,6 +272,9 @@ public: return hash; } + // True if only scriptSigs are different + bool IsEquivalentTo(const CTransaction& tx) const; + // Return sum of txouts. CAmount GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index f9626e2e9..041e93982 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -240,7 +240,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (status.depth < 0) { status.status = TransactionStatus::Conflicted; - status.hasConflicting = !(wtx.GetConflicts().empty()); + status.hasConflicting = !(wtx.GetConflicts(false).empty()); } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { @@ -249,7 +249,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; - status.hasConflicting = !(wtx.GetConflicts().empty()); + status.hasConflicting = !(wtx.GetConflicts(false).empty()); if (wtx.isAbandoned()) status.status = TransactionStatus::Abandoned; } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 97a335a99..8782678eb 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -684,7 +684,6 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem void CTxMemPool::removeConflicts(const CTransaction &tx, std::list& removed) { // Remove transactions which depend on inputs of tx, recursively - list result; LOCK(cs); BOOST_FOREACH(const CTxIn &txin, tx.vin) { std::map::iterator it = mapNextTx.find(txin.prevout); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 81f3b775f..7cc1b3ce3 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -14,7 +14,7 @@ CMainSignals& GetMainSignals() void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1)); - g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); + g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); @@ -32,7 +32,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); + g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1)); } @@ -48,6 +48,6 @@ void UnregisterAllValidationInterfaces() { g_signals.UpdatedBlockTip.disconnect_all_slots(); } -void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) { - g_signals.SyncTransaction(tx, pblock); +void SyncWithWallets(const CTransaction &tx, const CBlock *pblock, bool fRespend) { + g_signals.SyncTransaction(tx, pblock, fRespend); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 4da145473..211bcf0c7 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -27,12 +27,12 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllValidationInterfaces(); /** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL); +void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fRespend = false); class CValidationInterface { protected: virtual void UpdatedBlockTip(const CBlockIndex *pindex) {} - virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {} + virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock, bool fRespend) {} virtual void SetBestChain(const CBlockLocator &locator) {} virtual void UpdatedTransaction(const uint256 &hash) {} virtual void Inventory(const uint256 &hash) {} @@ -48,8 +48,8 @@ protected: struct CMainSignals { /** Notifies listeners of updated block chain tip */ boost::signals2::signal UpdatedBlockTip; - /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ - boost::signals2::signal SyncTransaction; + /** Notifies listeners of updated transaction data (transaction, optionally the block it is found in, and whether this is a known respend. */ + boost::signals2::signal SyncTransaction; /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ boost::signals2::signal UpdatedTransaction; /** Notifies listeners of a new active block chain. */ diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 2a68252c5..b9d7ded1e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -95,7 +95,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) conflicts.push_back(conflict.GetHex()); entry.push_back(Pair("walletconflicts", conflicts)); UniValue respends; - BOOST_FOREACH(const uint256& respend, wtx.GetConflicts()) + BOOST_FOREACH(const uint256& respend, wtx.GetConflicts(false)) respends.push_back(respend.GetHex()); entry.push_back(Pair("respendsobserved", respends)); entry.push_back(Pair("time", wtx.GetTxTime())); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a56e99e5e..977346a42 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -391,7 +391,7 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } -set CWallet::GetConflicts(const uint256& txid) const +set CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) const { set result; AssertLockHeld(cs_wallet); @@ -409,7 +409,8 @@ set CWallet::GetConflicts(const uint256& txid) const 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); + if (includeEquivalent || !wtx.IsEquivalentTo(mapWallet.at(it->second))) + result.insert(it->second); } return result; } @@ -1237,7 +1238,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD * pblock is optional, but should be provided if the transaction is known to be in a block. * If fUpdate is true, existing transactions will be updated. */ -bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fRespend) { { AssertLockHeld(cs_wallet); @@ -1257,7 +1258,15 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl bool fExisted = mapWallet.count(tx.GetHash()) != 0; if (fExisted && !fUpdate) return false; - if (fExisted || IsMine(tx) || IsFromMe(tx)) + + bool fIsConflicting = IsConflicting(tx); + // Don't add respends that pay us, unless they conflict with us. Prevents resource exhaustion. + if (!fIsConflicting && fRespend) return false; + + if (fIsConflicting) + nConflictsReceived++; + + if (fExisted || IsMine(tx) || IsFromMe(tx) || fIsConflicting) { CWalletTx wtx(this,tx); @@ -1392,24 +1401,21 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } } -void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) +void void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock, bool fRespend) { + LOCK2(cs_main, cs_wallet); + if (!pblock) { + // wallets need to refund inputs when disconnecting coinstake + if (tx.IsCoinStake()) { + if (IsFromMe(tx)) { + DisableTransaction(tx); + return; + } + } + } - - LOCK2(cs_main, cs_wallet); - - if (!pblock) { - // wallets need to refund inputs when disconnecting coinstake - if (tx.IsCoinStake()) { - if (IsFromMe(tx)) { - DisableTransaction(tx); - return; - } - } - } - - if (!AddToWalletIfInvolvingMe(tx, pblock, true)) + if (!AddToWalletIfInvolvingMe(tx, pblock, true, fRespend)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -1438,6 +1444,14 @@ isminetype CWallet::IsMine(const CTxIn &txin) const return ISMINE_NO; } +bool CWallet::IsConflicting(const CTransaction& tx) const +{ + BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (mapTxSpends.count(txin.prevout)) + return true; + return false; +} + CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const { { @@ -1774,7 +1788,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) ReadBlockFromDisk(block, pindex, Params().GetConsensus()); BOOST_FOREACH(CTransaction& tx, block.vtx) { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate, false)) ret++; } pindex = chainActive.Next(pindex); @@ -1805,7 +1819,7 @@ void CWallet::ReacceptWalletTransactions() int nDepth = wtx.GetDepthInMainChain(); - if (!wtx.IsCoinBase() && !wtx.IsCoinStake() && (nDepth == 0 && !wtx.isAbandoned())) { + if (!wtx.IsCoinBase() && !wtx.IsCoinStake() && (nDepth == 0 && !wtx.isAbandoned() && (IsMine(wtx) || IsFromMe(wtx)))) { mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); } } @@ -1834,13 +1848,13 @@ bool CWalletTx::RelayWalletTransaction() return false; } -set CWalletTx::GetConflicts() const +set CWalletTx::GetConflicts(bool includeEquivalent) const { set result; if (pwallet != NULL) { uint256 myHash = GetHash(); - result = pwallet->GetConflicts(myHash); + result = pwallet->GetConflicts(myHash, includeEquivalent); result.erase(myHash); } return result; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f87feb20c..63ecbc7fd 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -414,7 +414,7 @@ public: bool RelayWalletTransaction(); - std::set GetConflicts() const; + std::set GetConflicts(bool includeEquivalent=true) const; }; @@ -668,8 +668,8 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb); - void SyncTransaction(const CTransaction& tx, const CBlock* pblock); - bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); + void SyncTransaction(const CTransaction& tx, const CBlock* pblock, bool fRespend); + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fRespend); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime); @@ -737,6 +737,7 @@ public: bool IsMine(const CTransaction& tx) const; /** should probably be renamed to IsRelevantToMe */ bool IsFromMe(const CTransaction& tx) const; + bool IsConflicting(const CTransaction& tx) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; @@ -789,7 +790,7 @@ public: void DisableTransaction(const CTransaction &tx); //! Get wallet transactions that conflict with given transaction (spend same outputs) - std::set GetConflicts(const uint256& txid) const; + std::set GetConflicts(const uint256& txid, bool includeEquivalent) const; //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); From 72fb9f5567b32bb66bb477354996fd7a055b82a8 Mon Sep 17 00:00:00 2001 From: lateminer Date: Wed, 10 Jan 2018 22:12:18 +0300 Subject: [PATCH 5/8] Fix typo wallet.cpp --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 977346a42..91e55c43e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1401,7 +1401,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } } -void void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock, bool fRespend) +void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock, bool fRespend) { LOCK2(cs_main, cs_wallet); From 602f3d2aaddfe933c81a8db7a9874e1c760fdf60 Mon Sep 17 00:00:00 2001 From: janko33bd Date: Wed, 10 Jan 2018 23:06:58 +0100 Subject: [PATCH 6/8] removing confusing tooltip --- src/qt/forms/askpassphrasedialog.ui | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui index 5b7223e4f..dce4b8883 100644 --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -117,9 +117,6 @@ - - Serves to disable the trivial sendmoney when OS account compromised. Provides no real security. - * Tick for staking only From 714d2650b5fa0e1a5a0410a3795ca7417bf36033 Mon Sep 17 00:00:00 2001 From: janko33bd Date: Wed, 10 Jan 2018 23:13:59 +0100 Subject: [PATCH 7/8] added SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS --- src/script/interpreter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a6f8fd0e1..e86bf4fc1 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -428,6 +428,10 @@ bool EvalScript(vector >& stack, const CScript& script, un case OP_COUNT_ACKS: { + if (SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + // (secondary_chain_id ack_period liveness_period -- ) if (stack.size() < 3) From c4c9d818233f27d1309bcd6e8b500ce2049fad59 Mon Sep 17 00:00:00 2001 From: lateminer Date: Fri, 12 Jan 2018 07:34:45 +0300 Subject: [PATCH 8/8] Use std::shared_ptr instead of boost::shared_ptr --- src/miner.cpp | 2 +- src/rpc/mining.cpp | 3 +-- src/rpc/server.cpp | 5 ++--- src/validationinterface.h | 5 ++--- src/wallet/wallet.cpp | 4 ++-- src/wallet/wallet.h | 4 +--- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index 7a856ae29..543d3617b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -390,7 +390,7 @@ void static BitcoinMiner(const CChainParams& chainparams) unsigned int nExtraNonce = 0; - boost::shared_ptr coinbaseScript; + std::shared_ptr coinbaseScript; GetMainSignals().ScriptForMining(coinbaseScript); try { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 254fbc300..fac37d340 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -28,7 +28,6 @@ #include #include -#include #include @@ -141,7 +140,7 @@ UniValue generate(const UniValue& params, bool fHelp) int nHeight = 0; int nGenerate = params[0].get_int(); - boost::shared_ptr coinbaseScript; + std::shared_ptr coinbaseScript; GetMainSignals().ScriptForMining(coinbaseScript); // If the keypool is exhausted, no script is returned at all. Catch this. diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 102c77631..dc0d8074d 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include // for to_upper() @@ -36,7 +35,7 @@ static CCriticalSection cs_rpcWarmup; static RPCTimerInterface* timerInterface = NULL; /* Map of name to timer. * @note Can be changed to std::unique_ptr when C++11 */ -static std::map > deadlineTimers; +static std::map > deadlineTimers; static struct CRPCSignals { @@ -492,7 +491,7 @@ void RPCRunLater(const std::string& name, boost::function func, int6 throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); deadlineTimers.erase(name); LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); - deadlineTimers.insert(std::make_pair(name, boost::shared_ptr(timerInterface->NewTimer(func, nSeconds*1000)))); + deadlineTimers.insert(std::make_pair(name, std::shared_ptr(timerInterface->NewTimer(func, nSeconds*1000)))); } CRPCTable tableRPC; diff --git a/src/validationinterface.h b/src/validationinterface.h index 211bcf0c7..df03388b0 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -7,7 +7,6 @@ #define BITCOIN_VALIDATIONINTERFACE_H #include -#include class CBlock; struct CBlockLocator; @@ -38,7 +37,7 @@ protected: virtual void Inventory(const uint256 &hash) {} virtual void ResendWalletTransactions(int64_t nBestBlockTime) {} virtual void BlockChecked(const CBlock&, const CValidationState&) {} - virtual void GetScriptForMining(boost::shared_ptr&) {}; + virtual void GetScriptForMining(std::shared_ptr&) {}; virtual void ResetRequestCount(const uint256 &hash) {}; friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); @@ -61,7 +60,7 @@ struct CMainSignals { /** Notifies listeners of a block validation result */ boost::signals2::signal BlockChecked; /** Notifies listeners that a key for mining is required (coinbase) */ - boost::signals2::signal&)> ScriptForMining; + boost::signals2::signal&)> ScriptForMining; /** Notifies listeners that a block has been successfully mined */ boost::signals2::signal BlockFound; }; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 91e55c43e..75fbcc98d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3473,9 +3473,9 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx) } } -void CWallet::GetScriptForMining(boost::shared_ptr &script) +void CWallet::GetScriptForMining(std::shared_ptr &script) { - boost::shared_ptr rKey(new CReserveKey(this)); + std::shared_ptr rKey = std::make_shared(this); CPubKey pubkey; if (!rKey->GetReservedKey(pubkey)) return; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 63ecbc7fd..9b8125361 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -26,8 +26,6 @@ #include #include -#include - extern CWallet* pwalletMain; /** @@ -763,7 +761,7 @@ public: } } - void GetScriptForMining(boost::shared_ptr &script); + void GetScriptForMining(std::shared_ptr &script); void ResetRequestCount(const uint256 &hash) { LOCK(cs_wallet);