diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 110bfe03eff..33dab9b6388 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,122 @@ +bitcoin (0.14.1-trusty4) trusty; urgency=medium + + * Re-enable UPnP support. + + -- Matt Corallo (BlueMatt) Fri, 05 May 2017 13:28:00 -0400 + +bitcoin (0.14.1-trusty3) trusty; urgency=medium + + * Build with qt5 if we are on a non-Ubuntu (ie non-Unity) distro. + + -- Matt Corallo (BlueMatt) Thu, 04 May 2017 17:13:00 -0400 + +bitcoin (0.14.1-trusty2) trusty; urgency=medium + + * Bump minimum boost version in deps. + + -- Matt Corallo (BlueMatt) Thu, 04 May 2017 17:12:00 -0400 + +bitcoin (0.14.1-trusty1) trusty; urgency=medium + + * New upstream release. + + -- Matt Corallo (BlueMatt) Sat, 22 Apr 2017 17:10:00 -0400 + +bitcoin (0.14.0-trusty1) trusty; urgency=medium + + * New upstream release. + + -- Matt Corallo (BlueMatt) Wed, 08 Mar 2017 10:30:00 -0500 + +bitcoin (0.13.2-trusty1) trusty; urgency=medium + + * New upstream release. + + -- Matt Corallo (BlueMatt) Thu, 05 Jan 2017 09:59:00 -0500 + +bitcoin (0.13.1-trusty2) trusty; urgency=medium + + * Revert to Qt4, due to https://github.com/bitcoin/bitcoin/issues/9038 + + -- Matt Corallo (BlueMatt) Mon, 31 Oct 2016 11:16:00 -0400 + +bitcoin (0.13.1-trusty1) trusty; urgency=medium + + * New upstream release. + * Backport updated bitcoin-qt.desktop from upstream master + * Add zmq dependency + * Switch to Qt5 (breaks precise, but that was already broken by C++11) + + -- Matt Corallo (BlueMatt) Thu, 27 Oct 2016 17:32:00 -0400 + +bitcoin (0.13.0-trusty1) trusty; urgency=medium + + * New upstream release. + + -- Matt Corallo (BlueMatt) Sun, 04 Sep 2016 22:09:00 -0400 + +bitcoin (0.12.1-trusty1) trusty; urgency=medium + + * New upstream release. + + -- Matt Corallo (BlueMatt) Mon, 18 Apr 2016 14:26:00 -0700 + +bitcoin (0.12.0-trusty6) trusty; urgency=medium + + * Fix program-options dep. + + -- Matt Corallo (BlueMatt) Fri, 25 Mar 2016 21:41:00 -0700 + +bitcoin (0.12.0-trusty5) trusty; urgency=medium + + * Test explicit --with-gui + + -- Matt Corallo (BlueMatt) Tue, 23 Feb 2015 23:25:00 -0800 + +bitcoin (0.12.0-trusty4) trusty; urgency=medium + + * Fix libevent-dev dep. + + -- Matt Corallo (BlueMatt) Tue, 23 Feb 2015 23:25:00 -0800 + +bitcoin (0.12.0-trusty3) trusty; urgency=medium + + * Fix precise boost dep. + + -- Matt Corallo (BlueMatt) Tue, 23 Feb 2015 19:55:00 -0800 + +bitcoin (0.12.0-trusty2) trusty; urgency=medium + + * Fix libevent dep. + + -- Matt Corallo (BlueMatt) Tue, 23 Feb 2015 19:53:00 -0800 + +bitcoin (0.12.0-trusty1) trusty; urgency=medium + + * New upstream release + * Various updates to contrib/debian were merged, a few were not + + -- Matt Corallo (BlueMatt) Tue, 23 Feb 2015 19:29:00 -0800 + +bitcoin (0.11.2-trusty1) trusty; urgency=low + + * New upstream release. + + -- Matt Corallo (BlueMatt) Fri, 13 Nov 2015 18:39:00 -0800 + +bitcoin (0.11.1-trusty2) trusty; urgency=low + + * Remove minupnpc builddep. + + -- Matt Corallo (BlueMatt) Wed, 14 Oct 2015 23:06:00 -1000 + +bitcoin (0.11.1-trusty1) trusty; urgency=high + + * New upstream release. + * Disable all UPnP support. + + -- Matt Corallo (BlueMatt) Wed, 14 Oct 2015 13:57:00 -1000 + bitcoin (0.11.0-precise1) precise; urgency=medium * New upstream release. @@ -179,7 +298,7 @@ bitcoin (0.5.3-natty0) natty; urgency=low bitcoin (0.5.2-natty1) natty; urgency=low * Remove mentions on anonymity in package descriptions and manpage. - These should never have been there, bitcoin isn't anonymous without + These should never have been there, bitcoin isnt anonymous without a ton of work that virtually no users will ever be willing and capable of doing @@ -220,7 +339,7 @@ bitcoin (0.5.0~rc1-natty1) natty; urgency=low * Add test_bitcoin to build test * Fix clean - * Remove unnecessary build-dependancies + * Remove uneccessary build-dependancies -- Matt Corallo Wed, 26 Oct 2011 14:37:18 -0400 @@ -380,7 +499,7 @@ bitcoin (0.3.20.01~dfsg-1) unstable; urgency=low bitcoin (0.3.19~dfsg-6) unstable; urgency=low - * Fix override aggressive optimizations. + * Fix override agressive optimizations. * Fix tighten build-dependencies to really fit backporting to Lenny: + Add fallback build-dependency on libdb4.6++-dev. + Tighten unversioned Boost build-dependencies to recent versions, diff --git a/contrib/debian/control b/contrib/debian/control index fce6bc0118f..0d6ad25e249 100644 --- a/contrib/debian/control +++ b/contrib/debian/control @@ -1,27 +1,30 @@ Source: bitcoin Section: utils Priority: optional -Maintainer: Jonas Smedegaard -Uploaders: Micah Anderson +Maintainer: Matt Corallo +Uploaders: Matt Corallo Build-Depends: debhelper, devscripts, automake, libtool, bash-completion, - libboost-system-dev (>> 1.35) | libboost-system1.35-dev, libdb4.8++-dev, libssl-dev, pkg-config, - libminiupnpc8-dev | libminiupnpc-dev (>> 1.6), - libboost-filesystem-dev (>> 1.35) | libboost-filesystem1.35-dev, - libboost-program-options-dev (>> 1.35) | libboost-program-options1.35-dev, - libboost-thread-dev (>> 1.35) | libboost-thread1.35-dev, - libboost-test-dev (>> 1.35) | libboost-test1.35-dev, - qt4-qmake, - libqt4-dev, + libevent-dev, + libboost-system1.48-dev | libboost-system-dev (>> 1.47), + libboost-filesystem1.48-dev | libboost-filesystem-dev (>> 1.47), + libboost-program-options1.48-dev | libboost-program-options-dev (>> 1.47), + libboost-thread1.48-dev | libboost-thread-dev (>> 1.47), + libboost-test1.48-dev | libboost-test-dev (>> 1.47), + libboost-chrono1.48-dev | libboost-chrono-dev (>> 1.47), + libminiupnpc8-dev | libminiupnpc-dev, + qt4-qmake, libqt4-dev, + qttools5-dev-tools, qttools5-dev, libqrencode-dev, libprotobuf-dev, protobuf-compiler, - python + python, + libzmq3-dev Standards-Version: 3.9.2 Homepage: https://bitcoincore.org/ Vcs-Git: git://github.com/bitcoin/bitcoin.git @@ -31,11 +34,11 @@ Package: bitcoind Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: peer-to-peer network based digital currency - daemon - Bitcoin is an experimental new digital currency that enables instant - payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer - technology to operate with no central authority: managing transactions - and issuing money are carried out collectively by the network. Bitcoin Core - is the name of the open source software which enables the use of this currency. + Bitcoin is a free open source peer-to-peer electronic cash system that + is completely decentralized, without the need for a central server or + trusted parties. Users hold the crypto keys to their own money and + transact directly with each other, with the help of a P2P network to + check for double-spending. . This package provides the daemon, bitcoind, and the CLI tool bitcoin-cli to interact with the daemon. @@ -44,11 +47,11 @@ Package: bitcoin-qt Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: peer-to-peer network based digital currency - Qt GUI - Bitcoin is an experimental new digital currency that enables instant - payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer - technology to operate with no central authority: managing transactions - and issuing money are carried out collectively by the network. Bitcoin Core - is the name of the open source software which enables the use of this currency. + Bitcoin is a free open source peer-to-peer electronic cash system that + is completely decentralized, without the need for a central server or + trusted parties. Users hold the crypto keys to their own money and + transact directly with each other, with the help of a P2P network to + check for double-spending. . This package provides Bitcoin-Qt, a GUI for Bitcoin based on Qt. @@ -56,11 +59,11 @@ Package: bitcoin-tx Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: peer-to-peer digital currency - standalone transaction tool - Bitcoin is an experimental new digital currency that enables instant - payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer - technology to operate with no central authority: managing transactions - and issuing money are carried out collectively by the network. Bitcoin Core - is the name of the open source software which enables the use of this currency. + Bitcoin is a free open source peer-to-peer electronic cash system that + is completely decentralized, without the need for a central server or + trusted parties. Users hold the crypto keys to their own money and + transact directly with each other, with the help of a P2P network to + check for double-spending. . This package provides bitcoin-tx, a command-line transaction creation tool which can be used without a bitcoin daemon. Some means of diff --git a/contrib/debian/rules b/contrib/debian/rules index 3896d2caa31..6885e385212 100755 --- a/contrib/debian/rules +++ b/contrib/debian/rules @@ -12,10 +12,12 @@ override_dh_auto_clean: if [ -f Makefile ]; then $(MAKE) distclean; fi rm -rf Makefile.in aclocal.m4 configure src/Makefile.in src/bitcoin-config.h.in src/build-aux src/qt/Makefile.in src/qt/test/Makefile.in src/test/Makefile.in +QT=$(shell dpkg-vendor --derives-from Ubuntu && echo qt4 || echo qt5) + # Yea, autogen should be run on the source archive, but I like doing git archive override_dh_auto_configure: ./autogen.sh - ./configure + ./configure --with-gui=$(QT) override_dh_auto_test: make check diff --git a/contrib/init/bitcoind.openrcconf b/contrib/init/bitcoind.openrcconf index 0cbff6d30d2..f70e25cb5fd 100644 --- a/contrib/init/bitcoind.openrcconf +++ b/contrib/init/bitcoind.openrcconf @@ -23,7 +23,7 @@ #BITCOIND_NICE=0 # Additional options (avoid -conf and -datadir, use flags above) -BITCOIND_OPTS="-disablewallet" +#BITCOIND_OPTS="" # The timeout in seconds OpenRC will wait for bitcoind to terminate # after a SIGTERM has been raised. diff --git a/contrib/seeds/README.md b/contrib/seeds/README.md index afe902fd7f0..139c03181fc 100644 --- a/contrib/seeds/README.md +++ b/contrib/seeds/README.md @@ -8,7 +8,7 @@ and remove old versions as necessary. The seeds compiled into the release are created from sipa's DNS seed data, like this: - curl -s http://bitcoin.sipa.be/seeds.txt > seeds_main.txt + curl -s http://bitcoin.sipa.be/seeds.txt.gz | gzip -dc > seeds_main.txt python3 makeseeds.py < seeds_main.txt > nodes_main.txt python3 generate-seeds.py . > ../../src/chainparamsseeds.h diff --git a/doc/build-osx.md b/doc/build-osx.md index a15bcd012c6..32d7dbd69e7 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -11,14 +11,14 @@ Install the OS X command line tools: When the popup appears, click `Install`. -Then install [Homebrew](http://brew.sh). +Then install [Homebrew](https://brew.sh). Dependencies ---------------------- - brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf --c++11 qt5 libevent + brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf qt libevent -In case you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG +If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG brew install librsvg diff --git a/doc/release-notes.md b/doc/release-notes.md index 0ad554b7738..9c8c62d1bd9 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -30,6 +30,55 @@ frequently tested on them. Notable changes =============== +Low-level RPC changes +--------------------- + +- Error codes have been updated to be more accurate for the following error cases: + - `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for + example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR. + - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned + because the node is not in pruned mode. Previously returned RPC_METHOD_NOT_FOUND. + - `pruneblockchain` now returns RPC_INVALID_PARAMETER if the blocks cannot be pruned + because the supplied timestamp is too late. Previously returned RPC_INTERNAL_ERROR. + - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned + because the blockchain is too short. Previously returned RPC_INTERNAL_ERROR. + - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the supplied IP address + or subnet is invalid. Previously returned RPC_CLIENT_NODE_ALREADY_ADDED. + - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the user tries to unban + a node that has not previously been banned. Previously returned RPC_MISC_ERROR. + - `removeprunedfunds` now returns RPC_WALLET_ERROR if bitcoind is unable to remove + the transaction. Previously returned RPC_INTERNAL_ERROR. + - `removeprunedfunds` now returns RPC_INVALID_PARAMETER if the transaction does not + exist in the wallet. Previously returned RPC_INTERNAL_ERROR. + - `fundrawtransaction` now returns RPC_INVALID_ADDRESS_OR_KEY if an invalid change + address is provided. Previously returned RPC_INVALID_PARAMETER. + - `fundrawtransaction` now returns RPC_WALLET_ERROR if bitcoind is unable to create + the transaction. The error message provides further details. Previously returned + RPC_INTERNAL_ERROR. + - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has + descendants in the wallet. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has + descendants in the mempool. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has + has been mined or conflicts with a mined transaction. Previously returned + RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction is not + BIP 125 replaceable. Previously returned RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has already + been bumped by a different transaction. Previously returned RPC_INVALID_REQUEST. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction contains + inputs which don't belong to this wallet. Previously returned RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has multiple change + outputs. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has no change + output. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too high. Previously returned + RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too low. Previously returned + RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the change output is too small to bump the + fee. Previously returned RPC_MISC_ERROR. + miniupnp CVE-2017-8798 ---------------------------- diff --git a/qa/rpc-tests/bumpfee.py b/qa/rpc-tests/bumpfee.py index ac282796c14..7ed2beb176e 100755 --- a/qa/rpc-tests/bumpfee.py +++ b/qa/rpc-tests/bumpfee.py @@ -129,7 +129,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address): def test_nonrbf_bumpfee_fails(peer_node, dest_address): # cannot replace a non RBF transaction (from node which did not enable RBF) not_rbfid = create_fund_sign_send(peer_node, {dest_address: 0.00090000}) - assert_raises_message(JSONRPCException, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) + assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): @@ -149,7 +149,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): signedtx = rbf_node.signrawtransaction(rawtx) signedtx = peer_node.signrawtransaction(signedtx["hex"]) rbfid = rbf_node.sendrawtransaction(signedtx["hex"]) - assert_raises_message(JSONRPCException, "Transaction contains inputs that don't belong to this wallet", + assert_raises_jsonrpc(-4, "Transaction contains inputs that don't belong to this wallet", rbf_node.bumpfee, rbfid) @@ -160,7 +160,7 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) tx = rbf_node.signrawtransaction(tx) txid = rbf_node.sendrawtransaction(tx["hex"]) - assert_raises_message(JSONRPCException, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) + assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) def test_small_output_fails(rbf_node, dest_address): @@ -175,7 +175,7 @@ def test_small_output_fails(rbf_node, dest_address): Decimal("0.00100000"), {dest_address: 0.00080000, get_change_address(rbf_node): Decimal("0.00010000")}) - assert_raises_message(JSONRPCException, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 20001}) + assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 20001}) def test_dust_to_fee(rbf_node, dest_address): @@ -210,7 +210,7 @@ def test_rebumping(rbf_node, dest_address): rbf_node.settxfee(Decimal("0.00001000")) rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 1000}) - assert_raises_message(JSONRPCException, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 2000}) + assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 2000}) rbf_node.bumpfee(bumped["txid"], {"totalFee": 2000}) @@ -218,7 +218,7 @@ def test_rebumping_not_replaceable(rbf_node, dest_address): # check that re-bumping a non-replaceable bump tx fails rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False}) - assert_raises_message(JSONRPCException, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], + assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], {"totalFee": 20000}) @@ -269,7 +269,7 @@ def test_bumpfee_metadata(rbf_node, dest_address): def test_locked_wallet_fails(rbf_node, dest_address): rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) rbf_node.walletlock() - assert_raises_message(JSONRPCException, "Please enter the wallet passphrase with walletpassphrase first.", + assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.", rbf_node.bumpfee, rbfid) @@ -316,9 +316,7 @@ def submit_block_with_tx(node, tx): block.rehash() block.hashMerkleRoot = block.calc_merkle_root() block.solve() - error = node.submitblock(bytes_to_hex_str(block.serialize(True))) - if error is not None: - raise Exception(error) + node.submitblock(bytes_to_hex_str(block.serialize(True))) return block diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index cdea390e224..511faf740eb 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -186,12 +186,7 @@ class RawTransactionsTest(BitcoinTestFramework): dec_tx = self.nodes[2].decoderawtransaction(rawtx) assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - try: - self.nodes[2].fundrawtransaction(rawtx, {'foo': 'bar'}) - raise AssertionError("Accepted invalid option foo") - except JSONRPCException as e: - assert("Unexpected key foo" in e.error['message']) - + assert_raises_jsonrpc(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'}) ############################################################ # test a fundrawtransaction with an invalid change address # @@ -204,12 +199,7 @@ class RawTransactionsTest(BitcoinTestFramework): dec_tx = self.nodes[2].decoderawtransaction(rawtx) assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - try: - self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': 'foobar'}) - raise AssertionError("Accepted invalid bitcoin address") - except JSONRPCException as e: - assert("changeAddress must be a valid bitcoin address" in e.error['message']) - + assert_raises_jsonrpc(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'}) ############################################################ # test a fundrawtransaction with a provided change address # @@ -223,12 +213,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) change = self.nodes[2].getnewaddress() - try: - rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 2}) - except JSONRPCException as e: - assert('changePosition out of bounds' == e.error['message']) - else: - assert(False) + assert_raises_jsonrpc(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2}) rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0}) dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) out = dec_tx['vout'][0] @@ -337,12 +322,7 @@ class RawTransactionsTest(BitcoinTestFramework): rawtx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawtx) - try: - rawtxfund = self.nodes[2].fundrawtransaction(rawtx) - raise AssertionError("Spent more than available") - except JSONRPCException as e: - assert("Insufficient" in e.error['message']) - + assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx) ############################################################ #compare fee of a standard pubkeyhash transaction @@ -498,21 +478,13 @@ class RawTransactionsTest(BitcoinTestFramework): rawTx = self.nodes[1].createrawtransaction(inputs, outputs) # fund a transaction that requires a new key for the change output # creating the key must be impossible because the wallet is locked - try: - fundedTx = self.nodes[1].fundrawtransaction(rawTx) - raise AssertionError("Wallet unlocked without passphrase") - except JSONRPCException as e: - assert('Keypool ran out' in e.error['message']) + assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[1].fundrawtransaction, rawtx) #refill the keypool self.nodes[1].walletpassphrase("test", 100) self.nodes[1].walletlock() - try: - self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.2) - raise AssertionError("Wallet unlocked without passphrase") - except JSONRPCException as e: - assert('walletpassphrase' in e.error['message']) + assert_raises_jsonrpc(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2) oldBalance = self.nodes[0].getbalance() diff --git a/qa/rpc-tests/importprunedfunds.py b/qa/rpc-tests/importprunedfunds.py index 0dee8ad4ec9..b289f9be575 100755 --- a/qa/rpc-tests/importprunedfunds.py +++ b/qa/rpc-tests/importprunedfunds.py @@ -76,12 +76,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): self.sync_all() #Import with no affiliated address - try: - self.nodes[1].importprunedfunds(rawtxn1, proof1) - except JSONRPCException as e: - assert('No addresses' in e.error['message']) - else: - assert(False) + assert_raises_jsonrpc(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) balance1 = self.nodes[1].getbalance("", 0, True) assert_equal(balance1, Decimal(0)) @@ -112,12 +107,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(address_info['ismine'], True) #Remove transactions - try: - self.nodes[1].removeprunedfunds(txnid1) - except JSONRPCException as e: - assert('does not exist' in e.error['message']) - else: - assert(False) + assert_raises_jsonrpc(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1) balance1 = self.nodes[1].getbalance("*", 0, True) assert_equal(balance1, Decimal('0.075')) diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index 7313c79b54f..61be27ae2b6 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -7,79 +7,83 @@ # Test node handling # +from test_framework.mininode import wait_until from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import (assert_equal, + assert_raises_jsonrpc, + connect_nodes_bi, + start_node, + stop_node, + ) -import urllib.parse - -class NodeHandlingTest (BitcoinTestFramework): +class NodeHandlingTest(BitcoinTestFramework): def __init__(self): super().__init__() - self.num_nodes = 4 + self.num_nodes = 2 self.setup_clean_chain = False + def setup_network(self): + self.nodes = self.setup_nodes() + connect_nodes_bi(self.nodes, 0, 1) + def run_test(self): ########################### # setban/listbanned tests # ########################### - assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point - self.nodes[2].setban("127.0.0.1", "add") - time.sleep(3) #wait till the nodes are disconected - assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point - assert_equal(len(self.nodes[2].listbanned()), 1) - self.nodes[2].clearbanned() - assert_equal(len(self.nodes[2].listbanned()), 0) - self.nodes[2].setban("127.0.0.0/24", "add") - assert_equal(len(self.nodes[2].listbanned()), 1) - try: - self.nodes[2].setban("127.0.0.1", "add") #throws exception because 127.0.0.1 is within range 127.0.0.0/24 - except: - pass - assert_equal(len(self.nodes[2].listbanned()), 1) #still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24 - try: - self.nodes[2].setban("127.0.0.1", "remove") - except: - pass - assert_equal(len(self.nodes[2].listbanned()), 1) - self.nodes[2].setban("127.0.0.0/24", "remove") - assert_equal(len(self.nodes[2].listbanned()), 0) - self.nodes[2].clearbanned() - assert_equal(len(self.nodes[2].listbanned()), 0) + assert_equal(len(self.nodes[1].getpeerinfo()), 2) # node1 should have 2 connections to node0 at this point + self.nodes[1].setban("127.0.0.1", "add") + assert wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10) + assert_equal(len(self.nodes[1].getpeerinfo()), 0) # all nodes must be disconnected at this point + assert_equal(len(self.nodes[1].listbanned()), 1) + self.nodes[1].clearbanned() + assert_equal(len(self.nodes[1].listbanned()), 0) + self.nodes[1].setban("127.0.0.0/24", "add") + assert_equal(len(self.nodes[1].listbanned()), 1) + # This will throw an exception because 127.0.0.1 is within range 127.0.0.0/24 + assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add") + # This will throw an exception because 127.0.0.1/42 is not a real subnet + assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add") + assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24 + # This will throw an exception because 127.0.0.1 was not added above + assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove") + assert_equal(len(self.nodes[1].listbanned()), 1) + self.nodes[1].setban("127.0.0.0/24", "remove") + assert_equal(len(self.nodes[1].listbanned()), 0) + self.nodes[1].clearbanned() + assert_equal(len(self.nodes[1].listbanned()), 0) - ##test persisted banlist - self.nodes[2].setban("127.0.0.0/32", "add") - self.nodes[2].setban("127.0.0.0/24", "add") - self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds - self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds - listBeforeShutdown = self.nodes[2].listbanned() - assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) #must be here - time.sleep(2) #make 100% sure we expired 192.168.0.1 node time + # test persisted banlist + self.nodes[1].setban("127.0.0.0/32", "add") + self.nodes[1].setban("127.0.0.0/24", "add") + self.nodes[1].setban("192.168.0.1", "add", 1) # ban for 1 seconds + self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) # ban for 1000 seconds + listBeforeShutdown = self.nodes[1].listbanned() + assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) + assert wait_until(lambda: len(self.nodes[1].listbanned()) == 3, timeout=10) - #stop node - stop_node(self.nodes[2], 2) + stop_node(self.nodes[1], 1) - self.nodes[2] = start_node(2, self.options.tmpdir) - listAfterShutdown = self.nodes[2].listbanned() + self.nodes[1] = start_node(1, self.options.tmpdir) + listAfterShutdown = self.nodes[1].listbanned() assert_equal("127.0.0.0/24", listAfterShutdown[0]['address']) assert_equal("127.0.0.0/32", listAfterShutdown[1]['address']) assert_equal("/19" in listAfterShutdown[2]['address'], True) + # Clear ban lists + self.nodes[1].clearbanned() + connect_nodes_bi(self.nodes, 0, 1) + ########################### # RPC disconnectnode test # ########################### - url = urllib.parse.urlparse(self.nodes[1].url) - self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1))) - time.sleep(2) #disconnecting a node needs a little bit of time - for node in self.nodes[0].getpeerinfo(): - assert(node['addr'] != url.hostname+":"+str(p2p_port(1))) + address1 = self.nodes[0].getpeerinfo()[0]['addr'] + self.nodes[0].disconnectnode(address=address1) + assert wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) + assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1] - connect_nodes_bi(self.nodes,0,1) #reconnect the node - found = False - for node in self.nodes[0].getpeerinfo(): - if node['addr'] == url.hostname+":"+str(p2p_port(1)): - found = True - assert(found) + connect_nodes_bi(self.nodes, 0, 1) # reconnect the node + assert [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1] if __name__ == '__main__': - NodeHandlingTest ().main () + NodeHandlingTest().main() diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index 015ec34effa..4ebf2766202 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -199,11 +199,8 @@ class AcceptBlockTest(BitcoinTestFramework): assert_equal(x['status'], "headers-only") # But this block should be accepted by node0 since it has more work. - try: - self.nodes[0].getblock(blocks_h3[0].hash) - print("Unrequested more-work block accepted from non-whitelisted peer") - except: - raise AssertionError("Unrequested more work block was not processed") + self.nodes[0].getblock(blocks_h3[0].hash) + print("Unrequested more-work block accepted from non-whitelisted peer") # Node1 should have accepted and reorged. assert_equal(self.nodes[1].getblockcount(), 3) @@ -227,26 +224,17 @@ class AcceptBlockTest(BitcoinTestFramework): tips[j] = next_block time.sleep(2) - for x in all_blocks: - try: - self.nodes[0].getblock(x.hash) - if x == all_blocks[287]: - raise AssertionError("Unrequested block too far-ahead should have been ignored") - except: - if x == all_blocks[287]: - print("Unrequested block too far-ahead not processed") - else: - raise AssertionError("Unrequested block with more work should have been accepted") + # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead + for x in all_blocks[:-1]: + self.nodes[0].getblock(x.hash) + assert_raises_jsonrpc(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash) headers_message.headers.pop() # Ensure the last block is unrequested white_node.send_message(headers_message) # Send headers leading to tip white_node.send_message(msg_block(tips[1])) # Now deliver the tip - try: - white_node.sync_with_ping() - self.nodes[1].getblock(tips[1].hash) - print("Unrequested block far ahead of tip accepted from whitelisted peer") - except: - raise AssertionError("Unrequested block from whitelisted peer not accepted") + white_node.sync_with_ping() + self.nodes[1].getblock(tips[1].hash) + print("Unrequested block far ahead of tip accepted from whitelisted peer") # 5. Test handling of unrequested block on the node that didn't process # Should still not be processed (even though it has a child that has more diff --git a/qa/rpc-tests/prioritise_transaction.py b/qa/rpc-tests/prioritise_transaction.py index b7459c80cbd..16874a6b53b 100755 --- a/qa/rpc-tests/prioritise_transaction.py +++ b/qa/rpc-tests/prioritise_transaction.py @@ -16,7 +16,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework): def __init__(self): super().__init__() self.setup_clean_chain = True - self.num_nodes = 1 + self.num_nodes = 2 self.txouts = gen_return_txouts() @@ -25,8 +25,11 @@ class PrioritiseTransactionTest(BitcoinTestFramework): self.is_network_split = False self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-printpriority=1"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-printpriority=1"])) + connect_nodes(self.nodes[0], 1) self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] + def run_test(self): utxo_count = 90 utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], utxo_count) @@ -139,5 +142,16 @@ class PrioritiseTransactionTest(BitcoinTestFramework): assert_equal(self.nodes[0].sendrawtransaction(tx2_hex), tx2_id) assert(tx2_id in self.nodes[0].getrawmempool()) + # Test that calling prioritisetransaction is sufficient to trigger + # getblocktemplate to (eventually) return a new block. + mock_time = int(time.time()) + self.nodes[0].setmocktime(mock_time) + template = self.nodes[0].getblocktemplate() + self.nodes[0].prioritisetransaction(txid, 0, -int(self.relayfee*COIN)) + self.nodes[0].setmocktime(mock_time+10) + new_template = self.nodes[0].getblocktemplate() + + assert(template != new_template) + if __name__ == '__main__': PrioritiseTransactionTest().main() diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 1e6d4191869..3308bd1dc5e 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -186,11 +186,8 @@ class PruneTest(BitcoinTestFramework): def reorg_back(self): # Verify that a block on the old main chain fork has been pruned away - try: - self.nodes[2].getblock(self.forkhash) - raise AssertionError("Old block wasn't pruned so can't test redownload") - except JSONRPCException as e: - print("Will need to redownload block",self.forkheight) + assert_raises_jsonrpc(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash) + print("Will need to redownload block",self.forkheight) # Verify that we have enough history to reorg back to the fork point # Although this is more than 288 blocks, because this chain was written more recently @@ -235,7 +232,7 @@ class PruneTest(BitcoinTestFramework): # at this point, node has 995 blocks and has not yet run in prune mode node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0"], timewait=900) assert_equal(node.getblockcount(), 995) - assert_raises_message(JSONRPCException, "not in prune mode", node.pruneblockchain, 500) + assert_raises_jsonrpc(-1, "not in prune mode", node.pruneblockchain, 500) self.stop_node(node_number) # now re-start in manual pruning mode @@ -267,14 +264,14 @@ class PruneTest(BitcoinTestFramework): return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index)) # should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000) - assert_raises_message(JSONRPCException, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) + assert_raises_jsonrpc(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) # mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight) node.generate(6) assert_equal(node.getblockchaininfo()["blocks"], 1001) # negative heights should raise an exception - assert_raises_message(JSONRPCException, "Negative", node.pruneblockchain, -10) + assert_raises_jsonrpc(-8, "Negative", node.pruneblockchain, -10) # height=100 too low to prune first block file so this is a no-op prune(100) @@ -320,12 +317,9 @@ class PruneTest(BitcoinTestFramework): def wallet_test(self): # check that the pruning node's wallet is still in good shape print("Stop and start pruning node to trigger wallet rescan") - try: - self.stop_node(2) - start_node(2, self.options.tmpdir, ["-debug=1","-prune=550"]) - print("Success") - except Exception as detail: - raise AssertionError("Wallet test: unable to re-start the pruning node") + self.stop_node(2) + start_node(2, self.options.tmpdir, ["-debug=1","-prune=550"]) + print("Success") # check that wallet loads loads successfully when restarting a pruned node after IBD. # this was reported to fail in #7494. @@ -333,12 +327,9 @@ class PruneTest(BitcoinTestFramework): connect_nodes(self.nodes[0], 5) nds = [self.nodes[0], self.nodes[5]] sync_blocks(nds, wait=5, timeout=300) - try: - self.stop_node(5) #stop and start to trigger rescan - start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"]) - print ("Success") - except Exception as detail: - raise AssertionError("Wallet test: unable to re-start node5") + self.stop_node(5) #stop and start to trigger rescan + start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"]) + print ("Success") def run_test(self): print("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)") diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py index 33a6f2b0cca..2e27bbabb03 100755 --- a/qa/rpc-tests/rawtransactions.py +++ b/qa/rpc-tests/rawtransactions.py @@ -61,13 +61,8 @@ class RawTransactionsTest(BitcoinTestFramework): rawtx = self.nodes[2].createrawtransaction(inputs, outputs) rawtx = self.nodes[2].signrawtransaction(rawtx) - try: - rawtx = self.nodes[2].sendrawtransaction(rawtx['hex']) - except JSONRPCException as e: - assert("Missing inputs" in e.error['message']) - else: - assert(False) - + # This will raise an exception since there are missing inputs + assert_raises_jsonrpc(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) ######################### # RAW TX MULTISIG TESTS # @@ -161,13 +156,13 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" - assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, txHash, "Flase") + assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase") # 7. invalid parameters - supply txid and empty array - assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, txHash, []) + assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict - assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, txHash, {}) + assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {}) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } @@ -175,13 +170,15 @@ class RawTransactionsTest(BitcoinTestFramework): decrawtx= self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 1000) + # 9. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : -1}] outputs = { self.nodes[0].getnewaddress() : 1 } - assert_raises(JSONRPCException, self.nodes[0].createrawtransaction, inputs, outputs) + assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) + # 10. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967296}] outputs = { self.nodes[0].getnewaddress() : 1 } - assert_raises(JSONRPCException, self.nodes[0].createrawtransaction, inputs, outputs) + assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967294}] outputs = { self.nodes[0].getnewaddress() : 1 } diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 4d44b35bb61..99c25a1d4d9 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -89,6 +89,7 @@ BITCOIN_TESTS =\ test/blockencodings_tests.cpp \ test/bloom_tests.cpp \ test/bswap_tests.cpp \ + test/checkqueue_tests.cpp \ test/coins_tests.cpp \ test/compress_tests.cpp \ test/crypto_tests.cpp \ diff --git a/src/checkqueue.h b/src/checkqueue.h index 32e25d5c8c6..ea12df66dd0 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -127,6 +127,9 @@ private: } public: + //! Mutex to ensure only one concurrent CCheckQueueControl + boost::mutex ControlMutex; + //! Create a new check queue CCheckQueue(unsigned int nBatchSizeIn) : nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {} @@ -161,12 +164,6 @@ public: { } - bool IsIdle() - { - boost::unique_lock lock(mutex); - return (nTotal == nIdle && nTodo == 0 && fAllOk == true); - } - }; /** @@ -177,16 +174,18 @@ template class CCheckQueueControl { private: - CCheckQueue* pqueue; + CCheckQueue * const pqueue; bool fDone; public: - CCheckQueueControl(CCheckQueue* pqueueIn) : pqueue(pqueueIn), fDone(false) + CCheckQueueControl() = delete; + CCheckQueueControl(const CCheckQueueControl&) = delete; + CCheckQueueControl& operator=(const CCheckQueueControl&) = delete; + explicit CCheckQueueControl(CCheckQueue * const pqueueIn) : pqueue(pqueueIn), fDone(false) { // passed queue is supposed to be unused, or NULL if (pqueue != NULL) { - bool isIdle = pqueue->IsIdle(); - assert(isIdle); + ENTER_CRITICAL_SECTION(pqueue->ControlMutex); } } @@ -209,6 +208,9 @@ public: { if (!fDone) Wait(); + if (pqueue != NULL) { + LEAVE_CRITICAL_SECTION(pqueue->ControlMutex); + } } }; diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index e3f3e4621ad..78d7cd60010 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -65,7 +65,7 @@ uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::ve } else { // calculate left hash uint256 left = CalcHash(height-1, pos*2, vTxid), right; - // calculate right hash if not beyond the end of the array - copy left hash otherwise1 + // calculate right hash if not beyond the end of the array - copy left hash otherwise if (pos*2+1 < CalcTreeWidth(height-1)) right = CalcHash(height-1, pos*2+1, vTxid); else diff --git a/src/merkleblock.h b/src/merkleblock.h index 73cbf670ee9..de4c5c8d29e 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -23,7 +23,7 @@ * storing a bit for each traversed node, signifying whether the node is the * parent of at least one matched leaf txid (or a matched txid itself). In * case we are at the leaf level, or this bit is 0, its merkle node hash is - * stored, and its children are not explorer further. Otherwise, no hash is + * stored, and its children are not explored further. Otherwise, no hash is * stored, but we recurse into both (or the only) child branch. During * decoding, the same depth-first traversal is performed, consuming bits and * hashes as they written during encoding. diff --git a/src/net.cpp b/src/net.cpp index 1752c7707a7..5bc886c3408 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -413,10 +413,10 @@ void CConnman::DumpBanlist() CBanDB bandb; banmap_t banmap; - SetBannedSetDirty(false); GetBanned(banmap); - if (!bandb.Write(banmap)) - SetBannedSetDirty(true); + if (bandb.Write(banmap)) { + SetBannedSetDirty(false); + } LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); @@ -536,6 +536,8 @@ bool CConnman::Unban(const CSubNet &subNet) { void CConnman::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); + // Sweep the banlist so expired bans are not returned + SweepBanned(); banMap = setBanned; //create a thread safe copy } diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index ca8ecffafea..f13b0d9cf92 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -759,11 +759,33 @@ + + + + Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain. + + + + 75 + true + + + + Warning: Fee estimation is currently not possible. + + + false + + + Qt::Horizontal + + QSizePolicy::MinimumExpanding + 40 diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 1c0ed663c1f..65ca0075520 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -23,6 +23,7 @@ #include "txmempool.h" #include "wallet/wallet.h" +#include #include #include #include @@ -656,6 +657,11 @@ void SendCoinsDialog::updateSmartFeeLabel() std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelFeeEstimation->setText(""); + ui->fallbackFeeWarningLabel->setVisible(true); + int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness(); + QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14)); + ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }"); + ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x")); } else { @@ -663,6 +669,7 @@ void SendCoinsDialog::updateSmartFeeLabel() std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); ui->labelSmartFee2->hide(); ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks)); + ui->fallbackFeeWarningLabel->setVisible(false); } updateFeeMinimizedLabel(); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 7b69c81ff9c..fd8f52a5cb0 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -417,6 +417,7 @@ UniValue getrawmempool(const JSONRPCRequest& request) throw runtime_error( "getrawmempool ( verbose )\n" "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" + "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n" "\nArguments:\n" "1. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n" "\nResult: (for verbose = false):\n" @@ -744,10 +745,15 @@ UniValue getblock(const JSONRPCRequest& request) CBlockIndex* pblockindex = mapBlockIndex[hash]; if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); - if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + // Block not found on disk. This could be because we have the block + // header in our index but don't have the block (for example if a + // non-whitelisted node sends us an unrequested long chain of valid + // blocks, we add the headers to our index, but don't accept the + // block). + throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); if (!fVerbose) { @@ -829,7 +835,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request) + HelpExampleRpc("pruneblockchain", "1000")); if (!fPruneMode) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune blocks because node is not in prune mode."); + throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode."); LOCK(cs_main); @@ -843,7 +849,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request) // Add a 2 hour buffer to include blocks which might have had old timestamps CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - 7200); if (!pindex) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not find block with at least the specified timestamp."); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp."); } heightParam = pindex->nHeight; } @@ -851,7 +857,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request) unsigned int height = (unsigned int) heightParam; unsigned int chainHeight = (unsigned int) chainActive.Height(); if (chainHeight < Params().PruneAfterHeight()) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Blockchain is too short for pruning."); + throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning."); else if (height > chainHeight) throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 8706b429515..42f5db94f94 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -506,7 +506,7 @@ UniValue setban(const JSONRPCRequest& request) LookupSubNet(request.params[0].get_str().c_str(), subNet); if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) - throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet"); + throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Invalid IP/Subnet"); if (strCommand == "add") { @@ -526,7 +526,7 @@ UniValue setban(const JSONRPCRequest& request) else if(strCommand == "remove") { if (!( isSubnet ? g_connman->Unban(subNet) : g_connman->Unban(netAddr) )) - throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); + throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously banned."); } return NullUniValue; } diff --git a/src/streams.h b/src/streams.h index 1d3b55c91ef..e49a9e35469 100644 --- a/src/streams.h +++ b/src/streams.h @@ -248,7 +248,8 @@ public: void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) { - assert(last - first >= 0); + if (last == first) return; + assert(last - first > 0); if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) { // special case for inserting at the front when there's room @@ -261,7 +262,8 @@ public: void insert(iterator it, const char* first, const char* last) { - assert(last - first >= 0); + if (last == first) return; + assert(last - first > 0); if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) { // special case for inserting at the front when there's room @@ -339,6 +341,8 @@ public: void read(char* pch, size_t nSize) { + if (nSize == 0) return; + // Read from the beginning of the buffer unsigned int nReadPosNext = nReadPos + nSize; if (nReadPosNext >= vch.size()) diff --git a/src/support/cleanse.h b/src/support/cleanse.h index 3e02aa8fd1f..f020216c730 100644 --- a/src/support/cleanse.h +++ b/src/support/cleanse.h @@ -8,6 +8,7 @@ #include +// Attempt to overwrite data in the specified memory span. void memory_cleanse(void *ptr, size_t len); #endif // BITCOIN_SUPPORT_CLEANSE_H diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp new file mode 100644 index 00000000000..d89f9b770bc --- /dev/null +++ b/src/test/checkqueue_tests.cpp @@ -0,0 +1,442 @@ +// Copyright (c) 2012-2017 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 "util.h" +#include "utiltime.h" +#include "validation.h" + +#include "test/test_bitcoin.h" +#include "checkqueue.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "random.h" + +// BasicTestingSetup not sufficient because nScriptCheckThreads is not set +// otherwise. +BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup) + +static const int QUEUE_BATCH_SIZE = 128; + +struct FakeCheck { + bool operator()() + { + return true; + } + void swap(FakeCheck& x){}; +}; + +struct FakeCheckCheckCompletion { + static std::atomic n_calls; + bool operator()() + { + ++n_calls; + return true; + } + void swap(FakeCheckCheckCompletion& x){}; +}; + +struct FailingCheck { + bool fails; + FailingCheck(bool fails) : fails(fails){}; + FailingCheck() : fails(true){}; + bool operator()() + { + return !fails; + } + void swap(FailingCheck& x) + { + std::swap(fails, x.fails); + }; +}; + +struct UniqueCheck { + static std::mutex m; + static std::unordered_multiset results; + size_t check_id; + UniqueCheck(size_t check_id_in) : check_id(check_id_in){}; + UniqueCheck() : check_id(0){}; + bool operator()() + { + std::lock_guard l(m); + results.insert(check_id); + return true; + } + void swap(UniqueCheck& x) { std::swap(x.check_id, check_id); }; +}; + + +struct MemoryCheck { + static std::atomic fake_allocated_memory; + bool b {false}; + bool operator()() + { + return true; + } + MemoryCheck(){}; + MemoryCheck(const MemoryCheck& x) + { + // We have to do this to make sure that destructor calls are paired + // + // Really, copy constructor should be deletable, but CCheckQueue breaks + // if it is deleted because of internal push_back. + fake_allocated_memory += b; + }; + MemoryCheck(bool b_) : b(b_) + { + fake_allocated_memory += b; + }; + ~MemoryCheck(){ + fake_allocated_memory -= b; + + }; + void swap(MemoryCheck& x) { std::swap(b, x.b); }; +}; + +struct FrozenCleanupCheck { + static std::atomic nFrozen; + static std::condition_variable cv; + static std::mutex m; + // Freezing can't be the default initialized behavior given how the queue + // swaps in default initialized Checks. + bool should_freeze {false}; + bool operator()() + { + return true; + } + FrozenCleanupCheck() {} + ~FrozenCleanupCheck() + { + if (should_freeze) { + std::unique_lock l(m); + nFrozen = 1; + cv.notify_one(); + cv.wait(l, []{ return nFrozen == 0;}); + } + } + void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);}; +}; + +// Static Allocations +std::mutex FrozenCleanupCheck::m{}; +std::atomic FrozenCleanupCheck::nFrozen{0}; +std::condition_variable FrozenCleanupCheck::cv{}; +std::mutex UniqueCheck::m; +std::unordered_multiset UniqueCheck::results; +std::atomic FakeCheckCheckCompletion::n_calls{0}; +std::atomic MemoryCheck::fake_allocated_memory{0}; + +// Queue Typedefs +typedef CCheckQueue Correct_Queue; +typedef CCheckQueue Standard_Queue; +typedef CCheckQueue Failing_Queue; +typedef CCheckQueue Unique_Queue; +typedef CCheckQueue Memory_Queue; +typedef CCheckQueue FrozenCleanup_Queue; + + +/** This test case checks that the CCheckQueue works properly + * with each specified size_t Checks pushed. + */ +void Correct_Queue_range(std::vector range) +{ + auto small_queue = std::unique_ptr(new Correct_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{small_queue->Thread();}); + } + // Make vChecks here to save on malloc (this test can be slow...) + std::vector vChecks; + for (auto i : range) { + size_t total = i; + FakeCheckCheckCompletion::n_calls = 0; + CCheckQueueControl control(small_queue.get()); + while (total) { + vChecks.resize(std::min(total, (size_t) GetRand(10))); + total -= vChecks.size(); + control.Add(vChecks); + } + BOOST_REQUIRE(control.Wait()); + if (FakeCheckCheckCompletion::n_calls != i) { + BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i); + BOOST_TEST_MESSAGE("Failure on trial " << i << " expected, got " << FakeCheckCheckCompletion::n_calls); + } + } + tg.interrupt_all(); + tg.join_all(); +} + +/** Test that 0 checks is correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Zero) +{ + std::vector range; + range.push_back((size_t)0); + Correct_Queue_range(range); +} +/** Test that 1 check is correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_One) +{ + std::vector range; + range.push_back((size_t)1); + Correct_Queue_range(range); +} +/** Test that MAX check is correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Max) +{ + std::vector range; + range.push_back(100000); + Correct_Queue_range(range); +} +/** Test that random numbers of checks are correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) +{ + std::vector range; + range.reserve(100000/1000); + for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)GetRand(std::min((size_t)1000, ((size_t)100000) - i)))) + range.push_back(i); + Correct_Queue_range(range); +} + + +/** Test that failing checks are caught */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) +{ + auto fail_queue = std::unique_ptr(new Failing_Queue {QUEUE_BATCH_SIZE}); + + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{fail_queue->Thread();}); + } + + for (size_t i = 0; i < 1001; ++i) { + CCheckQueueControl control(fail_queue.get()); + size_t remaining = i; + while (remaining) { + size_t r = GetRand(10); + + std::vector vChecks; + vChecks.reserve(r); + for (size_t k = 0; k < r && remaining; k++, remaining--) + vChecks.emplace_back(remaining == 1); + control.Add(vChecks); + } + bool success = control.Wait(); + if (i > 0) { + BOOST_REQUIRE(!success); + } else if (i == 0) { + BOOST_REQUIRE(success); + } + } + tg.interrupt_all(); + tg.join_all(); +} +// Test that a block validation which fails does not interfere with +// future blocks, ie, the bad state is cleared. +BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) +{ + auto fail_queue = std::unique_ptr(new Failing_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{fail_queue->Thread();}); + } + + for (auto times = 0; times < 10; ++times) { + for (bool end_fails : {true, false}) { + CCheckQueueControl control(fail_queue.get()); + { + std::vector vChecks; + vChecks.resize(100, false); + vChecks[99] = end_fails; + control.Add(vChecks); + } + bool r =control.Wait(); + BOOST_REQUIRE(r || end_fails); + } + } + tg.interrupt_all(); + tg.join_all(); +} + +// Test that unique checks are actually all called individually, rather than +// just one check being called repeatedly. Test that checks are not called +// more than once as well +BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) +{ + auto queue = std::unique_ptr(new Unique_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{queue->Thread();}); + + } + + size_t COUNT = 100000; + size_t total = COUNT; + { + CCheckQueueControl control(queue.get()); + while (total) { + size_t r = GetRand(10); + std::vector vChecks; + for (size_t k = 0; k < r && total; k++) + vChecks.emplace_back(--total); + control.Add(vChecks); + } + } + bool r = true; + BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT); + for (size_t i = 0; i < COUNT; ++i) + r = r && UniqueCheck::results.count(i) == 1; + BOOST_REQUIRE(r); + tg.interrupt_all(); + tg.join_all(); +} + + +// Test that blocks which might allocate lots of memory free their memory agressively. +// +// This test attempts to catch a pathological case where by lazily freeing +// checks might mean leaving a check un-swapped out, and decreasing by 1 each +// time could leave the data hanging across a sequence of blocks. +BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) +{ + auto queue = std::unique_ptr(new Memory_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{queue->Thread();}); + } + for (size_t i = 0; i < 1000; ++i) { + size_t total = i; + { + CCheckQueueControl control(queue.get()); + while (total) { + size_t r = GetRand(10); + std::vector vChecks; + for (size_t k = 0; k < r && total; k++) { + total--; + // Each iteration leaves data at the front, back, and middle + // to catch any sort of deallocation failure + vChecks.emplace_back(total == 0 || total == i || total == i/2); + } + control.Add(vChecks); + } + } + BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0); + } + tg.interrupt_all(); + tg.join_all(); +} + +// Test that a new verification cannot occur until all checks +// have been destructed +BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) +{ + auto queue = std::unique_ptr(new FrozenCleanup_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + bool fails = false; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{queue->Thread();}); + } + std::thread t0([&]() { + CCheckQueueControl control(queue.get()); + std::vector vChecks(1); + // Freezing can't be the default initialized behavior given how the queue + // swaps in default initialized Checks (otherwise freezing destructor + // would get called twice). + vChecks[0].should_freeze = true; + control.Add(vChecks); + control.Wait(); // Hangs here + }); + { + std::unique_lock l(FrozenCleanupCheck::m); + // Wait until the queue has finished all jobs and frozen + FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;}); + // Try to get control of the queue a bunch of times + for (auto x = 0; x < 100 && !fails; ++x) { + fails = queue->ControlMutex.try_lock(); + } + // Unfreeze + FrozenCleanupCheck::nFrozen = 0; + } + // Awaken frozen destructor + FrozenCleanupCheck::cv.notify_one(); + // Wait for control to finish + t0.join(); + tg.interrupt_all(); + tg.join_all(); + BOOST_REQUIRE(!fails); +} + + +/** Test that CCheckQueueControl is threadsafe */ +BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) +{ + auto queue = std::unique_ptr(new Standard_Queue{QUEUE_BATCH_SIZE}); + { + boost::thread_group tg; + std::atomic nThreads {0}; + std::atomic fails {0}; + for (size_t i = 0; i < 3; ++i) { + tg.create_thread( + [&]{ + CCheckQueueControl control(queue.get()); + // While sleeping, no other thread should execute to this point + auto observed = ++nThreads; + MilliSleep(10); + fails += observed != nThreads; + }); + } + tg.join_all(); + BOOST_REQUIRE_EQUAL(fails, 0); + } + { + boost::thread_group tg; + std::mutex m; + bool has_lock {false}; + bool has_tried {false}; + bool done {false}; + bool done_ack {false}; + std::condition_variable cv; + { + std::unique_lock l(m); + tg.create_thread([&]{ + CCheckQueueControl control(queue.get()); + std::unique_lock l(m); + has_lock = true; + cv.notify_one(); + cv.wait(l, [&]{return has_tried;}); + done = true; + cv.notify_one(); + // Wait until the done is acknowledged + // + cv.wait(l, [&]{return done_ack;}); + }); + // Wait for thread to get the lock + cv.wait(l, [&](){return has_lock;}); + bool fails = false; + for (auto x = 0; x < 100 && !fails; ++x) { + fails = queue->ControlMutex.try_lock(); + } + has_tried = true; + cv.notify_one(); + cv.wait(l, [&](){return done;}); + // Acknowledge the done + done_ack = true; + cv.notify_one(); + BOOST_REQUIRE(!fails); + } + tg.join_all(); + } +} +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/txdb.cpp b/src/txdb.cpp index 1a30bb58ad8..662e7cb06d3 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -98,7 +98,11 @@ CCoinsViewCursor *CCoinsViewDB::Cursor() const that restriction. */ i->pcursor->Seek(DB_COINS); // Cache key of first record - i->pcursor->GetKey(i->keyTmp); + if (i->pcursor->Valid()) { + i->pcursor->GetKey(i->keyTmp); + } else { + i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false + } return i; } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 91040fb9b21..72547b58282 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -945,6 +945,7 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string str BOOST_FOREACH(txiter descendantIt, setDescendants) { mapTx.modify(descendantIt, update_ancestor_state(0, nFeeDelta, 0, 0)); } + ++nTransactionsUpdated; } } LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta)); diff --git a/src/txmempool.h b/src/txmempool.h index db1a02455f8..12c9e59f5f0 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -432,7 +432,7 @@ class CTxMemPool { private: uint32_t nCheckFrequency; //!< Value n means that n times in 2^32 we check. - unsigned int nTransactionsUpdated; + unsigned int nTransactionsUpdated; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* minerPolicyEstimator; uint64_t totalTxSize; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141. diff --git a/src/wallet/db.h b/src/wallet/db.h index b4ce044e7f5..ea562003230 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -126,22 +126,23 @@ protected: Dbt datValue; datValue.set_flags(DB_DBT_MALLOC); int ret = pdb->get(activeTxn, &datKey, &datValue, 0); - memset(datKey.get_data(), 0, datKey.get_size()); - if (datValue.get_data() == NULL) - return false; + memory_cleanse(datKey.get_data(), datKey.get_size()); + bool success = false; + if (datValue.get_data() != NULL) { + // Unserialize value + try { + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + success = true; + } catch (const std::exception&) { + // In this case success remains 'false' + } - // Unserialize value - try { - CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); - ssValue >> value; - } catch (const std::exception&) { - return false; + // Clear and free memory + memory_cleanse(datValue.get_data(), datValue.get_size()); + free(datValue.get_data()); } - - // Clear and free memory - memset(datValue.get_data(), 0, datValue.get_size()); - free(datValue.get_data()); - return (ret == 0); + return ret == 0 && success; } template @@ -168,8 +169,8 @@ protected: int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); // Clear memory in case it was a private key - memset(datKey.get_data(), 0, datKey.get_size()); - memset(datValue.get_data(), 0, datValue.get_size()); + memory_cleanse(datKey.get_data(), datKey.get_size()); + memory_cleanse(datValue.get_data(), datValue.get_size()); return (ret == 0); } @@ -191,7 +192,7 @@ protected: int ret = pdb->del(activeTxn, &datKey, 0); // Clear memory - memset(datKey.get_data(), 0, datKey.get_size()); + memory_cleanse(datKey.get_data(), datKey.get_size()); return (ret == 0 || ret == DB_NOTFOUND); } @@ -211,7 +212,7 @@ protected: int ret = pdb->exists(activeTxn, &datKey, 0); // Clear memory - memset(datKey.get_data(), 0, datKey.get_size()); + memory_cleanse(datKey.get_data(), datKey.get_size()); return (ret == 0); } @@ -254,8 +255,8 @@ protected: ssValue.write((char*)datValue.get_data(), datValue.get_size()); // Clear and free memory - memset(datKey.get_data(), 0, datKey.get_size()); - memset(datValue.get_data(), 0, datValue.get_size()); + memory_cleanse(datKey.get_data(), datKey.get_size()); + memory_cleanse(datValue.get_data(), datValue.get_size()); free(datKey.get_data()); free(datValue.get_data()); return 0; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index cd6ec207f90..dc61a4505ed 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -340,11 +340,11 @@ UniValue removeprunedfunds(const JSONRPCRequest& request) vector vHashOut; if(pwalletMain->ZapSelectTx(vHash, vHashOut) != DB_LOAD_OK) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not properly delete the transaction."); + throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction."); } if(vHashOut.empty()) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction does not exist in wallet."); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet."); } return NullUniValue; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5cf1592376f..7cc03201650 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2603,7 +2603,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) CBitcoinAddress address(options["changeAddress"].get_str()); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "changeAddress must be a valid bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address"); changeAddress = address.Get(); } @@ -2657,7 +2657,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) string strFailReason; if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, reserveChangeKey, changeAddress)) - throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); + throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", EncodeHexTx(tx))); @@ -2756,33 +2756,33 @@ UniValue bumpfee(const JSONRPCRequest& request) CWalletTx& wtx = pwalletMain->mapWallet[hash]; if (pwalletMain->HasWalletSpend(hash)) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the wallet"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "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"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "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"); + throw JSONRPCError(RPC_WALLET_ERROR, "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"); + throw JSONRPCError(RPC_WALLET_ERROR, "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"))); + throw JSONRPCError(RPC_WALLET_ERROR, 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"); + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction contains inputs that don't belong to this wallet"); } // figure out which output was change @@ -2791,13 +2791,13 @@ UniValue bumpfee(const JSONRPCRequest& request) 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"); + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction has multiple change outputs"); } nOutput = i; } } if (nOutput == -1) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output"); + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction does not have a change output"); } // Calculate the expected size of the new transaction. @@ -2888,7 +2888,7 @@ UniValue bumpfee(const JSONRPCRequest& request) // Check that in all cases the new fee doesn't violate maxTxFee if (nNewFee > maxTxFee) { - throw JSONRPCError(RPC_MISC_ERROR, + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)", FormatMoney(nNewFee), FormatMoney(maxTxFee))); } @@ -2900,7 +2900,7 @@ UniValue bumpfee(const JSONRPCRequest& request) // 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()))); + throw JSONRPCError(RPC_WALLET_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. @@ -2910,7 +2910,7 @@ UniValue bumpfee(const JSONRPCRequest& request) 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"); + throw JSONRPCError(RPC_WALLET_ERROR, "Change output is too small to bump the fee"); } // If the output would become dust, discard it (converting the dust to fee) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3e93a56d563..2697a3769ac 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1572,6 +1572,10 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f { 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)))); + if (GetTime() >= nNow + 60) { + nNow = GetTime(); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); + } CBlock block; if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { @@ -1585,10 +1589,6 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f 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 }