aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--contrib/gitian-descriptors/gitian-win-signer.yml15
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml7
-rw-r--r--doc/assets-attribution.md4
-rw-r--r--doc/release-process.md25
-rwxr-xr-xqa/pull-tester/rpc-tests.sh3
-rwxr-xr-xqa/rpc-tests/decodescript.py116
-rwxr-xr-xqa/rpc-tests/nodehandling.py20
-rwxr-xr-xqa/rpc-tests/txn_clone.py167
-rwxr-xr-xqa/rpc-tests/txn_doublespend.py79
-rw-r--r--src/init.cpp22
-rw-r--r--src/main.cpp7
-rw-r--r--src/main.h5
-rw-r--r--src/miner.cpp52
-rw-r--r--src/miner.h4
-rw-r--r--src/net.cpp237
-rw-r--r--src/net.h88
-rw-r--r--src/netbase.cpp7
-rw-r--r--src/netbase.h12
-rw-r--r--src/primitives/transaction.cpp9
-rw-r--r--src/primitives/transaction.h3
-rw-r--r--src/qt/optionsdialog.cpp2
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpcblockchain.cpp133
-rw-r--r--src/rpcclient.cpp1
-rw-r--r--src/rpcmining.cpp31
-rw-r--r--src/rpcmisc.cpp11
-rw-r--r--src/rpcnet.cpp14
-rw-r--r--src/rpcserver.cpp17
-rw-r--r--src/rpcserver.h1
-rw-r--r--src/script/script.h9
-rw-r--r--src/test/netbase_tests.cpp11
-rw-r--r--src/test/rpc_tests.cpp4
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/util.cpp10
-rw-r--r--src/util.h7
-rw-r--r--src/validationinterface.cpp6
-rw-r--r--src/validationinterface.h8
-rw-r--r--src/wallet/wallet.cpp12
-rw-r--r--src/wallet/wallet.h12
40 files changed, 1014 insertions, 165 deletions
diff --git a/README.md b/README.md
index 4fe6a87495..594d98c39f 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,10 @@ lots of money.
Developers are strongly encouraged to write unit tests for new code, and to
submit new unit tests for old code. Unit tests can be compiled and run (assuming they weren't disabled in configure) with: `make check`
+There are also regression and integration tests of the RPC interface, written
+in Python, that are run automatically on the build server.
+These tests can be run with: `qa/pull-tester/rpc-tests.sh`
+
Every pull request is built for both Windows and Linux on a dedicated server,
and unit and sanity tests are automatically run. The binaries produced may be
used for manual QA testing — a link to them will appear in a comment on the
diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml
index 0bf2ac9b2f..2a73050e0e 100644
--- a/contrib/gitian-descriptors/gitian-win-signer.yml
+++ b/contrib/gitian-descriptors/gitian-win-signer.yml
@@ -14,21 +14,26 @@ remotes:
files:
- "osslsigncode-1.7.1.tar.gz"
- "osslsigncode-Backports-to-1.7.1.patch"
-- "bitcoin-win32-setup.exe"
-- "bitcoin-win64-setup.exe"
+- "bitcoin-win-unsigned.tar.gz"
script: |
BUILD_DIR=`pwd`
SIGDIR=${BUILD_DIR}/signature/win
+ UNSIGNED_DIR=${BUILD_DIR}/unsigned
echo "f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 osslsigncode-1.7.1.tar.gz" | sha256sum -c
echo "a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 osslsigncode-Backports-to-1.7.1.patch" | sha256sum -c
+ mkdir -p ${UNSIGNED_DIR}
+ tar -C ${UNSIGNED_DIR} -xf bitcoin-win-unsigned.tar.gz
+
tar xf osslsigncode-1.7.1.tar.gz
cd osslsigncode-1.7.1
patch -p1 < ${BUILD_DIR}/osslsigncode-Backports-to-1.7.1.patch
./configure --without-gsf --without-curl --disable-dependency-tracking
make
-
- ./osslsigncode attach-signature -in ${BUILD_DIR}/bitcoin-win32-setup.exe -out ${OUTDIR}/bitcoin-win32-setup-signed.exe -sigin ${SIGDIR}/bitcoin-win32-setup.exe.pem
- ./osslsigncode attach-signature -in ${BUILD_DIR}/bitcoin-win64-setup.exe -out ${OUTDIR}/bitcoin-win64-setup-signed.exe -sigin ${SIGDIR}/bitcoin-win64-setup.exe.pem
+ find ${UNSIGNED_DIR} -name "*-unsigned.exe" | while read i; do
+ INFILE="`basename "${i}"`"
+ OUTFILE="`echo "${INFILE}" | sed s/-unsigned//`"
+ ./osslsigncode attach-signature -in "${i}" -out "${OUTDIR}/${OUTFILE}" -sigin "${SIGDIR}/${INFILE}.pem"
+ done
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 20cfb29859..966820ec51 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -84,6 +84,8 @@ script: |
pushd temp
tar xf ../$SOURCEDIST
find bitcoin-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST
+ mkdir -p $OUTDIR/src
+ cp ../$SOURCEDIST $OUTDIR/src
popd
ORIGPATH="$PATH"
@@ -109,7 +111,8 @@ script: |
find ${DISTNAME} -type f | sort | zip -X@ ${OUTDIR}/${DISTNAME}-${i}.zip
cd ../..
done
- mkdir -p $OUTDIR/src
- mv $SOURCEDIST $OUTDIR/src
+ cd $OUTDIR
+ rename 's/-setup\.exe$/-setup-unsigned.exe/' *-setup.exe
+ find . -name "*-setup-unsigned.exe" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz
mv ${OUTDIR}/${DISTNAME}-x86_64-*.zip ${OUTDIR}/${DISTNAME}-win64.zip
mv ${OUTDIR}/${DISTNAME}-i686-*.zip ${OUTDIR}/${DISTNAME}-win32.zip
diff --git a/doc/assets-attribution.md b/doc/assets-attribution.md
index ebba64a61a..c6da1a4586 100644
--- a/doc/assets-attribution.md
+++ b/doc/assets-attribution.md
@@ -6,7 +6,7 @@ The following is a list of assets used in the bitcoin source and their proper at
### Info
* Icon Pack: Typicons (http://typicons.com)
* Designer: Stephen Hutchings (and more)
-* License: CC BY-SA
+* License: MIT
* Site: [https://github.com/stephenhutchings/typicons.font](https://github.com/stephenhutchings/typicons.font)
### Assets Used
@@ -30,7 +30,7 @@ Jonas Schnelli
### Info
* Designer: Jonas Schnelli
* Bitcoin Icon: (based on the original bitcoin logo from Bitboy)
-* Some icons are based on Stephan Hutchings Typicons (these are under CC BY-SA license)
+* Some icons are based on Stephan Hutchings Typicons
* License: MIT
### Assets Used
diff --git a/doc/release-process.md b/doc/release-process.md
index 07005bfbb9..5ecb9334f5 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -25,7 +25,7 @@ Release Process
###update gitian
- In order to take advantage of the new caching features in gitian, be sure to update to a recent version (e9741525c or higher is recommended)
+ In order to take advantage of the new caching features in gitian, be sure to update to a recent version (`e9741525c` or later is recommended)
###perform gitian builds
@@ -66,22 +66,21 @@ Release Process
./bin/gsign --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml
mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../
./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
- ./bin/gsign --signer $SIGNER --release ${VERSION}-win --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
- mv build/out/bitcoin-*.zip ../
- mv build/out/bitcoin-*-win64-setup.exe inputs/bitcoin-win64-setup.exe
- mv build/out/bitcoin-*-win32-setup.exe inputs/bitcoin-win32-setup.exe
+ ./bin/gsign --signer $SIGNER --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
+ mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz
+ mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../
./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml
./bin/gsign --signer $SIGNER --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml
- mv build/out/bitcoin-*-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz
+ mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz
mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../
popd
Build output expected:
1. source tarball (bitcoin-${VERSION}.tar.gz)
- 2. linux 32-bit and 64-bit binaries dist tarballs (bitcoin-${VERSION}-linux[32|64].tar.gz)
- 3. windows 32-bit and 64-bit unsigned installers and dist zips (bitcoin-${VERSION}-win[32|64]-setup.exe, bitcoin-${VERSION}-win[32|64].zip)
- 4. OSX unsigned installer (bitcoin-${VERSION}-osx-unsigned.dmg)
- 5. Gitian signatures (in gitian.sigs/${VERSION}-<linux|win|osx-unsigned>/(your gitian key)/
+ 2. linux 32-bit and 64-bit dist tarballs (bitcoin-${VERSION}-linux[32|64].tar.gz)
+ 3. windows 32-bit and 64-bit unsigned installers and dist zips (bitcoin-${VERSION}-win[32|64]-setup-unsigned.exe, bitcoin-${VERSION}-win[32|64].zip)
+ 4. OSX unsigned installer and dist tarball (bitcoin-${VERSION}-osx-unsigned.dmg, bitcoin-${VERSION}-osx64.tar.gz)
+ 5. Gitian signatures (in gitian.sigs/${VERSION}-<linux|{win,osx}-unsigned>/(your gitian key)/
###Next steps:
@@ -89,7 +88,7 @@ Commit your signature to gitian.sigs:
pushd gitian.sigs
git add ${VERSION}-linux/${SIGNER}
- git add ${VERSION}-win/${SIGNER}
+ git add ${VERSION}-win-unsigned/${SIGNER}
git add ${VERSION}-osx-unsigned/${SIGNER}
git commit -a
git push # Assuming you can push to the gitian.sigs tree
@@ -112,8 +111,8 @@ Commit your signature to gitian.sigs:
pushd ./gitian-builder
./bin/gbuild -i --commit signature=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml
./bin/gsign --signer $SIGNER --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml
- mv build/out/bitcoin-win64-setup-signed.exe ../bitcoin-${VERSION}-win64-setup.exe
- mv build/out/bitcoin-win32-setup-signed.exe ../bitcoin-${VERSION}-win32-setup.exe
+ mv build/out/bitcoin-*win64-setup.exe ../bitcoin-${VERSION}-win64-setup.exe
+ mv build/out/bitcoin-*win32-setup.exe ../bitcoin-${VERSION}-win32-setup.exe
popd
Commit your signature for the signed OSX/Windows binaries:
diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh
index 2a7caf98de..b282082395 100755
--- a/qa/pull-tester/rpc-tests.sh
+++ b/qa/pull-tester/rpc-tests.sh
@@ -21,6 +21,8 @@ testScripts=(
'mempool_resurrect_test.py'
'txn_doublespend.py'
'txn_doublespend.py --mineblock'
+ 'txn_clone.py'
+ 'txn_clone.py --mineblock'
'getchaintips.py'
'rawtransactions.py'
'rest.py'
@@ -35,6 +37,7 @@ testScripts=(
'walletbackup.py'
'nodehandling.py'
'reindex.py'
+ 'decodescript.py'
);
testScriptsExt=(
'bipdersig-p2p.py'
diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py
new file mode 100755
index 0000000000..ce3bc94ef7
--- /dev/null
+++ b/qa/rpc-tests/decodescript.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python2
+# Copyright (c) 2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+class DecodeScriptTest(BitcoinTestFramework):
+ """Tests decoding scripts via RPC command "decodescript"."""
+
+ def setup_chain(self):
+ print('Initializing test directory ' + self.options.tmpdir)
+ initialize_chain_clean(self.options.tmpdir, 1)
+
+ def setup_network(self, split=False):
+ self.nodes = start_nodes(1, self.options.tmpdir)
+ self.is_network_split = False
+
+ def decodescript_script_sig(self):
+ signature = '304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001'
+ push_signature = '48' + signature
+ public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2'
+ push_public_key = '21' + public_key
+
+ # below are test cases for all of the standard transaction types
+
+ # 1) P2PK scriptSig
+ # the scriptSig of a public key scriptPubKey simply pushes a signature onto the stack
+ rpc_result = self.nodes[0].decodescript(push_signature)
+ assert_equal(signature, rpc_result['asm'])
+
+ # 2) P2PKH scriptSig
+ rpc_result = self.nodes[0].decodescript(push_signature + push_public_key)
+ assert_equal(signature + ' ' + public_key, rpc_result['asm'])
+
+ # 3) multisig scriptSig
+ # this also tests the leading portion of a P2SH multisig scriptSig
+ # OP_0 <A sig> <B sig>
+ rpc_result = self.nodes[0].decodescript('00' + push_signature + push_signature)
+ assert_equal('0 ' + signature + ' ' + signature, rpc_result['asm'])
+
+ # 4) P2SH scriptSig
+ # an empty P2SH redeemScript is valid and makes for a very simple test case.
+ # thus, such a spending scriptSig would just need to pass the outer redeemScript
+ # hash test and leave true on the top of the stack.
+ rpc_result = self.nodes[0].decodescript('5100')
+ assert_equal('1 0', rpc_result['asm'])
+
+ # 5) null data scriptSig - no such thing because null data scripts can not be spent.
+ # thus, no test case for that standard transaction type is here.
+
+ def decodescript_script_pub_key(self):
+ public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2'
+ push_public_key = '21' + public_key
+ public_key_hash = '11695b6cd891484c2d49ec5aa738ec2b2f897777'
+ push_public_key_hash = '14' + public_key_hash
+
+ # below are test cases for all of the standard transaction types
+
+ # 1) P2PK scriptPubKey
+ # <pubkey> OP_CHECKSIG
+ rpc_result = self.nodes[0].decodescript(push_public_key + 'ac')
+ assert_equal(public_key + ' OP_CHECKSIG', rpc_result['asm'])
+
+ # 2) P2PKH scriptPubKey
+ # OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
+ rpc_result = self.nodes[0].decodescript('76a9' + push_public_key_hash + '88ac')
+ assert_equal('OP_DUP OP_HASH160 ' + public_key_hash + ' OP_EQUALVERIFY OP_CHECKSIG', rpc_result['asm'])
+
+ # 3) multisig scriptPubKey
+ # <m> <A pubkey> <B pubkey> <C pubkey> <n> OP_CHECKMULTISIG
+ # just imagine that the pub keys used below are different.
+ # for our purposes here it does not matter that they are the same even though it is unrealistic.
+ rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_public_key + push_public_key + '53ae')
+ assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm'])
+
+ # 4) P2SH scriptPubKey
+ # OP_HASH160 <Hash160(redeemScript)> OP_EQUAL.
+ # push_public_key_hash here should actually be the hash of a redeem script.
+ # but this works the same for purposes of this test.
+ rpc_result = self.nodes[0].decodescript('a9' + push_public_key_hash + '87')
+ assert_equal('OP_HASH160 ' + public_key_hash + ' OP_EQUAL', rpc_result['asm'])
+
+ # 5) null data scriptPubKey
+ # use a signature look-alike here to make sure that we do not decode random data as a signature.
+ # this matters if/when signature sighash decoding comes along.
+ # would want to make sure that no such decoding takes place in this case.
+ signature_imposter = '48304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001'
+ # OP_RETURN <data>
+ rpc_result = self.nodes[0].decodescript('6a' + signature_imposter)
+ assert_equal('OP_RETURN ' + signature_imposter[2:], rpc_result['asm'])
+
+ # 6) a CLTV redeem script. redeem scripts are in-effect scriptPubKey scripts, so adding a test here.
+ # OP_NOP2 is also known as OP_CHECKLOCKTIMEVERIFY.
+ # just imagine that the pub keys used below are different.
+ # for our purposes here it does not matter that they are the same even though it is unrealistic.
+ #
+ # OP_IF
+ # <receiver-pubkey> OP_CHECKSIGVERIFY
+ # OP_ELSE
+ # <lock-until> OP_NOP2 OP_DROP
+ # OP_ENDIF
+ # <sender-pubkey> OP_CHECKSIG
+ #
+ # lock until block 500,000
+ rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac')
+ assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_NOP2 OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
+
+ def run_test(self):
+ self.decodescript_script_sig()
+ self.decodescript_script_pub_key()
+
+if __name__ == '__main__':
+ DecodeScriptTest().main()
+
diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py
index 9a77bd97e8..d89cfcf59b 100755
--- a/qa/rpc-tests/nodehandling.py
+++ b/qa/rpc-tests/nodehandling.py
@@ -48,7 +48,25 @@ class NodeHandlingTest (BitcoinTestFramework):
assert_equal(len(self.nodes[2].listbanned()), 0)
self.nodes[2].clearbanned()
assert_equal(len(self.nodes[2].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/255.255.255.255", listBeforeShutdown[2]['address']) #must be here
+ time.sleep(2) #make 100% sure we expired 192.168.0.1 node time
+
+ #stop node
+ stop_node(self.nodes[2], 2)
+
+ self.nodes[2] = start_node(2, self.options.tmpdir)
+ listAfterShutdown = self.nodes[2].listbanned();
+ assert_equal("127.0.0.0/255.255.255.0", listAfterShutdown[0]['address'])
+ assert_equal("127.0.0.0/255.255.255.255", listAfterShutdown[1]['address'])
+ assert_equal("2001:4000::/ffff:e000:0:0:0:0:0:0", listAfterShutdown[2]['address'])
+
###########################
# RPC disconnectnode test #
###########################
diff --git a/qa/rpc-tests/txn_clone.py b/qa/rpc-tests/txn_clone.py
new file mode 100755
index 0000000000..0d276ecc91
--- /dev/null
+++ b/qa/rpc-tests/txn_clone.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python2
+# Copyright (c) 2014 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#
+# Test proper accounting with an equivalent malleability clone
+#
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.authproxy import AuthServiceProxy, JSONRPCException
+from decimal import Decimal
+from test_framework.util import *
+import os
+import shutil
+
+class TxnMallTest(BitcoinTestFramework):
+
+ def add_options(self, parser):
+ parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true",
+ help="Test double-spend of 1-confirmed transaction")
+
+ def setup_network(self):
+ # Start with split network:
+ return super(TxnMallTest, self).setup_network(True)
+
+ def run_test(self):
+ # All nodes should start with 1,250 BTC:
+ starting_balance = 1250
+ for i in range(4):
+ assert_equal(self.nodes[i].getbalance(), starting_balance)
+ self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
+
+ # Assign coins to foo and bar accounts:
+ self.nodes[0].settxfee(.001)
+
+ node0_address_foo = self.nodes[0].getnewaddress("foo")
+ fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219)
+ fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
+
+ node0_address_bar = self.nodes[0].getnewaddress("bar")
+ fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29)
+ fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid)
+
+ assert_equal(self.nodes[0].getbalance(""),
+ starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"])
+
+ # Coins are sent to node1_address
+ node1_address = self.nodes[1].getnewaddress("from0")
+
+ # Send tx1, and another transaction tx2 that won't be cloned
+ txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0)
+ txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0)
+
+ # Construct a clone of tx1, to be malleated
+ rawtx1 = self.nodes[0].getrawtransaction(txid1,1)
+ clone_inputs = [{"txid":rawtx1["vin"][0]["txid"],"vout":rawtx1["vin"][0]["vout"]}]
+ clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][0]["value"],
+ rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][1]["value"]}
+ clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs)
+
+ # 3 hex manipulations on the clone are required
+
+ # manipulation 1. sequence is at version+#inputs+input+sigstub
+ posseq = 2*(4+1+36+1)
+ seqbe = '%08x' % rawtx1["vin"][0]["sequence"]
+ clone_raw = clone_raw[:posseq] + seqbe[6:8] + seqbe[4:6] + seqbe[2:4] + seqbe[0:2] + clone_raw[posseq + 8:]
+
+ # manipulation 2. createrawtransaction randomizes the order of its outputs, so swap them if necessary.
+ # output 0 is at version+#inputs+input+sigstub+sequence+#outputs
+ # 40 BTC serialized is 00286bee00000000
+ pos0 = 2*(4+1+36+1+4+1)
+ hex40 = "00286bee00000000"
+ output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16 : pos0 + 16 + 2], 0)
+ if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0 : pos0 + 16] != hex40 or
+ rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0 : pos0 + 16] == hex40):
+ output0 = clone_raw[pos0 : pos0 + output_len]
+ output1 = clone_raw[pos0 + output_len : pos0 + 2 * output_len]
+ clone_raw = clone_raw[:pos0] + output1 + output0 + clone_raw[pos0 + 2 * output_len:]
+
+ # manipulation 3. locktime is after outputs
+ poslt = pos0 + 2 * output_len
+ ltbe = '%08x' % rawtx1["locktime"]
+ clone_raw = clone_raw[:poslt] + ltbe[6:8] + ltbe[4:6] + ltbe[2:4] + ltbe[0:2] + clone_raw[poslt + 8:]
+
+ # Use a different signature hash type to sign. This creates an equivalent but malleated clone.
+ # Don't send the clone anywhere yet
+ tx1_clone = self.nodes[0].signrawtransaction(clone_raw, None, None, "ALL|ANYONECANPAY")
+ assert_equal(tx1_clone["complete"], True)
+
+ # Have node0 mine a block, if requested:
+ if (self.options.mine_block):
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes[0:2])
+
+ tx1 = self.nodes[0].gettransaction(txid1)
+ tx2 = self.nodes[0].gettransaction(txid2)
+
+ # Node0's balance should be starting balance, plus 50BTC for another
+ # matured block, minus tx1 and tx2 amounts, and minus transaction fees:
+ expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"]
+ if self.options.mine_block: expected += 50
+ expected += tx1["amount"] + tx1["fee"]
+ expected += tx2["amount"] + tx2["fee"]
+ assert_equal(self.nodes[0].getbalance(), expected)
+
+ # foo and bar accounts should be debited:
+ assert_equal(self.nodes[0].getbalance("foo", 0), 1219 + tx1["amount"] + tx1["fee"])
+ assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"])
+
+ if self.options.mine_block:
+ assert_equal(tx1["confirmations"], 1)
+ assert_equal(tx2["confirmations"], 1)
+ # Node1's "from0" balance should be both transaction amounts:
+ assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"] + tx2["amount"]))
+ else:
+ assert_equal(tx1["confirmations"], 0)
+ assert_equal(tx2["confirmations"], 0)
+
+ # Send clone and its parent to miner
+ self.nodes[2].sendrawtransaction(fund_foo_tx["hex"])
+ txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"])
+ # ... mine a block...
+ self.nodes[2].generate(1)
+
+ # Reconnect the split network, and sync chain:
+ connect_nodes(self.nodes[1], 2)
+ self.nodes[2].generate(1) # Mine another block to make sure we sync
+ sync_blocks(self.nodes)
+
+ # Re-fetch transaction info:
+ tx1 = self.nodes[0].gettransaction(txid1)
+ tx1_clone = self.nodes[0].gettransaction(txid1_clone)
+ tx2 = self.nodes[0].gettransaction(txid2)
+
+ # Verify expected confirmations
+ assert_equal(tx1["confirmations"], -1)
+ assert_equal(tx1_clone["confirmations"], 2)
+ assert_equal(tx2["confirmations"], 0)
+
+ # Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured,
+ # less possible orphaned matured subsidy
+ expected += 100
+ if (self.options.mine_block):
+ expected -= 50
+ assert_equal(self.nodes[0].getbalance(), expected)
+ assert_equal(self.nodes[0].getbalance("*", 0), expected)
+
+ # Check node0's individual account balances.
+ # "foo" should have been debited by the equivalent clone of tx1
+ assert_equal(self.nodes[0].getbalance("foo"), 1219 + tx1["amount"] + tx1["fee"])
+ # "bar" should have been debited by (possibly unconfirmed) tx2
+ assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"])
+ # "" should have starting balance, less funding txes, plus subsidies
+ assert_equal(self.nodes[0].getbalance("", 0), starting_balance
+ - 1219
+ + fund_foo_tx["fee"]
+ - 29
+ + fund_bar_tx["fee"]
+ + 100)
+
+ # Node1's "from0" account balance
+ assert_equal(self.nodes[1].getbalance("from0", 0), -(tx1["amount"] + tx2["amount"]))
+
+if __name__ == '__main__':
+ TxnMallTest().main()
+
diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py
index 99dcdae552..36081127b4 100755
--- a/qa/rpc-tests/txn_doublespend.py
+++ b/qa/rpc-tests/txn_doublespend.py
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
-# Test proper accounting with malleable transactions
+# Test proper accounting with a double-spend conflict
#
from test_framework.test_framework import BitcoinTestFramework
@@ -31,28 +31,40 @@ class TxnMallTest(BitcoinTestFramework):
self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
# Assign coins to foo and bar accounts:
- self.nodes[0].move("", "foo", 1220)
- self.nodes[0].move("", "bar", 30)
- assert_equal(self.nodes[0].getbalance(""), 0)
+ node0_address_foo = self.nodes[0].getnewaddress("foo")
+ fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219)
+ fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
+
+ node0_address_bar = self.nodes[0].getnewaddress("bar")
+ fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29)
+ fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid)
+
+ assert_equal(self.nodes[0].getbalance(""),
+ starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"])
# Coins are sent to node1_address
node1_address = self.nodes[1].getnewaddress("from0")
- # First: use raw transaction API to send 1210 BTC to node1_address,
+ # First: use raw transaction API to send 1240 BTC to node1_address,
# but don't broadcast:
- (total_in, inputs) = gather_inputs(self.nodes[0], 1210)
- change_address = self.nodes[0].getnewaddress("foo")
+ doublespend_fee = Decimal('-.02')
+ rawtx_input_0 = {}
+ rawtx_input_0["txid"] = fund_foo_txid
+ rawtx_input_0["vout"] = find_output(self.nodes[0], fund_foo_txid, 1219)
+ rawtx_input_1 = {}
+ rawtx_input_1["txid"] = fund_bar_txid
+ rawtx_input_1["vout"] = find_output(self.nodes[0], fund_bar_txid, 29)
+ inputs = [rawtx_input_0, rawtx_input_1]
+ change_address = self.nodes[0].getnewaddress()
outputs = {}
- outputs[change_address] = 40
- outputs[node1_address] = 1210
+ outputs[node1_address] = 1240
+ outputs[change_address] = 1248 - 1240 + doublespend_fee
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
doublespend = self.nodes[0].signrawtransaction(rawtx)
assert_equal(doublespend["complete"], True)
- # Create two transaction from node[0] to node[1]; the
- # second must spend change from the first because the first
- # spends all mature inputs:
- txid1 = self.nodes[0].sendfrom("foo", node1_address, 1210, 0)
+ # Create two spends using 1 50 BTC coin each
+ txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0)
txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0)
# Have node0 mine a block:
@@ -64,16 +76,16 @@ class TxnMallTest(BitcoinTestFramework):
tx2 = self.nodes[0].gettransaction(txid2)
# Node0's balance should be starting balance, plus 50BTC for another
- # matured block, minus 1210, minus 20, and minus transaction fees:
- expected = starting_balance
+ # matured block, minus 40, minus 20, and minus transaction fees:
+ expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"]
if self.options.mine_block: expected += 50
expected += tx1["amount"] + tx1["fee"]
expected += tx2["amount"] + tx2["fee"]
assert_equal(self.nodes[0].getbalance(), expected)
# foo and bar accounts should be debited:
- assert_equal(self.nodes[0].getbalance("foo"), 1220+tx1["amount"]+tx1["fee"])
- assert_equal(self.nodes[0].getbalance("bar"), 30+tx2["amount"]+tx2["fee"])
+ assert_equal(self.nodes[0].getbalance("foo", 0), 1219+tx1["amount"]+tx1["fee"])
+ assert_equal(self.nodes[0].getbalance("bar", 0), 29+tx2["amount"]+tx2["fee"])
if self.options.mine_block:
assert_equal(tx1["confirmations"], 1)
@@ -84,8 +96,10 @@ class TxnMallTest(BitcoinTestFramework):
assert_equal(tx1["confirmations"], 0)
assert_equal(tx2["confirmations"], 0)
- # Now give doublespend to miner:
- mutated_txid = self.nodes[2].sendrawtransaction(doublespend["hex"])
+ # Now give doublespend and its parents to miner:
+ self.nodes[2].sendrawtransaction(fund_foo_tx["hex"])
+ self.nodes[2].sendrawtransaction(fund_bar_tx["hex"])
+ self.nodes[2].sendrawtransaction(doublespend["hex"])
# ... mine a block...
self.nodes[2].generate(1)
@@ -103,17 +117,28 @@ class TxnMallTest(BitcoinTestFramework):
assert_equal(tx2["confirmations"], -1)
# Node0's total balance should be starting balance, plus 100BTC for
- # two more matured blocks, minus 1210 for the double-spend:
- expected = starting_balance + 100 - 1210
+ # two more matured blocks, minus 1240 for the double-spend, plus fees (which are
+ # negative):
+ expected = starting_balance + 100 - 1240 + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee
assert_equal(self.nodes[0].getbalance(), expected)
assert_equal(self.nodes[0].getbalance("*"), expected)
- # foo account should be debited, but bar account should not:
- assert_equal(self.nodes[0].getbalance("foo"), 1220-1210)
- assert_equal(self.nodes[0].getbalance("bar"), 30)
-
- # Node1's "from" account balance should be just the mutated send:
- assert_equal(self.nodes[1].getbalance("from0"), 1210)
+ # Final "" balance is starting_balance - amount moved to accounts - doublespend + subsidies +
+ # fees (which are negative)
+ assert_equal(self.nodes[0].getbalance("foo"), 1219)
+ assert_equal(self.nodes[0].getbalance("bar"), 29)
+ assert_equal(self.nodes[0].getbalance(""), starting_balance
+ -1219
+ - 29
+ -1240
+ + 100
+ + fund_foo_tx["fee"]
+ + fund_bar_tx["fee"]
+ + doublespend_fee)
+
+ # Node1's "from0" account balance should be just the doublespend:
+ assert_equal(self.nodes[1].getbalance("from0"), 1240)
if __name__ == '__main__':
TxnMallTest().main()
+
diff --git a/src/init.cpp b/src/init.cpp
index 9478b8b55f..abaf9b81d2 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -157,8 +157,8 @@ void Shutdown()
#ifdef ENABLE_WALLET
if (pwalletMain)
pwalletMain->Flush(false);
- GenerateBitcoins(false, NULL, 0);
#endif
+ GenerateBitcoins(false, 0, Params());
StopNode();
UnregisterNodeSignals(GetNodeSignals());
@@ -284,7 +284,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
- -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
+ -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
#ifndef WIN32
strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), "bitcoind.pid"));
#endif
@@ -371,10 +371,8 @@ std::string HelpMessage(HelpMessageMode mode)
debugCategories += ", qt";
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + _("<category> can be:") + " " + debugCategories + ".");
-#ifdef ENABLE_WALLET
strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), 0));
strUsage += HelpMessageOpt("-genproclimit=<n>", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), 1));
-#endif
strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)"));
strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), 0));
strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), 1));
@@ -397,6 +395,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
strUsage += HelpMessageGroup(_("Node relay options:"));
+ if (showDebug)
+ strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard()));
strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), 1));
strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY));
@@ -775,7 +775,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (nScriptCheckThreads <= 0)
- nScriptCheckThreads += boost::thread::hardware_concurrency();
+ nScriptCheckThreads += GetNumCores();
if (nScriptCheckThreads <= 1)
nScriptCheckThreads = 0;
else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
@@ -820,6 +820,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"]));
}
+ fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !Params().RequireStandard());
+ if (Params().RequireStandard() && !fRequireStandard)
+ return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
+
#ifdef ENABLE_WALLET
if (mapArgs.count("-mintxfee"))
{
@@ -869,6 +873,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS);
+ // Option to startup with mocktime set (used for regression testing):
+ SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
+
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
// Initialize elliptic curve code
@@ -1437,11 +1444,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
boost::ref(cs_main), boost::cref(pindexBestHeader), nPowTargetSpacing);
scheduler.scheduleEvery(f, nPowTargetSpacing);
-#ifdef ENABLE_WALLET
// Generate coins in the background
- if (pwalletMain)
- GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1));
-#endif
+ GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1), Params());
// ********************************************************* Step 11: finished
diff --git a/src/main.cpp b/src/main.cpp
index f966f68698..03c09f0a27 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -59,6 +59,7 @@ bool fTxIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
bool fIsBareMultisigStd = true;
+bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = true;
size_t nCoinCacheUsage = 5000 * 300;
@@ -764,7 +765,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
- if (Params().RequireStandard() && !IsStandardTx(tx, reason))
+ if (fRequireStandard && !IsStandardTx(tx, reason))
return state.DoS(0,
error("AcceptToMemoryPool: nonstandard transaction: %s", reason),
REJECT_NONSTANDARD, reason);
@@ -835,7 +836,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
}
// Check for non-standard pay-to-script-hash in inputs
- if (Params().RequireStandard() && !AreInputsStandard(tx, view))
+ if (fRequireStandard && !AreInputsStandard(tx, view))
return error("AcceptToMemoryPool: nonstandard transaction input");
// Check that the transaction doesn't have an excessive number of
@@ -4823,7 +4824,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
else
{
- CNode::Ban(pto->addr);
+ CNode::Ban(pto->addr, BanReasonNodeMisbehaving);
}
}
state.fShouldBan = false;
diff --git a/src/main.h b/src/main.h
index 8f72a955a6..ce18bd709f 100644
--- a/src/main.h
+++ b/src/main.h
@@ -99,6 +99,7 @@ extern bool fReindex;
extern int nScriptCheckThreads;
extern bool fTxIndex;
extern bool fIsBareMultisigStd;
+extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern size_t nCoinCacheUsage;
@@ -119,7 +120,7 @@ extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
-static const signed int MIN_BLOCKS_TO_KEEP = 288;
+static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
// Require that user allocate at least 550MB for block & undo files (blk???.dat and rev???.dat)
// At 1MB per block, 288 blocks = 288MB.
@@ -129,7 +130,7 @@ static const signed int MIN_BLOCKS_TO_KEEP = 288;
// full block file chunks, we need the high water mark which triggers the prune to be
// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB
// Setting the target to > than 550MB will make it likely we can respect the target.
-static const signed int MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
+static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
/** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals);
diff --git a/src/miner.cpp b/src/miner.cpp
index fb859ca2b9..bdb85c8c79 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -18,9 +18,7 @@
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
-#ifdef ENABLE_WALLET
-#include "wallet/wallet.h"
-#endif
+#include "validationinterface.h"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
@@ -363,7 +361,6 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}
-#ifdef ENABLE_WALLET
//////////////////////////////////////////////////////////////////////////////
//
// Internal miner
@@ -402,17 +399,7 @@ bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phas
}
}
-CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
-{
- CPubKey pubkey;
- if (!reservekey.GetReservedKey(pubkey))
- return NULL;
-
- CScript scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
- return CreateNewBlock(scriptPubKey);
-}
-
-static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
+static bool ProcessBlockFound(CBlock* pblock, const CChainParams& chainparams)
{
LogPrintf("%s\n", pblock->ToString());
LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
@@ -424,14 +411,8 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
return error("BitcoinMiner: generated block is stale");
}
- // Remove key from key pool
- reservekey.KeepKey();
-
- // Track how many getdata requests this block gets
- {
- LOCK(wallet.cs_wallet);
- wallet.mapRequestCount[pblock->GetHash()] = 0;
- }
+ // Inform about the new block
+ GetMainSignals().BlockFound(pblock->GetHash());
// Process this block the same as if we had received it from another node
CValidationState state;
@@ -441,18 +422,22 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
return true;
}
-void static BitcoinMiner(CWallet *pwallet)
+void static BitcoinMiner(const CChainParams& chainparams)
{
LogPrintf("BitcoinMiner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("bitcoin-miner");
- const CChainParams& chainparams = Params();
- // Each thread has its own key and counter
- CReserveKey reservekey(pwallet);
unsigned int nExtraNonce = 0;
+ boost::shared_ptr<CReserveScript> coinbaseScript;
+ GetMainSignals().ScriptForMining(coinbaseScript);
+
try {
+ //throw an error if no script was provided
+ if (!coinbaseScript->reserveScript.size())
+ throw std::runtime_error("No coinbase script available (mining requires a wallet)");
+
while (true) {
if (chainparams.MiningRequiresPeers()) {
// Busy-wait for the network to come online so we don't waste time mining
@@ -475,7 +460,7 @@ void static BitcoinMiner(CWallet *pwallet)
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrev = chainActive.Tip();
- auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
+ auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(coinbaseScript->reserveScript));
if (!pblocktemplate.get())
{
LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
@@ -507,8 +492,9 @@ void static BitcoinMiner(CWallet *pwallet)
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("BitcoinMiner:\n");
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex(), hashTarget.GetHex());
- ProcessBlockFound(pblock, *pwallet, reservekey);
+ ProcessBlockFound(pblock, chainparams);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ coinbaseScript->KeepScript();
// In regression test mode, stop mining after a block is found.
if (chainparams.MineBlocksOnDemand())
@@ -552,7 +538,7 @@ void static BitcoinMiner(CWallet *pwallet)
}
}
-void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
+void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams)
{
static boost::thread_group* minerThreads = NULL;
@@ -561,7 +547,7 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
if (Params().DefaultMinerThreads())
nThreads = Params().DefaultMinerThreads();
else
- nThreads = boost::thread::hardware_concurrency();
+ nThreads = GetNumCores();
}
if (minerThreads != NULL)
@@ -576,7 +562,5 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
minerThreads = new boost::thread_group();
for (int i = 0; i < nThreads; i++)
- minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
+ minerThreads->create_thread(boost::bind(&BitcoinMiner, boost::cref(chainparams)));
}
-
-#endif // ENABLE_WALLET
diff --git a/src/miner.h b/src/miner.h
index 96a6b70ecd..777a091967 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -11,6 +11,7 @@
#include <stdint.h>
class CBlockIndex;
+class CChainParams;
class CReserveKey;
class CScript;
class CWallet;
@@ -24,10 +25,9 @@ struct CBlockTemplate
};
/** Run the miner threads */
-void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads);
+void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams);
/** Generate a new block, without valid proof-of-work */
CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn);
-CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey);
/** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
diff --git a/src/net.cpp b/src/net.cpp
index 0511256e55..950311ee3a 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -443,13 +443,15 @@ void CNode::PushVersion()
-std::map<CSubNet, int64_t> CNode::setBanned;
+banmap_t CNode::setBanned;
CCriticalSection CNode::cs_setBanned;
+bool CNode::setBannedIsDirty;
void CNode::ClearBanned()
{
LOCK(cs_setBanned);
setBanned.clear();
+ setBannedIsDirty = true;
}
bool CNode::IsBanned(CNetAddr ip)
@@ -457,12 +459,12 @@ bool CNode::IsBanned(CNetAddr ip)
bool fResult = false;
{
LOCK(cs_setBanned);
- for (std::map<CSubNet, int64_t>::iterator it = setBanned.begin(); it != setBanned.end(); it++)
+ for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++)
{
CSubNet subNet = (*it).first;
- int64_t t = (*it).second;
+ CBanEntry banEntry = (*it).second;
- if(subNet.Match(ip) && GetTime() < t)
+ if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil)
fResult = true;
}
}
@@ -474,50 +476,99 @@ bool CNode::IsBanned(CSubNet subnet)
bool fResult = false;
{
LOCK(cs_setBanned);
- std::map<CSubNet, int64_t>::iterator i = setBanned.find(subnet);
+ banmap_t::iterator i = setBanned.find(subnet);
if (i != setBanned.end())
{
- int64_t t = (*i).second;
- if (GetTime() < t)
+ CBanEntry banEntry = (*i).second;
+ if (GetTime() < banEntry.nBanUntil)
fResult = true;
}
}
return fResult;
}
-void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) {
- CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
- Ban(subNet, bantimeoffset, sinceUnixEpoch);
+void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
+ CSubNet subNet(addr);
+ Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch);
}
-void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) {
- int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban
- if (bantimeoffset > 0)
- banTime = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
+void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
+ CBanEntry banEntry(GetTime());
+ banEntry.banReason = banReason;
+ if (bantimeoffset <= 0)
+ {
+ bantimeoffset = GetArg("-bantime", 60*60*24); // Default 24-hour ban
+ sinceUnixEpoch = false;
+ }
+ banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
+
LOCK(cs_setBanned);
- if (setBanned[subNet] < banTime)
- setBanned[subNet] = banTime;
+ if (setBanned[subNet].nBanUntil < banEntry.nBanUntil)
+ setBanned[subNet] = banEntry;
+
+ setBannedIsDirty = true;
}
bool CNode::Unban(const CNetAddr &addr) {
- CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
+ CSubNet subNet(addr);
return Unban(subNet);
}
bool CNode::Unban(const CSubNet &subNet) {
LOCK(cs_setBanned);
if (setBanned.erase(subNet))
+ {
+ setBannedIsDirty = true;
return true;
+ }
return false;
}
-void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap)
+void CNode::GetBanned(banmap_t &banMap)
{
LOCK(cs_setBanned);
banMap = setBanned; //create a thread safe copy
}
+void CNode::SetBanned(const banmap_t &banMap)
+{
+ LOCK(cs_setBanned);
+ setBanned = banMap;
+ setBannedIsDirty = true;
+}
+
+void CNode::SweepBanned()
+{
+ int64_t now = GetTime();
+
+ LOCK(cs_setBanned);
+ banmap_t::iterator it = setBanned.begin();
+ while(it != setBanned.end())
+ {
+ CBanEntry banEntry = (*it).second;
+ if(now > banEntry.nBanUntil)
+ {
+ setBanned.erase(it++);
+ setBannedIsDirty = true;
+ }
+ else
+ ++it;
+ }
+}
+
+bool CNode::BannedSetIsDirty()
+{
+ LOCK(cs_setBanned);
+ return setBannedIsDirty;
+}
+
+void CNode::SetBannedSetDirty(bool dirty)
+{
+ LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag
+ setBannedIsDirty = dirty;
+}
+
std::vector<CSubNet> CNode::vWhitelistedRange;
CCriticalSection CNode::cs_vWhitelistedRange;
@@ -1212,6 +1263,17 @@ void DumpAddresses()
addrman.size(), GetTimeMillis() - nStart);
}
+void DumpData()
+{
+ DumpAddresses();
+
+ if (CNode::BannedSetIsDirty())
+ {
+ DumpBanlist();
+ CNode::SetBannedSetDirty(false);
+ }
+}
+
void static ProcessOneShot()
{
string strDest;
@@ -1650,6 +1712,17 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!adb.Read(addrman))
LogPrintf("Invalid or missing peers.dat; recreating\n");
}
+
+ //try to read stored banlist
+ CBanDB bandb;
+ banmap_t banmap;
+ if (!bandb.Read(banmap))
+ LogPrintf("Invalid or missing banlist.dat; recreating\n");
+
+ CNode::SetBanned(banmap); //thread save setter
+ CNode::SetBannedSetDirty(false); //no need to write down just read or nonexistent data
+ CNode::SweepBanned(); //sweap out unused entries
+
LogPrintf("Loaded %i addresses from peers.dat %dms\n",
addrman.size(), GetTimeMillis() - nStart);
fAddressesInitialized = true;
@@ -1690,7 +1763,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
// Dump network addresses
- scheduler.scheduleEvery(&DumpAddresses, DUMP_ADDRESSES_INTERVAL);
+ scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL);
}
bool StopNode()
@@ -1703,7 +1776,7 @@ bool StopNode()
if (fAddressesInitialized)
{
- DumpAddresses();
+ DumpData();
fAddressesInitialized = false;
}
@@ -1907,11 +1980,11 @@ bool CAddrDB::Read(CAddrMan& addr)
return error("%s: Failed to open file %s", __func__, pathAddr.string());
// use file size to size memory buffer
- int fileSize = boost::filesystem::file_size(pathAddr);
- int dataSize = fileSize - sizeof(uint256);
+ uint64_t fileSize = boost::filesystem::file_size(pathAddr);
+ uint64_t dataSize = 0;
// Don't try to resize to a negative number if file is small
- if (dataSize < 0)
- dataSize = 0;
+ if (fileSize >= sizeof(uint256))
+ dataSize = fileSize - sizeof(uint256);
vector<unsigned char> vchData;
vchData.resize(dataSize);
uint256 hashIn;
@@ -2107,3 +2180,119 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
LEAVE_CRITICAL_SECTION(cs_vSend);
}
+
+//
+// CBanDB
+//
+
+CBanDB::CBanDB()
+{
+ pathBanlist = GetDataDir() / "banlist.dat";
+}
+
+bool CBanDB::Write(const banmap_t& banSet)
+{
+ // Generate random temporary filename
+ unsigned short randv = 0;
+ GetRandBytes((unsigned char*)&randv, sizeof(randv));
+ std::string tmpfn = strprintf("banlist.dat.%04x", randv);
+
+ // serialize banlist, checksum data up to that point, then append csum
+ CDataStream ssBanlist(SER_DISK, CLIENT_VERSION);
+ ssBanlist << FLATDATA(Params().MessageStart());
+ ssBanlist << banSet;
+ uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end());
+ ssBanlist << hash;
+
+ // open temp output file, and associate with CAutoFile
+ boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
+ FILE *file = fopen(pathTmp.string().c_str(), "wb");
+ CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
+ if (fileout.IsNull())
+ return error("%s: Failed to open file %s", __func__, pathTmp.string());
+
+ // Write and commit header, data
+ try {
+ fileout << ssBanlist;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Serialize or I/O error - %s", __func__, e.what());
+ }
+ FileCommit(fileout.Get());
+ fileout.fclose();
+
+ // replace existing banlist.dat, if any, with new banlist.dat.XXXX
+ if (!RenameOver(pathTmp, pathBanlist))
+ return error("%s: Rename-into-place failed", __func__);
+
+ return true;
+}
+
+bool CBanDB::Read(banmap_t& banSet)
+{
+ // open input file, and associate with CAutoFile
+ FILE *file = fopen(pathBanlist.string().c_str(), "rb");
+ CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull())
+ return error("%s: Failed to open file %s", __func__, pathBanlist.string());
+
+ // use file size to size memory buffer
+ uint64_t fileSize = boost::filesystem::file_size(pathBanlist);
+ uint64_t dataSize = 0;
+ // Don't try to resize to a negative number if file is small
+ if (fileSize >= sizeof(uint256))
+ dataSize = fileSize - sizeof(uint256);
+ vector<unsigned char> vchData;
+ vchData.resize(dataSize);
+ uint256 hashIn;
+
+ // read data and checksum from file
+ try {
+ filein.read((char *)&vchData[0], dataSize);
+ filein >> hashIn;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+ filein.fclose();
+
+ CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION);
+
+ // verify stored checksum matches input data
+ uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end());
+ if (hashIn != hashTmp)
+ return error("%s: Checksum mismatch, data corrupted", __func__);
+
+ unsigned char pchMsgTmp[4];
+ try {
+ // de-serialize file header (network specific magic number) and ..
+ ssBanlist >> FLATDATA(pchMsgTmp);
+
+ // ... verify the network matches ours
+ if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
+ return error("%s: Invalid network magic number", __func__);
+
+ // de-serialize address data into one CAddrMan object
+ ssBanlist >> banSet;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+
+ return true;
+}
+
+void DumpBanlist()
+{
+ int64_t nStart = GetTimeMillis();
+
+ CNode::SweepBanned(); //clean unused entires (if bantime has expired)
+
+ CBanDB bandb;
+ banmap_t banmap;
+ CNode::GetBanned(banmap);
+ bandb.Write(banmap);
+
+ LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
+ banmap.size(), GetTimeMillis() - nStart);
+} \ No newline at end of file
diff --git a/src/net.h b/src/net.h
index 69e4c592a9..f15b85474f 100644
--- a/src/net.h
+++ b/src/net.h
@@ -228,8 +228,66 @@ public:
};
+typedef enum BanReason
+{
+ BanReasonUnknown = 0,
+ BanReasonNodeMisbehaving = 1,
+ BanReasonManuallyAdded = 2
+} BanReason;
+
+class CBanEntry
+{
+public:
+ static const int CURRENT_VERSION=1;
+ int nVersion;
+ int64_t nCreateTime;
+ int64_t nBanUntil;
+ uint8_t banReason;
+
+ CBanEntry()
+ {
+ SetNull();
+ }
+
+ CBanEntry(int64_t nCreateTimeIn)
+ {
+ SetNull();
+ nCreateTime = nCreateTimeIn;
+ }
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ READWRITE(this->nVersion);
+ nVersion = this->nVersion;
+ READWRITE(nCreateTime);
+ READWRITE(nBanUntil);
+ READWRITE(banReason);
+ }
+ void SetNull()
+ {
+ nVersion = CBanEntry::CURRENT_VERSION;
+ nCreateTime = 0;
+ nBanUntil = 0;
+ banReason = BanReasonUnknown;
+ }
+
+ std::string banReasonToString()
+ {
+ switch (banReason) {
+ case BanReasonNodeMisbehaving:
+ return "node misbehabing";
+ case BanReasonManuallyAdded:
+ return "manually added";
+ default:
+ return "unknown";
+ }
+ }
+};
+typedef std::map<CSubNet, CBanEntry> banmap_t;
/** Information about a peer */
class CNode
@@ -285,8 +343,9 @@ protected:
// Denial-of-service detection/prevention
// Key is IP address, value is banned-until-time
- static std::map<CSubNet, int64_t> setBanned;
+ static banmap_t setBanned;
static CCriticalSection cs_setBanned;
+ static bool setBannedIsDirty;
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
@@ -608,11 +667,19 @@ public:
static void ClearBanned(); // needed for unit testing
static bool IsBanned(CNetAddr ip);
static bool IsBanned(CSubNet subnet);
- static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
- static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
+ static void Ban(const CNetAddr &ip, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
+ static void Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static bool Unban(const CNetAddr &ip);
static bool Unban(const CSubNet &ip);
- static void GetBanned(std::map<CSubNet, int64_t> &banmap);
+ static void GetBanned(banmap_t &banmap);
+ static void SetBanned(const banmap_t &banmap);
+
+ //!check is the banlist has unwritten changes
+ static bool BannedSetIsDirty();
+ //!set the "dirty" flag for the banlist
+ static void SetBannedSetDirty(bool dirty=true);
+ //!clean unused entires (if bantime has expired)
+ static void SweepBanned();
void copyStats(CNodeStats &stats);
@@ -644,4 +711,17 @@ public:
bool Read(CAddrMan& addr);
};
+/** Access to the banlist database (banlist.dat) */
+class CBanDB
+{
+private:
+ boost::filesystem::path pathBanlist;
+public:
+ CBanDB();
+ bool Write(const banmap_t& banSet);
+ bool Read(banmap_t& banSet);
+};
+
+void DumpBanlist();
+
#endif // BITCOIN_NET_H
diff --git a/src/netbase.cpp b/src/netbase.cpp
index adac5c2d07..c9fc7d67f7 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -1291,6 +1291,13 @@ CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup)
network.ip[x] &= netmask[x];
}
+CSubNet::CSubNet(const CNetAddr &addr):
+ valid(addr.IsValid())
+{
+ memset(netmask, 255, sizeof(netmask));
+ network = addr;
+}
+
bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid())
diff --git a/src/netbase.h b/src/netbase.h
index 27f0eac2a2..6f8882b852 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -118,6 +118,9 @@ class CSubNet
CSubNet();
explicit CSubNet(const std::string &strSubnet, bool fAllowLookup = false);
+ //constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
+ explicit CSubNet(const CNetAddr &addr);
+
bool Match(const CNetAddr &addr) const;
std::string ToString() const;
@@ -126,6 +129,15 @@ class CSubNet
friend bool operator==(const CSubNet& a, const CSubNet& b);
friend bool operator!=(const CSubNet& a, const CSubNet& b);
friend bool operator<(const CSubNet& a, const CSubNet& b);
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ READWRITE(network);
+ READWRITE(FLATDATA(netmask));
+ READWRITE(FLATDATA(valid));
+ }
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 606dbea798..d864a9b6d3 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -87,6 +87,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 6cfd93a9a1..0c9ebb7b88 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -222,6 +222,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/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index efb2bf4158..87c727335e 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -42,7 +42,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
/* Main elements init */
ui->databaseCache->setMinimum(nMinDbCache);
ui->databaseCache->setMaximum(nMaxDbCache);
- ui->threadsScriptVerif->setMinimum(-(int)boost::thread::hardware_concurrency());
+ ui->threadsScriptVerif->setMinimum(-GetNumCores());
ui->threadsScriptVerif->setMaximum(MAX_SCRIPTCHECK_THREADS);
/* Network elements init */
diff --git a/src/rest.cpp b/src/rest.cpp
index 1fce7dfc9c..a1bd893bec 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -20,7 +20,7 @@
using namespace std;
-static const int MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
+static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
enum RetFormat {
RF_UNDEF,
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index 85229e9857..b7c3eb1724 100644
--- a/src/rpcblockchain.cpp
+++ b/src/rpcblockchain.cpp
@@ -51,6 +51,32 @@ double GetDifficulty(const CBlockIndex* blockindex)
return dDiff;
}
+UniValue blockheaderToJSON(const CBlockIndex* blockindex)
+{
+ UniValue result(UniValue::VOBJ);
+ result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
+ int confirmations = -1;
+ // Only report confirmations if the block is on the main chain
+ if (chainActive.Contains(blockindex))
+ confirmations = chainActive.Height() - blockindex->nHeight + 1;
+ result.push_back(Pair("confirmations", confirmations));
+ result.push_back(Pair("height", blockindex->nHeight));
+ result.push_back(Pair("version", blockindex->nVersion));
+ result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex()));
+ result.push_back(Pair("time", (int64_t)blockindex->nTime));
+ result.push_back(Pair("nonce", (uint64_t)blockindex->nNonce));
+ result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits)));
+ result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
+ result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
+
+ if (blockindex->pprev)
+ result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
+ CBlockIndex *pnext = chainActive.Next(blockindex);
+ if (pnext)
+ result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
+ return result;
+}
+
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
{
@@ -255,6 +281,62 @@ UniValue getblockhash(const UniValue& params, bool fHelp)
return pblockindex->GetBlockHash().GetHex();
}
+UniValue getblockheader(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "getblockheader \"hash\" ( verbose )\n"
+ "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
+ "If verbose is true, returns an Object with information about blockheader <hash>.\n"
+ "\nArguments:\n"
+ "1. \"hash\" (string, required) The block hash\n"
+ "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n"
+ "\nResult (for verbose = true):\n"
+ "{\n"
+ " \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
+ " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
+ " \"height\" : n, (numeric) The block height or index\n"
+ " \"version\" : n, (numeric) The block version\n"
+ " \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
+ " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
+ " \"nonce\" : n, (numeric) The nonce\n"
+ " \"bits\" : \"1d00ffff\", (string) The bits\n"
+ " \"difficulty\" : x.xxx, (numeric) The difficulty\n"
+ " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
+ " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n"
+ "}\n"
+ "\nResult (for verbose=false):\n"
+ "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ );
+
+ LOCK(cs_main);
+
+ std::string strHash = params[0].get_str();
+ uint256 hash(uint256S(strHash));
+
+ bool fVerbose = true;
+ if (params.size() > 1)
+ fVerbose = params[1].get_bool();
+
+ if (mapBlockIndex.count(hash) == 0)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+
+ CBlockIndex* pblockindex = mapBlockIndex[hash];
+
+ if (!fVerbose)
+ {
+ CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
+ ssBlock << pblockindex->GetBlockHeader();
+ std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
+ return strHex;
+ }
+
+ return blockheaderToJSON(pblockindex);
+}
+
UniValue getblock(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
@@ -469,6 +551,36 @@ UniValue verifychain(const UniValue& params, bool fHelp)
return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth);
}
+/** Implementation of IsSuperMajority with better feedback */
+static UniValue SoftForkMajorityDesc(int minVersion, CBlockIndex* pindex, int nRequired, const Consensus::Params& consensusParams)
+{
+ int nFound = 0;
+ CBlockIndex* pstart = pindex;
+ for (int i = 0; i < consensusParams.nMajorityWindow && pstart != NULL; i++)
+ {
+ if (pstart->nVersion >= minVersion)
+ ++nFound;
+ pstart = pstart->pprev;
+ }
+
+ UniValue rv(UniValue::VOBJ);
+ rv.push_back(Pair("status", nFound >= nRequired));
+ rv.push_back(Pair("found", nFound));
+ rv.push_back(Pair("required", nRequired));
+ rv.push_back(Pair("window", consensusParams.nMajorityWindow));
+ return rv;
+}
+
+static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
+{
+ UniValue rv(UniValue::VOBJ);
+ rv.push_back(Pair("id", name));
+ rv.push_back(Pair("version", version));
+ rv.push_back(Pair("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)));
+ rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams)));
+ return rv;
+}
+
UniValue getblockchaininfo(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -484,6 +596,19 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n"
" \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
+ " \"softforks\": [ (array) status of softforks in progress\n"
+ " {\n"
+ " \"id\": \"xxxx\", (string) name of softfork\n"
+ " \"version\": xx, (numeric) block version\n"
+ " \"enforce\": { (object) progress toward enforcing the softfork rules for new-version blocks\n"
+ " \"status\": xx, (boolean) true if threshold reached\n"
+ " \"found\": xx, (numeric) number of blocks with the new version found\n"
+ " \"required\": xx, (numeric) number of blocks required to trigger\n"
+ " \"window\": xx, (numeric) maximum size of examined window of recent blocks\n"
+ " },\n"
+ " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n"
+ " }, ...\n"
+ " ]\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getblockchaininfo", "")
@@ -501,6 +626,14 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip())));
obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
obj.push_back(Pair("pruned", fPruneMode));
+
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ CBlockIndex* tip = chainActive.Tip();
+ UniValue softforks(UniValue::VARR);
+ softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
+ softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
+ obj.push_back(Pair("softforks", softforks));
+
if (fPruneMode)
{
CBlockIndex *block = chainActive.Tip();
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index 4c6b47e4a0..b41e960e8a 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -71,6 +71,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listunspent", 1 },
{ "listunspent", 2 },
{ "getblock", 1 },
+ { "getblockheader", 1 },
{ "gettransaction", 1 },
{ "getrawtransaction", 1 },
{ "createrawtransaction", 0 },
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index f332814611..9c6fb10af0 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -16,13 +16,11 @@
#include "rpcserver.h"
#include "util.h"
#include "validationinterface.h"
-#ifdef ENABLE_WALLET
-#include "wallet/wallet.h"
-#endif
#include <stdint.h>
#include <boost/assign/list_of.hpp>
+#include <boost/shared_ptr.hpp>
#include "univalue/univalue.h"
@@ -92,7 +90,6 @@ UniValue getnetworkhashps(const UniValue& params, bool fHelp)
return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1);
}
-#ifdef ENABLE_WALLET
UniValue getgenerate(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -127,8 +124,6 @@ UniValue generate(const UniValue& params, bool fHelp)
+ HelpExampleCli("generate", "11")
);
- if (pwalletMain == NULL)
- throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)");
if (!Params().MineBlocksOnDemand())
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method can only be used on regtest");
@@ -136,7 +131,13 @@ UniValue generate(const UniValue& params, bool fHelp)
int nHeightEnd = 0;
int nHeight = 0;
int nGenerate = params[0].get_int();
- CReserveKey reservekey(pwalletMain);
+
+ boost::shared_ptr<CReserveScript> coinbaseScript;
+ GetMainSignals().ScriptForMining(coinbaseScript);
+
+ //throw an error if no script was provided
+ if (!coinbaseScript->reserveScript.size())
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)");
{ // Don't keep cs_main locked
LOCK(cs_main);
@@ -148,9 +149,9 @@ UniValue generate(const UniValue& params, bool fHelp)
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd)
{
- auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
+ auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(coinbaseScript->reserveScript));
if (!pblocktemplate.get())
- throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty");
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
@@ -166,11 +167,13 @@ UniValue generate(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
+
+ //mark script as important because it was used at least for one coinbase output
+ coinbaseScript->KeepScript();
}
return blockHashes;
}
-
UniValue setgenerate(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
@@ -193,8 +196,6 @@ UniValue setgenerate(const UniValue& params, bool fHelp)
+ HelpExampleRpc("setgenerate", "true, 1")
);
- if (pwalletMain == NULL)
- throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)");
if (Params().MineBlocksOnDemand())
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Use the generate method instead of setgenerate on this network");
@@ -212,12 +213,10 @@ UniValue setgenerate(const UniValue& params, bool fHelp)
mapArgs["-gen"] = (fGenerate ? "1" : "0");
mapArgs ["-genproclimit"] = itostr(nGenProcLimit);
- GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit);
+ GenerateBitcoins(fGenerate, nGenProcLimit, Params());
return NullUniValue;
}
-#endif
-
UniValue getmininginfo(const UniValue& params, bool fHelp)
{
@@ -257,9 +256,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
obj.push_back(Pair("chain", Params().NetworkIDString()));
-#ifdef ENABLE_WALLET
obj.push_back(Pair("generate", getgenerate(params, false)));
-#endif
return obj;
}
diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp
index 1d47bc06a5..cab57d7027 100644
--- a/src/rpcmisc.cpp
+++ b/src/rpcmisc.cpp
@@ -378,10 +378,19 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
if (!Params().MineBlocksOnDemand())
throw runtime_error("setmocktime for regression testing (-regtest mode) only");
- LOCK(cs_main);
+ // cs_vNodes is locked and node send/receive times are updated
+ // atomically with the time change to prevent peers from being
+ // disconnected because we think we haven't communicated with them
+ // in a long time.
+ LOCK2(cs_main, cs_vNodes);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
SetMockTime(params[0].get_int64());
+ uint64_t t = GetTime();
+ BOOST_FOREACH(CNode* pnode, vNodes) {
+ pnode->nLastSend = pnode->nLastRecv = t;
+ }
+
return NullUniValue;
}
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index 1572b16687..dd631905fd 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -515,7 +515,7 @@ UniValue setban(const UniValue& params, bool fHelp)
if (params.size() == 4 && params[3].isTrue())
absolute = true;
- isSubnet ? CNode::Ban(subNet, banTime, absolute) : CNode::Ban(netAddr, banTime, absolute);
+ isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
//disconnect possible nodes
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
@@ -527,6 +527,7 @@ UniValue setban(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed");
}
+ DumpBanlist(); //store banlist to disk
return NullUniValue;
}
@@ -541,15 +542,19 @@ UniValue listbanned(const UniValue& params, bool fHelp)
+ HelpExampleRpc("listbanned", "")
);
- std::map<CSubNet, int64_t> banMap;
+ banmap_t banMap;
CNode::GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR);
- for (std::map<CSubNet, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++)
+ for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
{
+ CBanEntry banEntry = (*it).second;
UniValue rec(UniValue::VOBJ);
rec.push_back(Pair("address", (*it).first.ToString()));
- rec.push_back(Pair("banned_untill", (*it).second));
+ rec.push_back(Pair("banned_until", banEntry.nBanUntil));
+ rec.push_back(Pair("ban_created", banEntry.nCreateTime));
+ rec.push_back(Pair("ban_reason", banEntry.banReasonToString()));
+
bannedAddresses.push_back(rec);
}
@@ -568,6 +573,7 @@ UniValue clearbanned(const UniValue& params, bool fHelp)
);
CNode::ClearBanned();
+ DumpBanlist(); //store banlist to disk
return NullUniValue;
}
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 2f28971589..287cfb2f13 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -289,6 +289,7 @@ static const CRPCCommand vRPCCommands[] =
{ "blockchain", "getblockcount", &getblockcount, true },
{ "blockchain", "getblock", &getblock, true },
{ "blockchain", "getblockhash", &getblockhash, true },
+ { "blockchain", "getblockheader", &getblockheader, true },
{ "blockchain", "getchaintips", &getchaintips, true },
{ "blockchain", "getdifficulty", &getdifficulty, true },
{ "blockchain", "getmempoolinfo", &getmempoolinfo, true },
@@ -306,12 +307,10 @@ static const CRPCCommand vRPCCommands[] =
{ "mining", "prioritisetransaction", &prioritisetransaction, true },
{ "mining", "submitblock", &submitblock, true },
-#ifdef ENABLE_WALLET
/* Coin generation */
{ "generating", "getgenerate", &getgenerate, true },
{ "generating", "setgenerate", &setgenerate, true },
{ "generating", "generate", &generate, true },
-#endif
/* Raw transactions */
{ "rawtransactions", "createrawtransaction", &createrawtransaction, true },
@@ -932,13 +931,6 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
if (!valRequest.read(strRequest))
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
- // Return immediately if in warmup
- {
- LOCK(cs_rpcWarmup);
- if (fRPCInWarmup)
- throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
- }
-
string strReply;
// singleton request
@@ -1010,6 +1002,13 @@ void ServiceConnection(AcceptedConnection *conn)
UniValue CRPCTable::execute(const std::string &strMethod, const UniValue &params) const
{
+ // Return immediately if in warmup
+ {
+ LOCK(cs_rpcWarmup);
+ if (fRPCInWarmup)
+ throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
+ }
+
// Find method
const CRPCCommand *pcmd = tableRPC[strMethod];
if (!pcmd)
diff --git a/src/rpcserver.h b/src/rpcserver.h
index 45af10c2a8..89d3980223 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -234,6 +234,7 @@ extern UniValue settxfee(const UniValue& params, bool fHelp);
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
extern UniValue getrawmempool(const UniValue& params, bool fHelp);
extern UniValue getblockhash(const UniValue& params, bool fHelp);
+extern UniValue getblockheader(const UniValue& params, bool fHelp);
extern UniValue getblock(const UniValue& params, bool fHelp);
extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp);
extern UniValue gettxout(const UniValue& params, bool fHelp);
diff --git a/src/script/script.h b/src/script/script.h
index c09899aabd..e39ca57f4f 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -609,4 +609,13 @@ public:
}
};
+class CReserveScript
+{
+public:
+ CScript reserveScript;
+ virtual void KeepScript() {}
+ CReserveScript() {}
+ virtual ~CReserveScript() {}
+};
+
#endif // BITCOIN_SCRIPT_SCRIPT_H
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 0f5e1615c6..7154476c7c 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -143,6 +143,17 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/128").IsValid());
BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8/129").IsValid());
BOOST_CHECK(!CSubNet("fuzzy").IsValid());
+
+ //CNetAddr constructor test
+ BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).IsValid());
+ BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.1")));
+ BOOST_CHECK(!CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.2")));
+ BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).ToString() == "127.0.0.1/255.255.255.255");
+
+ BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).IsValid());
+ BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:8")));
+ BOOST_CHECK(!CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:9")));
+ BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index c38df0ecf3..9e99ff6286 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array();
o1 = ar[0].get_obj();
adr = find_value(o1, "address");
- UniValue banned_until = find_value(o1, "banned_untill");
+ UniValue banned_until = find_value(o1, "banned_until");
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check
@@ -210,7 +210,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array();
o1 = ar[0].get_obj();
adr = find_value(o1, "address");
- banned_until = find_value(o1, "banned_untill");
+ banned_until = find_value(o1, "banned_until");
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
int64_t now = GetTime();
BOOST_CHECK(banned_until.get_int64() > now);
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 1c16e2092e..2292191be4 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -166,7 +166,7 @@ void CTxMemPool::removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned in
continue;
const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash);
if (fSanityCheck) assert(coins);
- if (!coins || (coins->IsCoinBase() && nMemPoolHeight - coins->nHeight < COINBASE_MATURITY)) {
+ if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) {
transactionsToRemove.push_back(tx);
break;
}
diff --git a/src/util.cpp b/src/util.cpp
index da5821e530..bb7df23205 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -756,3 +756,13 @@ void SetThreadPriority(int nPriority)
#endif // PRIO_THREAD
#endif // WIN32
}
+
+int GetNumCores()
+{
+#if BOOST_VERSION >= 105600
+ return boost::thread::physical_concurrency();
+#else // Must fall back to hardware_concurrency, which unfortunately counts virtual cores
+ return boost::thread::hardware_concurrency();
+#endif
+}
+
diff --git a/src/util.h b/src/util.h
index 6ec81698ea..6019e25015 100644
--- a/src/util.h
+++ b/src/util.h
@@ -199,6 +199,13 @@ std::string HelpMessageGroup(const std::string& message);
*/
std::string HelpMessageOpt(const std::string& option, const std::string& message);
+/**
+ * Return the number of physical cores available on the current system.
+ * @note This does not count virtual cores, such as those provided by HyperThreading
+ * when boost is newer than 1.56.
+ */
+int GetNumCores();
+
void SetThreadPriority(int nPriority);
void RenameThread(const char* name);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 0532da5f37..d365f03008 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -19,9 +19,13 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
+ g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
+ g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
+ g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
+ g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
@@ -31,6 +35,8 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
}
void UnregisterAllValidationInterfaces() {
+ g_signals.BlockFound.disconnect_all_slots();
+ g_signals.ScriptForMining.disconnect_all_slots();
g_signals.BlockChecked.disconnect_all_slots();
g_signals.Broadcast.disconnect_all_slots();
g_signals.Inventory.disconnect_all_slots();
diff --git a/src/validationinterface.h b/src/validationinterface.h
index a911d1efeb..fb0ce0bdaa 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -7,9 +7,11 @@
#define BITCOIN_VALIDATIONINTERFACE_H
#include <boost/signals2/signal.hpp>
+#include <boost/shared_ptr.hpp>
class CBlock;
struct CBlockLocator;
+class CReserveScript;
class CTransaction;
class CValidationInterface;
class CValidationState;
@@ -34,6 +36,8 @@ 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<CReserveScript>&) {};
+ virtual void ResetRequestCount(const uint256 &hash) {};
friend void ::RegisterValidationInterface(CValidationInterface*);
friend void ::UnregisterValidationInterface(CValidationInterface*);
friend void ::UnregisterAllValidationInterfaces();
@@ -52,6 +56,10 @@ struct CMainSignals {
boost::signals2::signal<void (int64_t nBestBlockTime)> Broadcast;
/** Notifies listeners of a block validation result */
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
+ /** Notifies listeners that a key for mining is required (coinbase) */
+ boost::signals2::signal<void (boost::shared_ptr<CReserveScript>&)> ScriptForMining;
+ /** Notifies listeners that a block has been successfully mined */
+ boost::signals2::signal<void (const uint256 &)> BlockFound;
};
CMainSignals& GetMainSignals();
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 46037ef668..bb96255361 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -420,6 +420,7 @@ void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range)
const uint256& hash = it->second;
CWalletTx* copyTo = &mapWallet[hash];
if (copyFrom == copyTo) continue;
+ if (!copyFrom->IsEquivalentTo(*copyTo)) continue;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// fTimeReceivedIsTxTime not copied on purpose
@@ -2584,6 +2585,17 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx)
}
}
+void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)
+{
+ boost::shared_ptr<CReserveKey> rKey(new CReserveKey(this));
+ CPubKey pubkey;
+ if (!rKey->GetReservedKey(pubkey))
+ return;
+
+ script = rKey;
+ script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
+}
+
void CWallet::LockCoin(COutPoint& output)
{
AssertLockHeld(cs_wallet); // setLockedCoins
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index b6a8e8671f..f36c98e9fc 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -28,6 +28,8 @@
#include <utility>
#include <vector>
+#include <boost/shared_ptr.hpp>
+
/**
* Settings
*/
@@ -680,6 +682,13 @@ public:
}
}
+ void GetScriptForMining(boost::shared_ptr<CReserveScript> &script);
+ void ResetRequestCount(const uint256 &hash)
+ {
+ LOCK(cs_wallet);
+ mapRequestCount[hash] = 0;
+ };
+
unsigned int GetKeyPoolSize()
{
AssertLockHeld(cs_wallet); // setKeyPool
@@ -735,7 +744,7 @@ public:
};
/** A key allocated from the key pool. */
-class CReserveKey
+class CReserveKey : public CReserveScript
{
protected:
CWallet* pwallet;
@@ -756,6 +765,7 @@ public:
void ReturnKey();
bool GetReservedKey(CPubKey &pubkey);
void KeepKey();
+ void KeepScript() { KeepKey(); }
};