diff --git a/Dockerfile b/Dockerfile index 92b0b779c..7a4308f7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ RUN apt-get -y update && \ apt-get -y upgrade && \ apt-get -y install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool ncurses-dev \ unzip python zlib1g-dev wget bsdmainutils automake libssl-dev libprotobuf-dev \ - protobuf-compiler libqrencode-dev libdb++-dev software-properties-common libcurl4-openssl-dev && \ + protobuf-compiler libqrencode-dev libdb++-dev software-properties-common libcurl4-openssl-dev curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* diff --git a/Makefile.am b/Makefile.am index 143b2cc68..d0c289ee1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ endif BITCOIND_BIN=$(top_builddir)/src/zcashd$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/zcash-cli$(EXEEXT) +WALLET_UTILITY_BIN=$(top_builddir)/src/wallet-utility$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) if TARGET_DARWIN @@ -32,26 +33,26 @@ endif DIST_DOCS = $(wildcard doc/*.md) $(wildcard doc/release-notes/*.md) BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ - $(top_srcdir)/contrib/devtools/security-check.py + $(top_srcdir)/contrib/devtools/security-check.py WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \ - $(top_srcdir)/share/pixmaps/nsis-header.bmp \ - $(top_srcdir)/share/pixmaps/nsis-wizard.bmp + $(top_srcdir)/share/pixmaps/nsis-header.bmp \ + $(top_srcdir)/share/pixmaps/nsis-wizard.bmp if TARGET_DARWIN OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) $(OSX_BASE_LPROJ_DIR) \ - $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) \ - $(top_srcdir)/contrib/macdeploy/DS_Store \ - $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ - $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh + $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) \ + $(top_srcdir)/contrib/macdeploy/DS_Store \ + $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ + $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh endif if TARGET_DARWIN COVERAGE_INFO = baseline_filtered_combined.info baseline.info block_test.info \ - leveldb_baseline.info test_bitcoin_filtered.info total_coverage.info \ - baseline_filtered.info block_test_filtered.info \ - leveldb_baseline_filtered.info test_bitcoin_coverage.info test_bitcoin.info + leveldb_baseline.info test_bitcoin_filtered.info total_coverage.info \ + baseline_filtered.info block_test_filtered.info \ + leveldb_baseline_filtered.info test_bitcoin_coverage.info test_bitcoin.info # zcash-gtest.info zcash-gtest_filtered.info zcash-gtest_coverage.info else COVERAGE_INFO = baseline_filtered_combined.info baseline.info block_test.info \ @@ -104,9 +105,9 @@ $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(OSX_BASE_LPROJ_DIR $(INSTALL_DATA) $< $@ OSX_APP_BUILT=$(OSX_APP)/Contents/PkgInfo $(OSX_APP)/Contents/Resources/empty.lproj \ - $(OSX_APP)/Contents/Resources/bitcoin.icns $(OSX_APP)/Contents/Info.plist \ - $(OSX_APP)/Contents/MacOS/Bitcoin-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings - + $(OSX_APP)/Contents/Resources/bitcoin.icns $(OSX_APP)/Contents/Info.plist \ + $(OSX_APP)/Contents/MacOS/Bitcoin-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings + endif if BUILD_DARWIN @@ -155,6 +156,9 @@ $(BITCOIND_BIN): FORCE $(BITCOIN_CLI_BIN): FORCE $(MAKE) -C src $(@F) +$(WALLET_UTILITY_BIN): FORCE + $(MAKE) -C src $(@F) + if USE_LCOV baseline.info: @@ -163,27 +167,27 @@ baseline.info: if BUILD_DARWIN baseline_filtered.info: baseline.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ - "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ + "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ else baseline_filtered.info: baseline.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ endif leveldb_baseline.info: baseline_filtered.info @@ -192,27 +196,27 @@ leveldb_baseline.info: baseline_filtered.info if BUILD_DARWIN leveldb_baseline_filtered.info: leveldb_baseline.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ - "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ + "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ else leveldb_baseline_filtered.info: leveldb_baseline.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ endif baseline_filtered_combined.info: leveldb_baseline_filtered.info baseline_filtered.info @@ -227,27 +231,27 @@ test_bitcoin.info: baseline_filtered_combined.info if BUILD_DARWIN test_bitcoin_filtered.info: test_bitcoin.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ - "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ + "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ else test_bitcoin_filtered.info: test_bitcoin.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ endif @@ -261,32 +265,32 @@ block_test.info: test_bitcoin_filtered.info if BUILD_DARWIN block_test_filtered.info: block_test.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ - "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ - "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/$(BUILD)/include/*.h" \ + "$(abs_builddir)/depends/$(BUILD)/include/boost/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gmock/*" \ + "$(abs_builddir)/depends/$(BUILD)/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ else block_test_filtered.info: block_test.info $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ + "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ + "$(abs_builddir)/src/gtest/*" \ + "$(abs_builddir)/src/test/*" \ + "$(abs_builddir)/src/wallet/gtest/*" \ + "$(abs_builddir)/src/wallet/test/*" \ + -o $@ endif test_bitcoin_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info - $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -o $@ + $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -o $@ if ! BUILD_DARWIN zcash-gtest_coverage.info: baseline_filtered_combined.info zcash-gtest_filtered.info @@ -295,7 +299,7 @@ endif if BUILD_DARWIN total_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info zcash-gtest_filtered.info block_test_filtered.info - $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -a zcash-gtest_filtered.info -a block_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt + $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -a zcash-gtest_filtered.info -a block_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt else total_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info zcash-gtest_filtered.info block_test_filtered.info $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -a zcash-gtest_filtered.info -a block_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt @@ -303,19 +307,19 @@ endif test_bitcoin.coverage/.dirstamp: test_bitcoin_coverage.info - $(GENHTML) -s $< -o $(@D) - @touch $@ + $(GENHTML) -s $< -o $(@D) + @touch $@ if TARGET_DARWIN zcash-gtest.coverage/.dirstamp: zcash-gtest_coverage.info - $(GENHTML) -s $< -o $(@D) - @touch $@ + $(GENHTML) -s $< -o $(@D) + @touch $@ cov-zcash: zcash-gtest.coverage/.dirstamp endif total.coverage/.dirstamp: total_coverage.info - $(GENHTML) -s $< -o $(@D) - @touch $@ + $(GENHTML) -s $< -o $(@D) + @touch $@ if BUILD_DARWIN cov: test_bitcoin.coverage/.dirstamp cov-zcash total.coverage/.dirstamp diff --git a/README.md b/README.md index 25c573317..e766870a5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -Komodo 1.0.15 - +## Komodo with Bitcore +This version of Komodo contains Bitcore support for komodo and all its assetchains. ## Komodod This software is Komodo client, generally you will use this if you want to mine KMD or setup a full node. It downloads and stores the entire history of Komodo transactions; depending on the speed of your computer and network connection, the synchronization process could take a day or more once the blockchain has reached a significant size. @@ -34,7 +34,7 @@ Dependencies ``` #The following packages are needed: -sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool ncurses-dev unzip git python python-zmq zlib1g-dev wget libcurl3-gnutls-dev bsdmainutils automake +sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool ncurses-dev unzip git python python-zmq zlib1g-dev wget libcurl4-openssl-dev bsdmainutils automake curl ``` Komodo diff --git a/configure.ac b/configure.ac index decf2b2e6..7de40ff26 100644 --- a/configure.ac +++ b/configure.ac @@ -232,7 +232,7 @@ CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], - [build zcash-cli zcash-tx (default=yes)])], + [build zcash-cli zcash-tx wallet-utility (default=yes)])], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) @@ -805,7 +805,7 @@ AC_MSG_CHECKING([whether to build bitcoind]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) AC_MSG_RESULT($build_bitcoind) -AC_MSG_CHECKING([whether to build utils (zcash-cli zcash-tx)]) +AC_MSG_CHECKING([whether to build utils (zcash-cli zcash-tx wallet-utility)]) AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test x$build_bitcoin_utils = xyes]) AC_MSG_RESULT($build_bitcoin_utils) diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 0ed693951..50de39f64 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -44,6 +44,9 @@ testScripts=( 'key_import_export.py' 'nodehandling.py' 'reindex.py' + 'addressindex.py' + 'timestampindex.py' + 'spentindex.py' 'decodescript.py' 'disablewallet.py' 'zcjoinsplit.py' diff --git a/qa/rpc-tests/addressindex.py b/qa/rpc-tests/addressindex.py new file mode 100644 index 000000000..11fa7ffdb --- /dev/null +++ b/qa/rpc-tests/addressindex.py @@ -0,0 +1,349 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014-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. + +# +# Test addressindex generation and fetching +# + +import time +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from test_framework.script import * +from test_framework.mininode import * +import binascii + +class AddressIndexTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self): + self.nodes = [] + # Nodes 0/1 are "wallet" nodes + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-relaypriority=0"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-addressindex"])) + # Nodes 2/3 are used for testing + self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-addressindex", "-relaypriority=0"])) + self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-addressindex"])) + connect_nodes(self.nodes[0], 1) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[0], 3) + + self.is_network_split = False + self.sync_all() + + def run_test(self): + print "Mining blocks..." + self.nodes[0].generate(105) + self.sync_all() + + chain_height = self.nodes[1].getblockcount() + assert_equal(chain_height, 105) + assert_equal(self.nodes[1].getbalance(), 0) + assert_equal(self.nodes[2].getbalance(), 0) + + # Check that balances are correct + balance0 = self.nodes[1].getaddressbalance("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") + assert_equal(balance0["balance"], 0) + + # Check p2pkh and p2sh address indexes + print "Testing p2pkh and p2sh address index..." + + txid0 = self.nodes[0].sendtoaddress("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 10) + self.nodes[0].generate(1) + + txidb0 = self.nodes[0].sendtoaddress("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 10) + self.nodes[0].generate(1) + + txid1 = self.nodes[0].sendtoaddress("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 15) + self.nodes[0].generate(1) + + txidb1 = self.nodes[0].sendtoaddress("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 15) + self.nodes[0].generate(1) + + txid2 = self.nodes[0].sendtoaddress("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 20) + self.nodes[0].generate(1) + + txidb2 = self.nodes[0].sendtoaddress("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 20) + self.nodes[0].generate(1) + + self.sync_all() + + txids = self.nodes[1].getaddresstxids("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs") + assert_equal(len(txids), 3) + assert_equal(txids[0], txid0) + assert_equal(txids[1], txid1) + assert_equal(txids[2], txid2) + + txidsb = self.nodes[1].getaddresstxids("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") + assert_equal(len(txidsb), 3) + assert_equal(txidsb[0], txidb0) + assert_equal(txidsb[1], txidb1) + assert_equal(txidsb[2], txidb2) + + # Check that limiting by height works + print "Testing querying txids by range of block heights.." + height_txids = self.nodes[1].getaddresstxids({ + "addresses": ["2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br"], + "start": 105, + "end": 110 + }) + assert_equal(len(height_txids), 2) + assert_equal(height_txids[0], txidb0) + assert_equal(height_txids[1], txidb1) + + # Check that multiple addresses works + multitxids = self.nodes[1].getaddresstxids({"addresses": ["2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs"]}) + assert_equal(len(multitxids), 6) + assert_equal(multitxids[0], txid0) + assert_equal(multitxids[1], txidb0) + assert_equal(multitxids[2], txid1) + assert_equal(multitxids[3], txidb1) + assert_equal(multitxids[4], txid2) + assert_equal(multitxids[5], txidb2) + + # Check that balances are correct + balance0 = self.nodes[1].getaddressbalance("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") + assert_equal(balance0["balance"], 45 * 100000000) + + # Check that outputs with the same address will only return one txid + print "Testing for txid uniqueness..." + addressHash = "6349a418fc4578d10a372b54b45c280cc8c4382f".decode("hex") + scriptPubKey = CScript([OP_HASH160, addressHash, OP_EQUAL]) + unspent = self.nodes[0].listunspent() + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] + tx.vout = [CTxOut(10, scriptPubKey), CTxOut(11, scriptPubKey)] + tx.rehash() + + signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + sent_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) + + self.nodes[0].generate(1) + self.sync_all() + + txidsmany = self.nodes[1].getaddresstxids("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") + assert_equal(len(txidsmany), 4) + assert_equal(txidsmany[3], sent_txid) + + # Check that balances are correct + print "Testing balances..." + balance0 = self.nodes[1].getaddressbalance("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") + assert_equal(balance0["balance"], 45 * 100000000 + 21) + + # Check that balances are correct after spending + print "Testing balances after spending..." + privkey2 = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" + address2 = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" + addressHash2 = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") + scriptPubKey2 = CScript([OP_DUP, OP_HASH160, addressHash2, OP_EQUALVERIFY, OP_CHECKSIG]) + self.nodes[0].importprivkey(privkey2) + + unspent = self.nodes[0].listunspent() + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] + amount = unspent[0]["amount"] * 100000000 + tx.vout = [CTxOut(amount, scriptPubKey2)] + tx.rehash() + signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + spending_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) + self.nodes[0].generate(1) + self.sync_all() + balance1 = self.nodes[1].getaddressbalance(address2) + assert_equal(balance1["balance"], amount) + + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(int(spending_txid, 16), 0))] + send_amount = 1 * 100000000 + 12840 + change_amount = amount - send_amount - 10000 + tx.vout = [CTxOut(change_amount, scriptPubKey2), CTxOut(send_amount, scriptPubKey)] + tx.rehash() + + signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + sent_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) + self.nodes[0].generate(1) + self.sync_all() + + balance2 = self.nodes[1].getaddressbalance(address2) + assert_equal(balance2["balance"], change_amount) + + # Check that deltas are returned correctly + deltas = self.nodes[1].getaddressdeltas({"addresses": [address2], "start": 1, "end": 200}) + balance3 = 0 + for delta in deltas: + balance3 += delta["satoshis"] + assert_equal(balance3, change_amount) + assert_equal(deltas[0]["address"], address2) + assert_equal(deltas[0]["blockindex"], 1) + + # Check that entire range will be queried + deltasAll = self.nodes[1].getaddressdeltas({"addresses": [address2]}) + assert_equal(len(deltasAll), len(deltas)) + + # Check that deltas can be returned from range of block heights + deltas = self.nodes[1].getaddressdeltas({"addresses": [address2], "start": 113, "end": 113}) + assert_equal(len(deltas), 1) + + # Check that unspent outputs can be queried + print "Testing utxos..." + utxos = self.nodes[1].getaddressutxos({"addresses": [address2]}) + assert_equal(len(utxos), 1) + assert_equal(utxos[0]["satoshis"], change_amount) + + # Check that indexes will be updated with a reorg + print "Testing reorg..." + + best_hash = self.nodes[0].getbestblockhash() + self.nodes[0].invalidateblock(best_hash) + self.nodes[1].invalidateblock(best_hash) + self.nodes[2].invalidateblock(best_hash) + self.nodes[3].invalidateblock(best_hash) + self.sync_all() + + balance4 = self.nodes[1].getaddressbalance(address2) + assert_equal(balance4, balance1) + + utxos2 = self.nodes[1].getaddressutxos({"addresses": [address2]}) + assert_equal(len(utxos2), 1) + assert_equal(utxos2[0]["satoshis"], amount) + + # Check sorting of utxos + self.nodes[2].generate(150) + + txidsort1 = self.nodes[2].sendtoaddress(address2, 50) + self.nodes[2].generate(1) + txidsort2 = self.nodes[2].sendtoaddress(address2, 50) + self.nodes[2].generate(1) + self.sync_all() + + utxos3 = self.nodes[1].getaddressutxos({"addresses": [address2]}) + assert_equal(len(utxos3), 3) + assert_equal(utxos3[0]["height"], 114) + assert_equal(utxos3[1]["height"], 264) + assert_equal(utxos3[2]["height"], 265) + + # Check mempool indexing + print "Testing mempool indexing..." + + privKey3 = "cVfUn53hAbRrDEuMexyfgDpZPhF7KqXpS8UZevsyTDaugB7HZ3CD" + address3 = "mw4ynwhS7MmrQ27hr82kgqu7zryNDK26JB" + addressHash3 = "aa9872b5bbcdb511d89e0e11aa27da73fd2c3f50".decode("hex") + scriptPubKey3 = CScript([OP_DUP, OP_HASH160, addressHash3, OP_EQUALVERIFY, OP_CHECKSIG]) + address4 = "2N8oFVB2vThAKury4vnLquW2zVjsYjjAkYQ" + scriptPubKey4 = CScript([OP_HASH160, addressHash3, OP_EQUAL]) + unspent = self.nodes[2].listunspent() + + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] + amount = unspent[0]["amount"] * 100000000 + tx.vout = [CTxOut(amount, scriptPubKey3)] + tx.rehash() + signed_tx = self.nodes[2].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + memtxid1 = self.nodes[2].sendrawtransaction(signed_tx["hex"], True) + time.sleep(2) + + tx2 = CTransaction() + tx2.vin = [CTxIn(COutPoint(int(unspent[1]["txid"], 16), unspent[1]["vout"]))] + amount = unspent[1]["amount"] * 100000000 + tx2.vout = [ + CTxOut(amount / 4, scriptPubKey3), + CTxOut(amount / 4, scriptPubKey3), + CTxOut(amount / 4, scriptPubKey4), + CTxOut(amount / 4, scriptPubKey4) + ] + tx2.rehash() + signed_tx2 = self.nodes[2].signrawtransaction(binascii.hexlify(tx2.serialize()).decode("utf-8")) + memtxid2 = self.nodes[2].sendrawtransaction(signed_tx2["hex"], True) + time.sleep(2) + + mempool = self.nodes[2].getaddressmempool({"addresses": [address3]}) + assert_equal(len(mempool), 3) + assert_equal(mempool[0]["txid"], memtxid1) + assert_equal(mempool[0]["address"], address3) + assert_equal(mempool[0]["index"], 0) + assert_equal(mempool[1]["txid"], memtxid2) + assert_equal(mempool[1]["index"], 0) + assert_equal(mempool[2]["txid"], memtxid2) + assert_equal(mempool[2]["index"], 1) + + self.nodes[2].generate(1); + self.sync_all(); + mempool2 = self.nodes[2].getaddressmempool({"addresses": [address3]}) + assert_equal(len(mempool2), 0) + + tx = CTransaction() + tx.vin = [ + CTxIn(COutPoint(int(memtxid2, 16), 0)), + CTxIn(COutPoint(int(memtxid2, 16), 1)) + ] + tx.vout = [CTxOut(amount / 2 - 10000, scriptPubKey2)] + tx.rehash() + self.nodes[2].importprivkey(privKey3) + signed_tx3 = self.nodes[2].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + memtxid3 = self.nodes[2].sendrawtransaction(signed_tx3["hex"], True) + time.sleep(2) + + mempool3 = self.nodes[2].getaddressmempool({"addresses": [address3]}) + assert_equal(len(mempool3), 2) + assert_equal(mempool3[0]["prevtxid"], memtxid2) + assert_equal(mempool3[0]["prevout"], 0) + assert_equal(mempool3[1]["prevtxid"], memtxid2) + assert_equal(mempool3[1]["prevout"], 1) + + # sending and receiving to the same address + privkey1 = "cQY2s58LhzUCmEXN8jtAp1Etnijx78YRZ466w4ikX1V4UpTpbsf8" + address1 = "myAUWSHnwsQrhuMWv4Br6QsCnpB41vFwHn" + address1hash = "c192bff751af8efec15135d42bfeedf91a6f3e34".decode("hex") + address1script = CScript([OP_DUP, OP_HASH160, address1hash, OP_EQUALVERIFY, OP_CHECKSIG]) + + self.nodes[0].sendtoaddress(address1, 10) + self.nodes[0].generate(1) + self.sync_all() + + utxos = self.nodes[1].getaddressutxos({"addresses": [address1]}) + assert_equal(len(utxos), 1) + + tx = CTransaction() + tx.vin = [ + CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["outputIndex"])) + ] + amount = utxos[0]["satoshis"] - 1000 + tx.vout = [CTxOut(amount, address1script)] + tx.rehash() + self.nodes[0].importprivkey(privkey1) + signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + mem_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) + + self.sync_all() + mempool_deltas = self.nodes[2].getaddressmempool({"addresses": [address1]}) + assert_equal(len(mempool_deltas), 2) + + # Include chaininfo in results + print "Testing results with chain info..." + + deltas_with_info = self.nodes[1].getaddressdeltas({ + "addresses": [address2], + "start": 1, + "end": 200, + "chainInfo": True + }) + start_block_hash = self.nodes[1].getblockhash(1); + end_block_hash = self.nodes[1].getblockhash(200); + assert_equal(deltas_with_info["start"]["height"], 1) + assert_equal(deltas_with_info["start"]["hash"], start_block_hash) + assert_equal(deltas_with_info["end"]["height"], 200) + assert_equal(deltas_with_info["end"]["hash"], end_block_hash) + + utxos_with_info = self.nodes[1].getaddressutxos({"addresses": [address2], "chainInfo": True}) + expected_tip_block_hash = self.nodes[1].getblockhash(267); + assert_equal(utxos_with_info["height"], 267) + assert_equal(utxos_with_info["hash"], expected_tip_block_hash) + + print "Passed\n" + + +if __name__ == '__main__': + AddressIndexTest().main() \ No newline at end of file diff --git a/qa/rpc-tests/spentindex.py b/qa/rpc-tests/spentindex.py new file mode 100644 index 000000000..ebe2b8cc2 --- /dev/null +++ b/qa/rpc-tests/spentindex.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014-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. + +# +# Test addressindex generation and fetching +# + +import time +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from test_framework.script import * +from test_framework.mininode import * +import binascii + +class SpentIndexTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self): + self.nodes = [] + # Nodes 0/1 are "wallet" nodes + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-spentindex"])) + # Nodes 2/3 are used for testing + self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-spentindex"])) + self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-spentindex", "-txindex"])) + connect_nodes(self.nodes[0], 1) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[0], 3) + + self.is_network_split = False + self.sync_all() + + def run_test(self): + print "Mining blocks..." + self.nodes[0].generate(105) + self.sync_all() + + chain_height = self.nodes[1].getblockcount() + assert_equal(chain_height, 105) + + # Check that + print "Testing spent index..." + + privkey = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" + address = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" + addressHash = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") + scriptPubKey = CScript([OP_DUP, OP_HASH160, addressHash, OP_EQUALVERIFY, OP_CHECKSIG]) + unspent = self.nodes[0].listunspent() + tx = CTransaction() + amount = unspent[0]["amount"] * 100000000 + tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] + tx.vout = [CTxOut(amount, scriptPubKey)] + tx.rehash() + + signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) + self.nodes[0].generate(1) + self.sync_all() + + print "Testing getspentinfo method..." + + # Check that the spentinfo works standalone + info = self.nodes[1].getspentinfo({"txid": unspent[0]["txid"], "index": unspent[0]["vout"]}) + assert_equal(info["txid"], txid) + assert_equal(info["index"], 0) + assert_equal(info["height"], 106) + + print "Testing getrawtransaction method..." + + # Check that verbose raw transaction includes spent info + txVerbose = self.nodes[3].getrawtransaction(unspent[0]["txid"], 1) + assert_equal(txVerbose["vout"][unspent[0]["vout"]]["spentTxId"], txid) + assert_equal(txVerbose["vout"][unspent[0]["vout"]]["spentIndex"], 0) + assert_equal(txVerbose["vout"][unspent[0]["vout"]]["spentHeight"], 106) + + # Check that verbose raw transaction includes input values + txVerbose2 = self.nodes[3].getrawtransaction(txid, 1) + assert_equal(txVerbose2["vin"][0]["value"], Decimal(unspent[0]["amount"])) + assert_equal(txVerbose2["vin"][0]["valueSat"], amount) + + # Check that verbose raw transaction includes address values and input values + privkey2 = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" + address2 = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" + addressHash2 = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") + scriptPubKey2 = CScript([OP_DUP, OP_HASH160, addressHash2, OP_EQUALVERIFY, OP_CHECKSIG]) + tx2 = CTransaction() + tx2.vin = [CTxIn(COutPoint(int(txid, 16), 0))] + tx2.vout = [CTxOut(amount, scriptPubKey2)] + tx.rehash() + self.nodes[0].importprivkey(privkey) + signed_tx2 = self.nodes[0].signrawtransaction(binascii.hexlify(tx2.serialize()).decode("utf-8")) + txid2 = self.nodes[0].sendrawtransaction(signed_tx2["hex"], True) + + # Check the mempool index + self.sync_all() + txVerbose3 = self.nodes[1].getrawtransaction(txid2, 1) + assert_equal(txVerbose3["vin"][0]["address"], address2) + assert_equal(txVerbose3["vin"][0]["value"], Decimal(unspent[0]["amount"])) + assert_equal(txVerbose3["vin"][0]["valueSat"], amount) + + # Check the database index + block_hash = self.nodes[0].generate(1) + self.sync_all() + + txVerbose4 = self.nodes[3].getrawtransaction(txid2, 1) + assert_equal(txVerbose4["vin"][0]["address"], address2) + assert_equal(txVerbose4["vin"][0]["value"], Decimal(unspent[0]["amount"])) + assert_equal(txVerbose4["vin"][0]["valueSat"], amount) + + + # Check block deltas + print "Testing getblockdeltas..." + + block = self.nodes[3].getblockdeltas(block_hash[0]) + assert_equal(len(block["deltas"]), 2) + assert_equal(block["deltas"][0]["index"], 0) + assert_equal(len(block["deltas"][0]["inputs"]), 0) + assert_equal(len(block["deltas"][0]["outputs"]), 0) + assert_equal(block["deltas"][1]["index"], 1) + assert_equal(block["deltas"][1]["txid"], txid2) + assert_equal(block["deltas"][1]["inputs"][0]["index"], 0) + assert_equal(block["deltas"][1]["inputs"][0]["address"], "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW") + assert_equal(block["deltas"][1]["inputs"][0]["satoshis"], amount * -1) + assert_equal(block["deltas"][1]["inputs"][0]["prevtxid"], txid) + assert_equal(block["deltas"][1]["inputs"][0]["prevout"], 0) + assert_equal(block["deltas"][1]["outputs"][0]["index"], 0) + assert_equal(block["deltas"][1]["outputs"][0]["address"], "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW") + assert_equal(block["deltas"][1]["outputs"][0]["satoshis"], amount) + + print "Passed\n" + + +if __name__ == '__main__': + SpentIndexTest().main() \ No newline at end of file diff --git a/qa/rpc-tests/timestampindex.py b/qa/rpc-tests/timestampindex.py new file mode 100644 index 000000000..23244159f --- /dev/null +++ b/qa/rpc-tests/timestampindex.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014-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. + +# +# Test timestampindex generation and fetching +# + +import time + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + + +class TimestampIndexTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self): + self.nodes = [] + # Nodes 0/1 are "wallet" nodes + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-timestampindex"])) + # Nodes 2/3 are used for testing + self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"])) + self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-timestampindex"])) + connect_nodes(self.nodes[0], 1) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[0], 3) + + self.is_network_split = False + self.sync_all() + + def run_test(self): + print "Mining 25 blocks..." + blockhashes = self.nodes[0].generate(25) + time.sleep(3) + print "Mining 25 blocks..." + blockhashes.extend(self.nodes[0].generate(25)) + time.sleep(3) + print "Mining 25 blocks..." + blockhashes.extend(self.nodes[0].generate(25)) + self.sync_all() + low = self.nodes[1].getblock(blockhashes[0])["time"] + high = low + 76 + + print "Checking timestamp index..." + hashes = self.nodes[1].getblockhashes(high, low) + + assert_equal(len(hashes), len(blockhashes)) + + assert_equal(hashes, blockhashes) + + print "Passed\n" + + +if __name__ == '__main__': + TimestampIndexTest().main() \ No newline at end of file diff --git a/qa/rpc-tests/txindex.py b/qa/rpc-tests/txindex.py new file mode 100644 index 000000000..e59b6d7d9 --- /dev/null +++ b/qa/rpc-tests/txindex.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014-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. + +# +# Test txindex generation and fetching +# + +import time +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from test_framework.script import * +from test_framework.mininode import * +import binascii + +class TxIndexTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self): + self.nodes = [] + # Nodes 0/1 are "wallet" nodes + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-txindex"])) + # Nodes 2/3 are used for testing + self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-txindex"])) + self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-txindex"])) + connect_nodes(self.nodes[0], 1) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[0], 3) + + self.is_network_split = False + self.sync_all() + + def run_test(self): + print "Mining blocks..." + self.nodes[0].generate(105) + self.sync_all() + + chain_height = self.nodes[1].getblockcount() + assert_equal(chain_height, 105) + + print "Testing transaction index..." + + privkey = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" + address = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" + addressHash = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") + scriptPubKey = CScript([OP_DUP, OP_HASH160, addressHash, OP_EQUALVERIFY, OP_CHECKSIG]) + unspent = self.nodes[0].listunspent() + tx = CTransaction() + amount = unspent[0]["amount"] * 100000000 + tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] + tx.vout = [CTxOut(amount, scriptPubKey)] + tx.rehash() + + signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) + txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) + self.nodes[0].generate(1) + self.sync_all() + + # Check verbose raw transaction results + verbose = self.nodes[3].getrawtransaction(unspent[0]["txid"], 1) + assert_equal(verbose["vout"][0]["valueSat"], 5000000000); + assert_equal(verbose["vout"][0]["value"], 50); + + print "Passed\n" + + +if __name__ == '__main__': + TxIndexTest().main() \ No newline at end of file diff --git a/qa/zcash/create_wallet_200k_utxos.py b/qa/zcash/create_wallet_200k_utxos.py old mode 100755 new mode 100644 diff --git a/qa/zcash/full-test-suite.sh b/qa/zcash/full-test-suite.sh new file mode 100755 index 000000000..af4fb2791 --- /dev/null +++ b/qa/zcash/full-test-suite.sh @@ -0,0 +1,201 @@ +#!/usr/bin/env python2 +# +# Execute all of the automated tests related to Zcash. +# + +import argparse +import os +import re +import subprocess +import sys + +REPOROOT = os.path.dirname( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__) + ) + ) +) + +def repofile(filename): + return os.path.join(REPOROOT, filename) + + +# +# Custom test runners +# + +RE_RPATH_RUNPATH = re.compile('No RPATH.*No RUNPATH') +RE_FORTIFY_AVAILABLE = re.compile('FORTIFY_SOURCE support available.*Yes') +RE_FORTIFY_USED = re.compile('Binary compiled with FORTIFY_SOURCE support.*Yes') + +def test_rpath_runpath(filename): + output = subprocess.check_output( + [repofile('qa/zcash/checksec.sh'), '--file', repofile(filename)] + ) + if RE_RPATH_RUNPATH.search(output): + print('PASS: %s has no RPATH or RUNPATH.' % filename) + return True + else: + print('FAIL: %s has an RPATH or a RUNPATH.' % filename) + print(output) + return False + +def test_fortify_source(filename): + proc = subprocess.Popen( + [repofile('qa/zcash/checksec.sh'), '--fortify-file', repofile(filename)], + stdout=subprocess.PIPE, + ) + line1 = proc.stdout.readline() + line2 = proc.stdout.readline() + proc.terminate() + if RE_FORTIFY_AVAILABLE.search(line1) and RE_FORTIFY_USED.search(line2): + print('PASS: %s has FORTIFY_SOURCE.' % filename) + return True + else: + print('FAIL: %s is missing FORTIFY_SOURCE.' % filename) + return False + +def check_security_hardening(): + ret = True + + # PIE, RELRO, Canary, and NX are tested by make check-security. + ret &= subprocess.call(['make', '-C', repofile('src'), 'check-security']) == 0 + + ret &= test_rpath_runpath('src/zcashd') + ret &= test_rpath_runpath('src/zcash-cli') + ret &= test_rpath_runpath('src/zcash-gtest') + ret &= test_rpath_runpath('src/zcash-tx') + ret &= test_rpath_runpath('src/test/test_bitcoin') + ret &= test_rpath_runpath('src/zcash/GenerateParams') + + # NOTE: checksec.sh does not reliably determine whether FORTIFY_SOURCE + # is enabled for the entire binary. See issue #915. + ret &= test_fortify_source('src/zcashd') + ret &= test_fortify_source('src/zcash-cli') + ret &= test_fortify_source('src/zcash-gtest') + ret &= test_fortify_source('src/zcash-tx') + ret &= test_fortify_source('src/test/test_bitcoin') + ret &= test_fortify_source('src/zcash/GenerateParams') + + return ret + +def ensure_no_dot_so_in_depends(): + arch_dir = os.path.join( + REPOROOT, + 'depends', + 'x86_64-unknown-linux-gnu', + ) + + exit_code = 0 + + if os.path.isdir(arch_dir): + lib_dir = os.path.join(arch_dir, 'lib') + libraries = os.listdir(lib_dir) + + for lib in libraries: + if lib.find(".so") != -1: + print lib + exit_code = 1 + else: + exit_code = 2 + print "arch-specific build dir not present: {}".format(arch_dir) + print "Did you build the ./depends tree?" + print "Are you on a currently unsupported architecture?" + + if exit_code == 0: + print "PASS." + else: + print "FAIL." + + return exit_code == 0 + +def util_test(): + return subprocess.call( + [repofile('src/test/bitcoin-util-test.py')], + cwd=repofile('src'), + env={'PYTHONPATH': repofile('src/test'), 'srcdir': repofile('src')} + ) == 0 + + +# +# Tests +# + +STAGES = [ + 'btest', + 'gtest', + 'sec-hard', + 'no-dot-so', + 'util-test', + 'secp256k1', + 'libsnark', + 'univalue', + 'rpc', +] + +STAGE_COMMANDS = { + 'btest': [repofile('src/test/test_bitcoin'), '-p'], + 'gtest': [repofile('src/zcash-gtest')], + 'sec-hard': check_security_hardening, + 'no-dot-so': ensure_no_dot_so_in_depends, + 'util-test': util_test, + 'secp256k1': ['make', '-C', repofile('src/secp256k1'), 'check'], + 'libsnark': ['make', '-C', repofile('src'), 'libsnark-tests'], + 'univalue': ['make', '-C', repofile('src/univalue'), 'check'], + 'rpc': [repofile('qa/pull-tester/rpc-tests.sh')], +} + + +# +# Test driver +# + +def run_stage(stage): + print('Running stage %s' % stage) + print('=' * (len(stage) + 14)) + print + + cmd = STAGE_COMMANDS[stage] + if type(cmd) == type([]): + ret = subprocess.call(cmd) == 0 + else: + ret = cmd() + + print + print('-' * (len(stage) + 15)) + print('Finished stage %s' % stage) + print + + return ret + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--list-stages', dest='list', action='store_true') + parser.add_argument('stage', nargs='*', default=STAGES, + help='One of %s'%STAGES) + args = parser.parse_args() + + # Check for list + if args.list: + for s in STAGES: + print(s) + sys.exit(0) + + # Check validity of stages + for s in args.stage: + if s not in STAGES: + print("Invalid stage '%s' (choose from %s)" % (s, STAGES)) + sys.exit(1) + + # Run the stages + passed = True + for s in args.stage: + passed &= run_stage(s) + + if not passed: + print("!!! One or more test stages failed !!!") + sys.exit(1) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/qa/zcash/performance-measurements.sh b/qa/zcash/performance-measurements.sh index 15f3077b5..3370bd20f 100755 --- a/qa/zcash/performance-measurements.sh +++ b/qa/zcash/performance-measurements.sh @@ -338,4 +338,4 @@ case "$1" in esac # Cleanup -rm -rf "$DATADIR" +rm -rf "$DATADIR" \ No newline at end of file diff --git a/qa/zcash/test-depends-sources-mirror.py b/qa/zcash/test-depends-sources-mirror.py index e09c2c238..6b612d715 100755 --- a/qa/zcash/test-depends-sources-mirror.py +++ b/qa/zcash/test-depends-sources-mirror.py @@ -40,4 +40,4 @@ for filename in get_depends_sources_list(): sys.exit(1) print "PASS." -sys.exit(0) +sys.exit(0) \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index 00acd5e05..5166a6103 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,12 +72,12 @@ $(LIBCRYPTOCONDITIONS): $(wildcard cryptoconditions/src/*) $(wildcard cryptocond # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: EXTRA_LIBRARIES = \ - crypto/libbitcoin_crypto.a \ - libbitcoin_util.a \ - libbitcoin_common.a \ - libbitcoin_server.a \ - libbitcoin_cli.a \ - libzcash.a + crypto/libbitcoin_crypto.a \ + libbitcoin_util.a \ + libbitcoin_common.a \ + libbitcoin_server.a \ + libbitcoin_cli.a \ + libzcash.a if ENABLE_WALLET BITCOIN_INCLUDES += $(BDB_CPPFLAGS) EXTRA_LIBRARIES += libbitcoin_wallet.a @@ -107,6 +107,9 @@ endif if BUILD_BITCOIN_UTILS bin_PROGRAMS += komodo-cli komodo-tx endif +if ENABLE_WALLET + bin_PROGRAMS += wallet-utility +endif LIBZCASH_H = \ zcash/IncrementalMerkleTree.hpp \ @@ -122,6 +125,8 @@ LIBZCASH_H = \ .PHONY: FORCE collate-libsnark check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ + addressindex.h \ + spentindex.h \ addrman.h \ alert.h \ amount.h \ @@ -235,7 +240,7 @@ BITCOIN_CORE_H = \ obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \ - $(abs_top_srcdir) + $(abs_top_srcdir) libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: zcashd @@ -292,9 +297,9 @@ LIBBITCOIN_ZMQ=libbitcoin_zmq.a libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_zmq_a_SOURCES = \ - zmq/zmqabstractnotifier.cpp \ - zmq/zmqnotificationinterface.cpp \ - zmq/zmqpublishnotifier.cpp + zmq/zmqabstractnotifier.cpp \ + zmq/zmqnotificationinterface.cpp \ + zmq/zmqpublishnotifier.cpp endif if ENABLE_PROTON @@ -336,33 +341,33 @@ libbitcoin_wallet_a_SOURCES = \ crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ - crypto/common.h \ - crypto/equihash.cpp \ - crypto/equihash.h \ - crypto/equihash.tcc \ - crypto/hmac_sha256.cpp \ - crypto/hmac_sha256.h \ - crypto/hmac_sha512.cpp \ - crypto/hmac_sha512.h \ - crypto/ripemd160.cpp \ - crypto/ripemd160.h \ - crypto/sha1.cpp \ - crypto/sha1.h \ - crypto/sha256.cpp \ - crypto/sha256.h \ - crypto/sha512.cpp \ - crypto/sha512.h + crypto/common.h \ + crypto/equihash.cpp \ + crypto/equihash.h \ + crypto/equihash.tcc \ + crypto/hmac_sha256.cpp \ + crypto/hmac_sha256.h \ + crypto/hmac_sha512.cpp \ + crypto/hmac_sha512.h \ + crypto/ripemd160.cpp \ + crypto/ripemd160.h \ + crypto/sha1.cpp \ + crypto/sha1.h \ + crypto/sha256.cpp \ + crypto/sha256.h \ + crypto/sha512.cpp \ + crypto/sha512.h if ENABLE_MINING EQUIHASH_TROMP_SOURCES = \ - pow/tromp/equi_miner.h \ - pow/tromp/equi.h \ - pow/tromp/osx_barrier.h + pow/tromp/equi_miner.h \ + pow/tromp/equi.h \ + pow/tromp/osx_barrier.h crypto_libbitcoin_crypto_a_CPPFLAGS += \ - -DEQUIHASH_TROMP_ATOMIC + -DEQUIHASH_TROMP_ATOMIC crypto_libbitcoin_crypto_a_SOURCES += \ - ${EQUIHASH_TROMP_SOURCES} + ${EQUIHASH_TROMP_SOURCES} endif # common: shared between zcashd and non-server tools @@ -402,23 +407,23 @@ libbitcoin_common_a_SOURCES = \ libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_util_a_SOURCES = \ - support/pagelocker.cpp \ - chainparamsbase.cpp \ - clientversion.cpp \ - compat/glibc_sanity.cpp \ - compat/glibcxx_sanity.cpp \ - compat/strnlen.cpp \ - random.cpp \ - rpcprotocol.cpp \ - support/cleanse.cpp \ - sync.cpp \ - uint256.cpp \ - util.cpp \ - utilmoneystr.cpp \ - utilstrencodings.cpp \ - utiltime.cpp \ - $(BITCOIN_CORE_H) \ - $(LIBZCASH_H) + support/pagelocker.cpp \ + chainparamsbase.cpp \ + clientversion.cpp \ + compat/glibc_sanity.cpp \ + compat/glibcxx_sanity.cpp \ + compat/strnlen.cpp \ + random.cpp \ + rpcprotocol.cpp \ + support/cleanse.cpp \ + sync.cpp \ + uint256.cpp \ + util.cpp \ + utilmoneystr.cpp \ + utilstrencodings.cpp \ + utiltime.cpp \ + $(BITCOIN_CORE_H) \ + $(LIBZCASH_H) if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp @@ -428,9 +433,9 @@ endif libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_cli_a_SOURCES = \ - rpcclient.cpp \ - $(BITCOIN_CORE_H) \ - $(LIBZCASH_H) + rpcclient.cpp \ + $(BITCOIN_CORE_H) \ + $(LIBZCASH_H) nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # @@ -486,23 +491,45 @@ komodo_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) komodo_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) komodo_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +# wallet-utility binary # +if ENABLE_WALLET +wallet_utility_SOURCES = wallet-utility.cpp +wallet_utility_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +wallet_utility_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +wallet_utility_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +endif + if TARGET_WINDOWS komodo_cli_SOURCES += bitcoin-cli-res.rc endif komodo_cli_LDADD = \ - $(LIBBITCOIN_CLI) \ - $(LIBUNIVALUE) \ - $(LIBBITCOIN_UTIL) \ - $(BOOST_LIBS) \ - $(SSL_LIBS) \ - $(CRYPTO_LIBS) \ - $(EVENT_LIBS) \ - $(LIBZCASH) \ - $(LIBSNARK) \ - $(LIBBITCOIN_CRYPTO) \ - $(LIBZCASH_LIBS) -# + $(LIBBITCOIN_CLI) \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_UTIL) \ + $(BOOST_LIBS) \ + $(SSL_LIBS) \ + $(CRYPTO_LIBS) \ + $(EVENT_LIBS) \ + $(LIBZCASH) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBZCASH_LIBS) + +if ENABLE_WALLET +wallet_utility_LDADD = \ + libbitcoin_wallet.a \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBSECP256K1) \ + $(LIBBITCOIN_UTIL) \ + $(BOOST_LIBS) \ + $(BDB_LIBS) \ + $(CRYPTO_LIBS) \ + $(LIBZCASH) \ + $(LIBSNARK) \ + $(LIBZCASH_LIBS)\ + $(LIBCRYPTOCONDITIONS) +endif # zcash-tx binary # komodo_tx_SOURCES = komodo-tx.cpp @@ -531,20 +558,20 @@ komodo_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # zcash protocol primitives # libzcash_a_SOURCES = \ - zcash/IncrementalMerkleTree.cpp \ - zcash/NoteEncryption.cpp \ - zcash/Address.cpp \ - zcash/JoinSplit.cpp \ - zcash/Proof.cpp \ - zcash/Note.cpp \ - zcash/prf.cpp \ - zcash/util.cpp \ - zcash/circuit/commitment.tcc \ - zcash/circuit/gadget.tcc \ - zcash/circuit/merkle.tcc \ - zcash/circuit/note.tcc \ - zcash/circuit/prfs.tcc \ - zcash/circuit/utils.tcc + zcash/IncrementalMerkleTree.cpp \ + zcash/NoteEncryption.cpp \ + zcash/Address.cpp \ + zcash/JoinSplit.cpp \ + zcash/Proof.cpp \ + zcash/Note.cpp \ + zcash/prf.cpp \ + zcash/util.cpp \ + zcash/circuit/commitment.tcc \ + zcash/circuit/gadget.tcc \ + zcash/circuit/merkle.tcc \ + zcash/circuit/note.tcc \ + zcash/circuit/prfs.tcc \ + zcash/circuit/utils.tcc libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) $(HARDENED_CXXFLAGS) $(HARDENED_LDFLAGS) -pipe $(SAN_LDFLAGS) -O1 -g -Wstack-protector $(SAN_CXXFLAGS) -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) @@ -572,7 +599,7 @@ libzcashconsensus_la_SOURCES = \ utilstrencodings.cpp if GLIBC_BACK_COMPAT - libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp +libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp endif libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 582ecb577..ff270bd6a 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -15,87 +15,90 @@ EXTRA_DIST += \ test/data/tx394b54bb.hex \ test/data/txcreate1.hex \ test/data/txcreate2.hex \ - test/data/txcreatesign.hex + test/data/txcreatesign.hex \ + test/wallet-utility.py \ + test/data/wallet.dat JSON_TEST_FILES = \ - test/data/script_valid.json \ - test/data/base58_keys_valid.json \ - test/data/base58_encode_decode.json \ - test/data/base58_keys_invalid.json \ - test/data/script_invalid.json \ - test/data/tx_invalid.json \ - test/data/tx_valid.json \ - test/data/sighash.json \ - test/data/merkle_roots.json \ - test/data/merkle_roots_empty.json \ - test/data/merkle_serialization.json \ - test/data/merkle_witness_serialization.json \ - test/data/merkle_path.json \ - test/data/merkle_commitments.json \ - test/data/g1_compressed.json \ - test/data/g2_compressed.json + test/data/script_valid.json \ + test/data/base58_keys_valid.json \ + test/data/base58_encode_decode.json \ + test/data/base58_keys_invalid.json \ + test/data/script_invalid.json \ + test/data/tx_invalid.json \ + test/data/tx_valid.json \ + test/data/sighash.json \ + test/data/merkle_roots.json \ + test/data/merkle_roots_empty.json \ + test/data/merkle_serialization.json \ + test/data/merkle_witness_serialization.json \ + test/data/merkle_path.json \ + test/data/merkle_commitments.json \ + test/data/g1_compressed.json \ + test/data/g2_compressed.json RAW_TEST_FILES = test/data/alertTests.raw GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) BITCOIN_TESTS =\ - test/arith_uint256_tests.cpp \ - test/bignum.h \ - test/addrman_tests.cpp \ - test/alert_tests.cpp \ - test/allocator_tests.cpp \ - test/base32_tests.cpp \ - test/base58_tests.cpp \ - test/base64_tests.cpp \ - test/bip32_tests.cpp \ - test/bloom_tests.cpp \ - test/checkblock_tests.cpp \ - test/Checkpoints_tests.cpp \ - test/coins_tests.cpp \ - test/compress_tests.cpp \ - test/crypto_tests.cpp \ - test/DoS_tests.cpp \ - test/equihash_tests.cpp \ - test/getarg_tests.cpp \ - test/hash_tests.cpp \ - test/key_tests.cpp \ - test/main_tests.cpp \ - test/mempool_tests.cpp \ - test/miner_tests.cpp \ - test/mruset_tests.cpp \ - test/multisig_tests.cpp \ - test/netbase_tests.cpp \ - test/pmt_tests.cpp \ - test/policyestimator_tests.cpp \ - test/pow_tests.cpp \ - test/raii_event_tests.cpp \ - test/reverselock_tests.cpp \ - test/rpc_tests.cpp \ - test/sanity_tests.cpp \ - test/scheduler_tests.cpp \ - test/script_P2SH_tests.cpp \ - test/script_tests.cpp \ - test/scriptnum_tests.cpp \ - test/serialize_tests.cpp \ - test/sighash_tests.cpp \ - test/sigopcount_tests.cpp \ - test/skiplist_tests.cpp \ - test/test_bitcoin.cpp \ - test/test_bitcoin.h \ - test/timedata_tests.cpp \ - test/torcontrol_tests.cpp \ - test/transaction_tests.cpp \ - test/uint256_tests.cpp \ - test/univalue_tests.cpp \ - test/util_tests.cpp \ - test/sha256compress_tests.cpp + test/arith_uint256_tests.cpp \ + test/bignum.h \ + test/addrman_tests.cpp \ + test/alert_tests.cpp \ + test/allocator_tests.cpp \ + test/base32_tests.cpp \ + test/base58_tests.cpp \ + test/base64_tests.cpp \ + test/bip32_tests.cpp \ + test/bloom_tests.cpp \ + test/checkblock_tests.cpp \ + test/Checkpoints_tests.cpp \ + test/coins_tests.cpp \ + test/compress_tests.cpp \ + test/crypto_tests.cpp \ + test/DoS_tests.cpp \ + test/equihash_tests.cpp \ + test/getarg_tests.cpp \ + test/hash_tests.cpp \ + test/key_tests.cpp \ + test/main_tests.cpp \ + test/mempool_tests.cpp \ + test/miner_tests.cpp \ + test/mruset_tests.cpp \ + test/multisig_tests.cpp \ + test/netbase_tests.cpp \ + test/pmt_tests.cpp \ + test/policyestimator_tests.cpp \ + test/pow_tests.cpp \ + test/raii_event_tests.cpp \ + test/reverselock_tests.cpp \ + test/rpc_tests.cpp \ + test/sanity_tests.cpp \ + test/scheduler_tests.cpp \ + test/script_P2SH_tests.cpp \ + test/script_P2PKH_tests.cpp \ + test/script_tests.cpp \ + test/scriptnum_tests.cpp \ + test/serialize_tests.cpp \ + test/sighash_tests.cpp \ + test/sigopcount_tests.cpp \ + test/skiplist_tests.cpp \ + test/test_bitcoin.cpp \ + test/test_bitcoin.h \ + test/timedata_tests.cpp \ + test/torcontrol_tests.cpp \ + test/transaction_tests.cpp \ + test/uint256_tests.cpp \ + test/univalue_tests.cpp \ + test/util_tests.cpp \ + test/sha256compress_tests.cpp if ENABLE_WALLET BITCOIN_TESTS += \ - test/accounting_tests.cpp \ - wallet/test/wallet_tests.cpp \ - test/rpc_wallet_tests.cpp + test/accounting_tests.cpp \ + wallet/test/wallet_tests.cpp \ + test/rpc_wallet_tests.cpp endif test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) @@ -137,6 +140,10 @@ bitcoin_test_clean : FORCE check-local: @echo "Running test/bitcoin-util-test.py..." $(AM_V_at)srcdir=$(srcdir) PYTHONPATH=$(builddir)/test $(srcdir)/test/bitcoin-util-test.py +if ENABLE_WALLET + @echo "Running test/wallet-utility.py..." + $(AM_V_at)srcdir=$(srcdir) PYTHONPATH=$(builddir)/test $(srcdir)/test/wallet-utility.py +endif $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check diff --git a/src/addressindex.h b/src/addressindex.h new file mode 100644 index 000000000..643f06e13 --- /dev/null +++ b/src/addressindex.h @@ -0,0 +1,82 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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. + +#ifndef BITCOIN_ADDRESSINDEX_H +#define BITCOIN_ADDRESSINDEX_H + +#include "uint256.h" +#include "amount.h" + +struct CMempoolAddressDelta +{ + int64_t time; + CAmount amount; + uint256 prevhash; + unsigned int prevout; + + CMempoolAddressDelta(int64_t t, CAmount a, uint256 hash, unsigned int out) { + time = t; + amount = a; + prevhash = hash; + prevout = out; + } + + CMempoolAddressDelta(int64_t t, CAmount a) { + time = t; + amount = a; + prevhash.SetNull(); + prevout = 0; + } +}; + +struct CMempoolAddressDeltaKey +{ + int type; + uint160 addressBytes; + uint256 txhash; + unsigned int index; + int spending; + + CMempoolAddressDeltaKey(int addressType, uint160 addressHash, uint256 hash, unsigned int i, int s) { + type = addressType; + addressBytes = addressHash; + txhash = hash; + index = i; + spending = s; + } + + CMempoolAddressDeltaKey(int addressType, uint160 addressHash) { + type = addressType; + addressBytes = addressHash; + txhash.SetNull(); + index = 0; + spending = 0; + } +}; + +struct CMempoolAddressDeltaKeyCompare +{ + bool operator()(const CMempoolAddressDeltaKey& a, const CMempoolAddressDeltaKey& b) const { + if (a.type == b.type) { + if (a.addressBytes == b.addressBytes) { + if (a.txhash == b.txhash) { + if (a.index == b.index) { + return a.spending < b.spending; + } else { + return a.index < b.index; + } + } else { + return a.txhash < b.txhash; + } + } else { + return a.addressBytes < b.addressBytes; + } + } else { + return a.type < b.type; + } + } +}; + +#endif // BITCOIN_ADDRESSINDEX_H \ No newline at end of file diff --git a/src/base58.cpp b/src/base58.cpp index c9c64bc58..c086cc854 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -275,6 +275,23 @@ CTxDestination CBitcoinAddress::Get() const return CNoDestination(); } +bool CBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type) const +{ + if (!IsValid()) { + return false; + } else if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) { + memcpy(&hashBytes, &vchData[0], 20); + type = 1; + return true; + } else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) { + memcpy(&hashBytes, &vchData[0], 20); + type = 2; + return true; + } + + return false; +} + bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const { if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) diff --git a/src/base58.h b/src/base58.h index 24e7abfc8..73e1c4d94 100644 --- a/src/base58.h +++ b/src/base58.h @@ -163,6 +163,7 @@ public: CTxDestination Get() const; bool GetKeyID(CKeyID &keyID) const; + bool GetIndexKey(uint160& hashBytes, int& type) const; bool IsScript() const; }; diff --git a/src/clientversion.cpp b/src/clientversion.cpp index ae67e678f..9b523e582 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -48,10 +48,11 @@ const std::string CLIENT_NAME("MagicBean"); #include "build.h" #endif -//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ +//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. +#define GIT_ARCHIVE 1 #ifdef GIT_ARCHIVE -#define GIT_COMMIT_ID "$Format:%h$" -#define GIT_COMMIT_DATE "$Format:%cD$" +#define GIT_COMMIT_ID "a86845f3dc" +#define GIT_COMMIT_DATE "Wed, 21 Feb 2018 16:15:11 +0200" #endif #define RENDER_BETA_STRING(num) "-beta" DO_STRINGIZE(num) diff --git a/src/dpowassets b/src/dpowassets index cd68083ae..14bb39912 100755 --- a/src/dpowassets +++ b/src/dpowassets @@ -33,5 +33,3 @@ curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dp curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BNTN\",\"pubkey\":\"$pubkey\"}" curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"PRLPAY\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"USD\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"EUR\",\"pubkey\":\"$pubkey\"}" diff --git a/src/init.cpp b/src/init.cpp index 7268c362c..7027ede54 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -370,7 +370,9 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); #endif strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), 0)); - + strUsage += HelpMessageOpt("-addressindex", strprintf(_("Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u)"), DEFAULT_ADDRESSINDEX)); + strUsage += HelpMessageOpt("-timestampindex", strprintf(_("Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u)"), DEFAULT_TIMESTAMPINDEX)); + strUsage += HelpMessageOpt("-spentindex", strprintf(_("Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u)"), DEFAULT_SPENTINDEX)); strUsage += HelpMessageGroup(_("Connection options:")); strUsage += HelpMessageOpt("-addnode=", _("Add a node to connect to and attempt to keep the connection open")); strUsage += HelpMessageOpt("-banscore=", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), 100)); @@ -1364,18 +1366,34 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } } + // block tree db settings + int dbMaxOpenFiles = GetArg("-dbmaxopenfiles", DEFAULT_DB_MAX_OPEN_FILES); + bool dbCompression = GetBoolArg("-dbcompression", DEFAULT_DB_COMPRESSION); + + LogPrintf("Block index database configuration:\n"); + LogPrintf("* Using %d max open files\n", dbMaxOpenFiles); + LogPrintf("* Compression is %s\n", dbCompression ? "enabled" : "disabled"); + // cache size calculations int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache int64_t nBlockTreeDBCache = nTotalCache / 8; - if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", true)) - nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + + if (GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) || GetBoolArg("-spentindex", DEFAULT_SPENTINDEX)) { + // enable 3/4 of the cache if addressindex and/or spentindex is enabled + nBlockTreeDBCache = nTotalCache * 3 / 4; + } else { + if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) { + nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + } + } nTotalCache -= nBlockTreeDBCache; int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache nTotalCache -= nCoinDBCache; nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache LogPrintf("Cache configuration:\n"); + LogPrintf("* Max cache setting possible %.1fMiB\n", nMaxDbCache); LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for in-memory UTXO set\n", nCoinCacheUsage * (1.0 / 1024 / 1024)); @@ -1396,7 +1414,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) delete pcoinscatcher; delete pblocktree; - pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex, dbCompression, dbMaxOpenFiles); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinscatcher); diff --git a/src/komodo.h b/src/komodo.h index 32fcce047..829d64ec2 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -587,7 +587,7 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr { if ( last_rewind != 0 ) { - KOMODO_REWIND = rewindtarget; + //KOMODO_REWIND = rewindtarget; fprintf(stderr,"%s FORK detected. notarized.%d %s not in this chain! last notarization %d -> rewindtarget.%d\n",ASSETCHAINS_SYMBOL,*notarizedheightp,srchash.ToString().c_str(),sp->NOTARIZED_HEIGHT,rewindtarget); } last_rewind = rewindtarget; diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 4d513ca90..e9a379abc 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -651,7 +651,7 @@ int32_t komodo_block2height(CBlock *block) } if ( height != height2 ) { - fprintf(stderr,"block2height height.%d vs height2.%d, match.%d mismatch.%d\n",height,height2,match,mismatch); + //fprintf(stderr,"block2height height.%d vs height2.%d, match.%d mismatch.%d\n",height,height2,match,mismatch); mismatch++; if ( height2 >= 0 ) height = height2; diff --git a/src/komodo_globals.h b/src/komodo_globals.h index ecd8bfd6a..31ba6c32c 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -46,13 +46,13 @@ struct komodo_state KOMODO_STATES[34]; int COINBASE_MATURITY = _COINBASE_MATURITY;//100; int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND; -int32_t KOMODO_LASTMINED,prevKOMODO_LASTMINED,JUMBLR_PAUSE,ASSETCHAINS_CC; +int32_t KOMODO_LASTMINED,prevKOMODO_LASTMINED,JUMBLR_PAUSE; std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY; uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33]; char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096]; uint16_t ASSETCHAINS_PORT; -uint32_t ASSETCHAIN_INIT; +uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC; uint32_t ASSETCHAINS_MAGIC = 2387029918; uint64_t ASSETCHAINS_ENDSUBSIDY,ASSETCHAINS_REWARD,ASSETCHAINS_HALVING,ASSETCHAINS_DECAY,ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY = 10; diff --git a/src/komodo_notary.h b/src/komodo_notary.h index d0e02999a..ca56ecb61 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -199,7 +199,7 @@ const char *Notaries_elected1[][2] = int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp) { - static uint8_t elected_pubkeys0[64][33],elected_pubkeys1[64][33],did0,did1; + static uint8_t elected_pubkeys0[64][33],elected_pubkeys1[64][33],did0,did1; static int32_t n0,n1; int32_t i,htind,n; uint64_t mask = 0; struct knotary_entry *kp,*tmp; if ( timestamp == 0 && ASSETCHAINS_SYMBOL[0] != 0 ) timestamp = komodo_heightstamp(height); @@ -211,29 +211,30 @@ int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestam { if ( did0 == 0 ) { - n = (int32_t)(sizeof(Notaries_elected0)/sizeof(*Notaries_elected0)); - for (i=0; i= KOMODO_MAXBLOCKS / KOMODO_ELECTION_GAP ) diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index b5d024abb..3f1abbe14 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -27,14 +27,14 @@ void HandleError(const leveldb::Status& status) throw leveldb_error("Unknown database error"); } -static leveldb::Options GetOptions(size_t nCacheSize) +static leveldb::Options GetOptions(size_t nCacheSize, bool compression, int maxOpenFiles) { leveldb::Options options; options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously options.filter_policy = leveldb::NewBloomFilterPolicy(10); - options.compression = leveldb::kNoCompression; - options.max_open_files = 64; + options.compression = compression ? leveldb::kSnappyCompression : leveldb::kNoCompression; + options.max_open_files = maxOpenFiles; if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error // on corruption in later versions. @@ -43,14 +43,14 @@ static leveldb::Options GetOptions(size_t nCacheSize) return options; } -CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe) +CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool compression, int maxOpenFiles) { penv = NULL; readoptions.verify_checksums = true; iteroptions.verify_checksums = true; iteroptions.fill_cache = false; syncoptions.sync = true; - options = GetOptions(nCacheSize); + options = GetOptions(nCacheSize, compression, maxOpenFiles); options.create_if_missing = true; if (fMemory) { penv = leveldb::NewMemEnv(leveldb::Env::Default()); diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h index 639f736a5..2a6e345f1 100644 --- a/src/leveldbwrapper.h +++ b/src/leveldbwrapper.h @@ -86,7 +86,7 @@ private: leveldb::DB* pdb; public: - CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); + CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool compression = false, int maxOpenFiles = 64); ~CLevelDBWrapper(); template diff --git a/src/main.cpp b/src/main.cpp index da117a6e2..1d21289f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,7 +53,9 @@ using namespace std; CCriticalSection cs_main; extern uint8_t NOTARY_PUBKEY33[33]; -extern int32_t KOMODO_LOADINGBLOCKS; +extern int32_t KOMODO_LOADINGBLOCKS,KOMODO_LONGESTCHAIN; +int32_t KOMODO_NEWBLOCKS; +void komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block); BlockMap mapBlockIndex; CChain chainActive; @@ -66,6 +68,9 @@ bool fExperimentalMode = false; bool fImporting = false; bool fReindex = false; bool fTxIndex = false; +bool fAddressIndex = false; +bool fTimestampIndex = false; +bool fSpentIndex = false; bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = true; @@ -105,30 +110,30 @@ const string strMessageMagic = "Komodo Signed Message:\n"; // Internal stuff namespace { - + struct CBlockIndexWorkComparator { bool operator()(CBlockIndex *pa, CBlockIndex *pb) const { // First sort by most total work, ... if (pa->nChainWork > pb->nChainWork) return false; if (pa->nChainWork < pb->nChainWork) return true; - + // ... then by earliest time received, ... if (pa->nSequenceId < pb->nSequenceId) return false; if (pa->nSequenceId > pb->nSequenceId) return true; - + // Use pointer address as tie breaker (should only happen with blocks // loaded from disk, as those all have id 0). if (pa < pb) return false; if (pa > pb) return true; - + // Identical blocks. return false; } }; - + CBlockIndex *pindexBestInvalid; - + /** * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be @@ -138,10 +143,10 @@ namespace { /** Number of nodes with fSyncStarted. */ int nSyncStarted = 0; /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. - * Pruned nodes may have entries where B is missing data. - */ + * Pruned nodes may have entries where B is missing data. + */ multimap mapBlocksUnlinked; - + CCriticalSection cs_LastBlockFile; std::vector vinfoBlockFile; int nLastBlockFile = 0; @@ -150,7 +155,7 @@ namespace { * or if we allocate more file space when we're in prune mode */ bool fCheckForPruning = false; - + /** * Every received block is assigned a unique and increasing identifier, so we * know which one to give priority in case of a fork. @@ -158,14 +163,14 @@ namespace { CCriticalSection cs_nBlockSequenceId; /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ uint32_t nBlockSequenceId = 1; - + /** * Sources of received blocks, saved to be able to send them reject * messages or ban them when processing happens afterwards. Protected by * cs_main. */ map mapBlockSource; - + /** * Filter for transactions that were recently rejected by * AcceptToMemoryPool. These are not rerequested until the chain tip @@ -188,7 +193,7 @@ namespace { */ boost::scoped_ptr recentRejects; uint256 hashRecentRejectsChainTip; - + /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; @@ -198,16 +203,16 @@ namespace { int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; - + /** Number of blocks in flight with validated headers. */ int nQueuedValidatedHeaders = 0; - + /** Number of preferable block download peers. */ int nPreferredDownload = 0; - + /** Dirty block index entries. */ set setDirtyBlockIndex; - + /** Dirty block file entries. */ set setDirtyFileInfo; } // anon namespace @@ -218,306 +223,306 @@ namespace { // namespace { - -struct CBlockReject { - unsigned char chRejectCode; - string strRejectReason; - uint256 hashBlock; -}; - -/** - * Maintain validation-specific state about nodes, protected by cs_main, instead - * by CNode's own locks. This simplifies asynchronous operation, where - * processing of incoming data is done after the ProcessMessage call returns, - * and we're no longer holding the node's locks. - */ -struct CNodeState { - //! The peer's address - CService address; - //! Whether we have a fully established connection. - bool fCurrentlyConnected; - //! Accumulated misbehaviour score for this peer. - int nMisbehavior; - //! Whether this peer should be disconnected and banned (unless whitelisted). - bool fShouldBan; - //! String name of this peer (debugging/logging purposes). - std::string name; - //! List of asynchronously-determined block rejections to notify this peer about. - std::vector rejects; - //! The best known block we know this peer has announced. - CBlockIndex *pindexBestKnownBlock; - //! The hash of the last unknown block this peer has announced. - uint256 hashLastUnknownBlock; - //! The last full block we both have. - CBlockIndex *pindexLastCommonBlock; - //! Whether we've started headers synchronization with this peer. - bool fSyncStarted; - //! Since when we're stalling block download progress (in microseconds), or 0. - int64_t nStallingSince; - list vBlocksInFlight; - int nBlocksInFlight; - int nBlocksInFlightValidHeaders; - //! Whether we consider this a preferred download peer. - bool fPreferredDownload; - - CNodeState() { - fCurrentlyConnected = false; - nMisbehavior = 0; - fShouldBan = false; - pindexBestKnownBlock = NULL; - hashLastUnknownBlock.SetNull(); - pindexLastCommonBlock = NULL; - fSyncStarted = false; - nStallingSince = 0; - nBlocksInFlight = 0; - nBlocksInFlightValidHeaders = 0; - fPreferredDownload = false; - } -}; - -/** Map maintaining per-node state. Requires cs_main. */ -map mapNodeState; - -// Requires cs_main. -CNodeState *State(NodeId pnode) { - map::iterator it = mapNodeState.find(pnode); - if (it == mapNodeState.end()) - return NULL; - return &it->second; -} - -int GetHeight() -{ - LOCK(cs_main); - return chainActive.Height(); -} - -void UpdatePreferredDownload(CNode* node, CNodeState* state) -{ - nPreferredDownload -= state->fPreferredDownload; - - // Whether this node should be marked as a preferred download node. - state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; - - nPreferredDownload += state->fPreferredDownload; -} - -// Returns time at which to timeout block request (nTime in microseconds) -int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) -{ - return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); -} - -void InitializeNode(NodeId nodeid, const CNode *pnode) { - LOCK(cs_main); - CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; - state.name = pnode->addrName; - state.address = pnode->addr; -} - -void FinalizeNode(NodeId nodeid) { - LOCK(cs_main); - CNodeState *state = State(nodeid); - - if (state->fSyncStarted) - nSyncStarted--; - - if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { - AddressCurrentlyConnected(state->address); - } - - BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) - mapBlocksInFlight.erase(entry.hash); - EraseOrphansFor(nodeid); - nPreferredDownload -= state->fPreferredDownload; - - mapNodeState.erase(nodeid); -} -void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) -{ -/* int expired = pool.Expire(GetTime() - age); - if (expired != 0) - LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + struct CBlockReject { + unsigned char chRejectCode; + string strRejectReason; + uint256 hashBlock; + }; - std::vector vNoSpendsRemaining; - pool.TrimToSize(limit, &vNoSpendsRemaining); - BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) - pcoinsTip->Uncache(removed);*/ -} - -// Requires cs_main. -// Returns a bool indicating whether we requested this block. -bool MarkBlockAsReceived(const uint256& hash) { - map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); - if (itInFlight != mapBlocksInFlight.end()) { - CNodeState *state = State(itInFlight->second.first); - nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; - state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; - state->vBlocksInFlight.erase(itInFlight->second.second); - state->nBlocksInFlight--; - state->nStallingSince = 0; - mapBlocksInFlight.erase(itInFlight); - return true; - } - return false; -} - -// Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - // Make sure it's not listed somewhere already. - MarkBlockAsReceived(hash); - - int64_t nNow = GetTimeMicros(); - QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; - nQueuedValidatedHeaders += newentry.fValidatedHeaders; - list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); - state->nBlocksInFlight++; - state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; - mapBlocksInFlight[hash] = std::make_pair(nodeid, it); -} - -/** Check whether the last unknown block a peer advertized is not yet known. */ -void ProcessBlockAvailability(NodeId nodeid) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - if (!state->hashLastUnknownBlock.IsNull()) { - BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); - if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) - { - if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = itOld->second; - state->hashLastUnknownBlock.SetNull(); + /** + * Maintain validation-specific state about nodes, protected by cs_main, instead + * by CNode's own locks. This simplifies asynchronous operation, where + * processing of incoming data is done after the ProcessMessage call returns, + * and we're no longer holding the node's locks. + */ + struct CNodeState { + //! The peer's address + CService address; + //! Whether we have a fully established connection. + bool fCurrentlyConnected; + //! Accumulated misbehaviour score for this peer. + int nMisbehavior; + //! Whether this peer should be disconnected and banned (unless whitelisted). + bool fShouldBan; + //! String name of this peer (debugging/logging purposes). + std::string name; + //! List of asynchronously-determined block rejections to notify this peer about. + std::vector rejects; + //! The best known block we know this peer has announced. + CBlockIndex *pindexBestKnownBlock; + //! The hash of the last unknown block this peer has announced. + uint256 hashLastUnknownBlock; + //! The last full block we both have. + CBlockIndex *pindexLastCommonBlock; + //! Whether we've started headers synchronization with this peer. + bool fSyncStarted; + //! Since when we're stalling block download progress (in microseconds), or 0. + int64_t nStallingSince; + list vBlocksInFlight; + int nBlocksInFlight; + int nBlocksInFlightValidHeaders; + //! Whether we consider this a preferred download peer. + bool fPreferredDownload; + + CNodeState() { + fCurrentlyConnected = false; + nMisbehavior = 0; + fShouldBan = false; + pindexBestKnownBlock = NULL; + hashLastUnknownBlock.SetNull(); + pindexLastCommonBlock = NULL; + fSyncStarted = false; + nStallingSince = 0; + nBlocksInFlight = 0; + nBlocksInFlightValidHeaders = 0; + fPreferredDownload = false; } + }; + + /** Map maintaining per-node state. Requires cs_main. */ + map mapNodeState; + + // Requires cs_main. + CNodeState *State(NodeId pnode) { + map::iterator it = mapNodeState.find(pnode); + if (it == mapNodeState.end()) + return NULL; + return &it->second; } -} - -/** Update tracking information about which blocks a peer is assumed to have. */ -void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - /*ProcessBlockAvailability(nodeid); - - BlockMap::iterator it = mapBlockIndex.find(hash); - if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { - // An actually better block was announced. - if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = it->second; - } else*/ + + int GetHeight() { - // An unknown block was announced; just assume that the latest one is the best one. - state->hashLastUnknownBlock = hash; + LOCK(cs_main); + return chainActive.Height(); } -} - -/** Find the last common ancestor two blocks have. - * Both pa and pb must be non-NULL. */ -CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { - if (pa->nHeight > pb->nHeight) { - pa = pa->GetAncestor(pb->nHeight); - } else if (pb->nHeight > pa->nHeight) { - pb = pb->GetAncestor(pa->nHeight); + + void UpdatePreferredDownload(CNode* node, CNodeState* state) + { + nPreferredDownload -= state->fPreferredDownload; + + // Whether this node should be marked as a preferred download node. + state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; + + nPreferredDownload += state->fPreferredDownload; } - - while (pa != pb && pa && pb) { - pa = pa->pprev; - pb = pb->pprev; + + // Returns time at which to timeout block request (nTime in microseconds) + int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) + { + return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); } - - // Eventually all chain branches meet at the genesis block. - assert(pa == pb); - return pa; -} - -/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has - * at most count entries. */ -void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) { - if (count == 0) - return; - - vBlocks.reserve(vBlocks.size() + count); - CNodeState *state = State(nodeid); - assert(state != NULL); - - // Make sure pindexBestKnownBlock is up to date, we'll need it. - ProcessBlockAvailability(nodeid); - - if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { - // This peer has nothing interesting. - return; + + void InitializeNode(NodeId nodeid, const CNode *pnode) { + LOCK(cs_main); + CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; + state.name = pnode->addrName; + state.address = pnode->addr; } - - if (state->pindexLastCommonBlock == NULL) { - // Bootstrap quickly by guessing a parent of our best tip is the forking point. - // Guessing wrong in either direction is not a problem. - state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; - } - - // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor - // of its current tip anymore. Go back enough to fix that. - state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); - if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) - return; - - std::vector vToFetch; - CBlockIndex *pindexWalk = state->pindexLastCommonBlock; - // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last - // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to - // download that next block if the window were 1 larger. - int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; - int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); - NodeId waitingfor = -1; - while (pindexWalk->nHeight < nMaxHeight) { - // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards - // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive - // as iterating over ~100 CBlockIndex* entries anyway. - int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); - vToFetch.resize(nToFetch); - pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); - vToFetch[nToFetch - 1] = pindexWalk; - for (unsigned int i = nToFetch - 1; i > 0; i--) { - vToFetch[i - 1] = vToFetch[i]->pprev; + + void FinalizeNode(NodeId nodeid) { + LOCK(cs_main); + CNodeState *state = State(nodeid); + + if (state->fSyncStarted) + nSyncStarted--; + + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { + AddressCurrentlyConnected(state->address); } - - // Iterate over those blocks in vToFetch (in forward direction), adding the ones that - // are not yet downloaded and not in flight to vBlocks. In the meantime, update - // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's - // already part of our chain (and therefore don't need it even if pruned). - BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { - if (!pindex->IsValid(BLOCK_VALID_TREE)) { - // We consider the chain that this peer is on invalid. - return; + + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) + mapBlocksInFlight.erase(entry.hash); + EraseOrphansFor(nodeid); + nPreferredDownload -= state->fPreferredDownload; + + mapNodeState.erase(nodeid); + } + + void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) + { + /* int expired = pool.Expire(GetTime() - age); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + std::vector vNoSpendsRemaining; + pool.TrimToSize(limit, &vNoSpendsRemaining); + BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) + pcoinsTip->Uncache(removed);*/ + } + + // Requires cs_main. + // Returns a bool indicating whether we requested this block. + bool MarkBlockAsReceived(const uint256& hash) { + map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); + if (itInFlight != mapBlocksInFlight.end()) { + CNodeState *state = State(itInFlight->second.first); + nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; + state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; + state->vBlocksInFlight.erase(itInFlight->second.second); + state->nBlocksInFlight--; + state->nStallingSince = 0; + mapBlocksInFlight.erase(itInFlight); + return true; + } + return false; + } + + // Requires cs_main. + void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + // Make sure it's not listed somewhere already. + MarkBlockAsReceived(hash); + + int64_t nNow = GetTimeMicros(); + QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; + nQueuedValidatedHeaders += newentry.fValidatedHeaders; + list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); + state->nBlocksInFlight++; + state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; + mapBlocksInFlight[hash] = std::make_pair(nodeid, it); + } + + /** Check whether the last unknown block a peer advertized is not yet known. */ + void ProcessBlockAvailability(NodeId nodeid) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + if (!state->hashLastUnknownBlock.IsNull()) { + BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); + if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) + { + if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = itOld->second; + state->hashLastUnknownBlock.SetNull(); } - if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { - if (pindex->nChainTx) - state->pindexLastCommonBlock = pindex; - } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { - // The block is not already downloaded, and not yet in flight. - if (pindex->nHeight > nWindowEnd) { - // We reached the end of the window. - if (vBlocks.size() == 0 && waitingfor != nodeid) { - // We aren't able to fetch anything, but we would be if the download window was one larger. - nodeStaller = waitingfor; + } + } + + /** Update tracking information about which blocks a peer is assumed to have. */ + void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + /*ProcessBlockAvailability(nodeid); + + BlockMap::iterator it = mapBlockIndex.find(hash); + if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { + // An actually better block was announced. + if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = it->second; + } else*/ + { + // An unknown block was announced; just assume that the latest one is the best one. + state->hashLastUnknownBlock = hash; + } + } + + /** Find the last common ancestor two blocks have. + * Both pa and pb must be non-NULL. */ + CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { + if (pa->nHeight > pb->nHeight) { + pa = pa->GetAncestor(pb->nHeight); + } else if (pb->nHeight > pa->nHeight) { + pb = pb->GetAncestor(pa->nHeight); + } + + while (pa != pb && pa && pb) { + pa = pa->pprev; + pb = pb->pprev; + } + + // Eventually all chain branches meet at the genesis block. + assert(pa == pb); + return pa; + } + + /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has + * at most count entries. */ + void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) { + if (count == 0) + return; + + vBlocks.reserve(vBlocks.size() + count); + CNodeState *state = State(nodeid); + assert(state != NULL); + + // Make sure pindexBestKnownBlock is up to date, we'll need it. + ProcessBlockAvailability(nodeid); + + if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { + // This peer has nothing interesting. + return; + } + + if (state->pindexLastCommonBlock == NULL) { + // Bootstrap quickly by guessing a parent of our best tip is the forking point. + // Guessing wrong in either direction is not a problem. + state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; + } + + // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor + // of its current tip anymore. Go back enough to fix that. + state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); + if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) + return; + + std::vector vToFetch; + CBlockIndex *pindexWalk = state->pindexLastCommonBlock; + // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last + // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to + // download that next block if the window were 1 larger. + int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; + int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); + NodeId waitingfor = -1; + while (pindexWalk->nHeight < nMaxHeight) { + // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards + // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive + // as iterating over ~100 CBlockIndex* entries anyway. + int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); + vToFetch.resize(nToFetch); + pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); + vToFetch[nToFetch - 1] = pindexWalk; + for (unsigned int i = nToFetch - 1; i > 0; i--) { + vToFetch[i - 1] = vToFetch[i]->pprev; + } + + // Iterate over those blocks in vToFetch (in forward direction), adding the ones that + // are not yet downloaded and not in flight to vBlocks. In the meantime, update + // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's + // already part of our chain (and therefore don't need it even if pruned). + BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { + if (!pindex->IsValid(BLOCK_VALID_TREE)) { + // We consider the chain that this peer is on invalid. + return; + } + if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { + if (pindex->nChainTx) + state->pindexLastCommonBlock = pindex; + } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { + // The block is not already downloaded, and not yet in flight. + if (pindex->nHeight > nWindowEnd) { + // We reached the end of the window. + if (vBlocks.size() == 0 && waitingfor != nodeid) { + // We aren't able to fetch anything, but we would be if the download window was one larger. + nodeStaller = waitingfor; + } + return; } - return; + vBlocks.push_back(pindex); + if (vBlocks.size() == count) { + return; + } + } else if (waitingfor == -1) { + // This is the first already-in-flight block. + waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; } - vBlocks.push_back(pindex); - if (vBlocks.size() == count) { - return; - } - } else if (waitingfor == -1) { - // This is the first already-in-flight block. - waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; } } } -} - + } // anon namespace bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { @@ -589,7 +594,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return false; - + // Ignore big transactions, to avoid a // send-big-orphans memory exhaustion attack. If a peer has a legitimate // large transaction with a missing parent then we assume @@ -603,12 +608,12 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; } - + mapOrphanTransactions[hash].tx = tx; mapOrphanTransactions[hash].fromPeer = peer; BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); + LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); return true; @@ -658,8 +663,8 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE map::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); - EraseOrphanTx(it->first); - ++nEvicted; + EraseOrphanTx(it->first); + ++nEvicted; } return nEvicted; } @@ -668,7 +673,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) { bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); - + if (isOverwinter) { // Overwinter standard rules apply if (tx.nVersion > CTransaction::OVERWINTER_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::OVERWINTER_MIN_CURRENT_VERSION) { @@ -682,7 +687,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) return false; } } - + BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed @@ -701,7 +706,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) return false; } } - + unsigned int v=0,nDataOut = 0; txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) @@ -727,13 +732,13 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) } v++; } - + // only one OP_RETURN txout is permitted if (nDataOut > 1) { reason = "multi-op-return"; return false; } - + return true; } @@ -770,7 +775,7 @@ bool IsExpiredTx(const CTransaction &tx, int nBlockHeight) bool CheckFinalTx(const CTransaction &tx, int flags) { AssertLockHeld(cs_main); - + // By convention a negative value for flags indicates that the // current network-enforced consensus rules should be used. In // a future soft-fork scenario that would mean checking which @@ -778,7 +783,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // appropriate flags. At the present time no soft-forks are // scheduled, so no flags are set. flags = std::max(flags, 0); - + // CheckFinalTx() uses chainActive.Height()+1 to evaluate // nLockTime because when IsFinalTx() is called within // CBlock::AcceptBlock(), the height of the block *being* @@ -786,15 +791,15 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // transaction can be part of the *next* block, we need to call // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; - + // Timestamps on the other hand don't get any special treatment, // because we can't know what timestamp the next block will have, // and there aren't timestamp applications where it matters. // However this changes once median past time-locks are enforced: const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) - ? chainActive.Tip()->GetMedianTimePast() - : GetAdjustedTime(); - + ? chainActive.Tip()->GetMedianTimePast() + : GetAdjustedTime(); + return IsFinalTx(tx, nBlockHeight, nBlockTime); } @@ -811,11 +816,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, { if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally - + for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); - + vector > vSolutions; txnouttype whichType; // get the scriptPubKey corresponding to this input: @@ -825,7 +830,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); if (nArgsExpected < 0) return false; - + // Transactions with extra stuff in their scriptSigs are // non-standard. Note that this EvalScript() call will // be quick, because if there are any operations @@ -835,7 +840,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, vector > stack; if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), consensusBranchId)) return false; - + if (whichType == TX_SCRIPTHASH) { if (stack.empty()) @@ -858,11 +863,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, return (sigops <= MAX_P2SH_SIGOPS); } } - + if (stack.size() != (unsigned int)nArgsExpected) return false; } - + return true; } @@ -884,7 +889,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in { if (tx.IsCoinBase()) return 0; - + unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -897,7 +902,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in /** * Check a transaction contextually against a set of consensus rules valid at a given block height. - * + * * Notes: * 1. AcceptToMemoryPool calls CheckTransaction and this function. * 2. ProcessNewBlock calls AcceptBlock, which calls CheckBlock (which calls CheckTransaction) @@ -907,39 +912,39 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, { bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); bool isSprout = !isOverwinter; - + // If Sprout rules apply, reject transactions which are intended for Overwinter and beyond if (isSprout && tx.fOverwintered) { return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter is not active yet"), REJECT_INVALID, "tx-overwinter-not-active"); } - + // If Overwinter rules apply: if (isOverwinter) { // Reject transactions with valid version but missing overwinter flag if (tx.nVersion >= OVERWINTER_MIN_TX_VERSION && !tx.fOverwintered) { return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter flag must be set"), - REJECT_INVALID, "tx-overwinter-flag-not-set"); + REJECT_INVALID, "tx-overwinter-flag-not-set"); } - + // Reject transactions with invalid version if (tx.fOverwintered && tx.nVersion > OVERWINTER_MAX_TX_VERSION ) { return state.DoS(100, error("CheckTransaction(): overwinter version too high"), - REJECT_INVALID, "bad-tx-overwinter-version-too-high"); + REJECT_INVALID, "bad-tx-overwinter-version-too-high"); } - + // Reject transactions intended for Sprout if (!tx.fOverwintered) { return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"), - REJECT_INVALID, "tx-overwinter-active"); + REJECT_INVALID, "tx-overwinter-active"); } - + // Check that all transactions are unexpired if (IsExpiredTx(tx, nHeight)) { return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired"); } } - + if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) { auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus()); // Empty output script. @@ -949,11 +954,11 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); } catch (std::logic_error ex) { return state.DoS(100, error("CheckTransaction(): error computing signature hash"), - REJECT_INVALID, "error-computing-signature-hash"); + REJECT_INVALID, "error-computing-signature-hash"); } - + BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); - + // We rely on libsodium to check that the signature is canonical. // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 if (crypto_sign_verify_detached(&tx.joinSplitSig[0], @@ -961,7 +966,7 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, tx.joinSplitPubKey.begin() ) != 0) { return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"), - REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); + REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); } } return true; @@ -987,11 +992,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, } } } - // Don't count coinbase transactions because mining skews the count + // Don't count coinbase transactions because mining skews the count if (!tx.IsCoinBase()) { transactionsValidated.increment(); } - + if (!CheckTransactionWithoutProofVerification(tx, state)) { return false; } else { @@ -999,7 +1004,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) { return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"), - REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); + REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); } } return true; @@ -1009,7 +1014,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context - + /** * Previously: * 1. The consensus rule below was: @@ -1039,18 +1044,18 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio else if (tx.fOverwintered) { if (tx.nVersion < OVERWINTER_MIN_TX_VERSION) { return state.DoS(100, error("CheckTransaction(): overwinter version too low"), - REJECT_INVALID, "bad-tx-overwinter-version-too-low"); + REJECT_INVALID, "bad-tx-overwinter-version-too-low"); } if (tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) { return state.DoS(100, error("CheckTransaction(): unknown tx version group id"), - REJECT_INVALID, "bad-tx-version-group-id"); + REJECT_INVALID, "bad-tx-version-group-id"); } if (tx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD) { return state.DoS(100, error("CheckTransaction(): expiry height is too high"), - REJECT_INVALID, "bad-tx-expiry-height-too-high"); + REJECT_INVALID, "bad-tx-expiry-height-too-high"); } } - + // Transactions can contain empty `vin` and `vout` so long as // `vjoinsplit` is non-empty. if (tx.vin.empty() && tx.vjoinsplit.empty()) @@ -1059,13 +1064,13 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio if (tx.vout.empty() && tx.vjoinsplit.empty()) return state.DoS(10, error("CheckTransaction(): vout empty"), REJECT_INVALID, "bad-txns-vout-empty"); - + // Size limits BOOST_STATIC_ASSERT(MAX_BLOCK_SIZE > MAX_TX_SIZE); // sanity if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE) return state.DoS(100, error("CheckTransaction(): size limits failed"), REJECT_INVALID, "bad-txns-oversize"); - + // Check for negative or overflow output values CAmount nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) @@ -1083,7 +1088,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } - + // Ensure that joinsplit values are well-formed BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) { @@ -1091,34 +1096,34 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old negative"), REJECT_INVALID, "bad-txns-vpub_old-negative"); } - + if (joinsplit.vpub_new < 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new negative"), REJECT_INVALID, "bad-txns-vpub_new-negative"); } - + if (joinsplit.vpub_old > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old too high"), REJECT_INVALID, "bad-txns-vpub_old-toolarge"); } - + if (joinsplit.vpub_new > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new too high"), REJECT_INVALID, "bad-txns-vpub_new-toolarge"); } - + if (joinsplit.vpub_new != 0 && joinsplit.vpub_old != 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new and joinsplit.vpub_old both nonzero"), REJECT_INVALID, "bad-txns-vpubs-both-nonzero"); } - + nValueOut += joinsplit.vpub_old; if (!MoneyRange(nValueOut)) { return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } } - + // Ensure input values do not exceed MAX_MONEY // We have not resolved the txin values at this stage, // but we do know what the joinsplits claim to add @@ -1128,15 +1133,15 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio for (std::vector::const_iterator it(tx.vjoinsplit.begin()); it != tx.vjoinsplit.end(); ++it) { nValueIn += it->vpub_new; - + if (!MoneyRange(it->vpub_new) || !MoneyRange(nValueIn)) { return state.DoS(100, error("CheckTransaction(): txin total out of range"), REJECT_INVALID, "bad-txns-txintotal-toolarge"); } } } - - + + // Check for duplicate inputs set vInOutPoints; BOOST_FOREACH(const CTxIn& txin, tx.vin) @@ -1146,7 +1151,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } - + // Check for duplicate joinsplit nullifiers in this transaction set vJoinSplitNullifiers; BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) @@ -1155,19 +1160,19 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio { if (vJoinSplitNullifiers.count(nf)) return state.DoS(100, error("CheckTransaction(): duplicate nullifiers"), - REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate"); - + REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate"); + vJoinSplitNullifiers.insert(nf); } } - + if (tx.IsCoinBase()) { // There should be no joinsplits in a coinbase transaction if (tx.vjoinsplit.size() > 0) return state.DoS(100, error("CheckTransaction(): coinbase has joinsplits"), REJECT_INVALID, "bad-cb-has-joinsplits"); - + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, error("CheckTransaction(): coinbase script size"), REJECT_INVALID, "bad-cb-length"); @@ -1175,11 +1180,11 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio else { BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction(): prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); + if (txin.prevout.IsNull()) + return state.DoS(10, error("CheckTransaction(): prevout is null"), + REJECT_INVALID, "bad-txns-prevout-null"); } - + return true; } @@ -1195,9 +1200,9 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (dPriorityDelta > 0 || nFeeDelta > 0) return 0; } - + CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); - + if (fAllowFree) { // There is a free transaction area in blocks created by most miners, @@ -1207,7 +1212,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) nMinFee = 0; } - + if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return nMinFee; @@ -1241,11 +1246,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return error("AcceptToMemoryPool: komodo_validate_interest failed"); } if (!CheckTransaction(tx, state, verifier)) + { + return error("AcceptToMemoryPool: CheckTransaction failed"); - + } // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. - if (!ContextualCheckTransaction(tx, state, nextBlockHeight, 10)) { + if (!ContextualCheckTransaction(tx, state, nextBlockHeight, 10)) + { return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); } @@ -1305,7 +1313,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } } } - + { CCoinsView dummy; CCoinsViewCache view(&dummy); @@ -1376,7 +1384,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa fprintf(stderr,"accept failure.4\n"); return state.DoS(0, error("AcceptToMemoryPool: too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS),REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); } - + CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); @@ -1408,7 +1416,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount txMinFee = GetMinRelayFee(tx, nSize, true); if (fLimitFree && nFees < txMinFee) { - fprintf(stderr,"accept failure.5\n"); + //fprintf(stderr,"accept failure.5\n"); return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",hash.ToString(), nFees, txMinFee),REJECT_INSUFFICIENTFEE, "insufficient fee"); } } @@ -1479,6 +1487,16 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if ( komodo_is_notarytx(tx) == 0 ) KOMODO_ON_DEMAND++; pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); + + // Add memory address index + if (fAddressIndex) { + pool.addAddressIndex(entry, view); + } + + // Add memory spent index + if (fSpentIndex) { + pool.addSpentIndex(entry, view); + } } SyncWithWallets(tx, NULL); @@ -1486,18 +1504,67 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return true; } +bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes) +{ + if (!fTimestampIndex) + return error("Timestamp index not enabled"); + + if (!pblocktree->ReadTimestampIndex(high, low, fActiveOnly, hashes)) + return error("Unable to get hashes for timestamps"); + + return true; +} + +bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) +{ + if (!fSpentIndex) + return false; + + if (mempool.getSpentIndex(key, value)) + return true; + + if (!pblocktree->ReadSpentIndex(key, value)) + return false; + + return true; +} + +bool GetAddressIndex(uint160 addressHash, int type, + std::vector > &addressIndex, int start, int end) +{ + if (!fAddressIndex) + return error("address index not enabled"); + + if (!pblocktree->ReadAddressIndex(addressHash, type, addressIndex, start, end)) + return error("unable to get txids for address"); + + return true; +} + +bool GetAddressUnspent(uint160 addressHash, int type, + std::vector > &unspentOutputs) +{ + if (!fAddressIndex) + return error("address index not enabled"); + + if (!pblocktree->ReadAddressUnspentIndex(addressHash, type, unspentOutputs)) + return error("unable to get txids for address"); + + return true; +} + /** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; - + LOCK(cs_main); - + if (mempool.lookup(hash, txOut)) { return true; } - + if (fTxIndex) { CDiskTxPos postx; if (pblocktree->ReadTxIndex(hash, postx)) { @@ -1518,7 +1585,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock return true; } } - + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { @@ -1530,7 +1597,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (nHeight > 0) pindexSlow = chainActive[nHeight]; } - + if (pindexSlow) { CBlock block; if (ReadBlockFromDisk(block, pindexSlow)) { @@ -1543,23 +1610,23 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock } } } - + return false; } /*char *komodo_getspendscript(uint256 hash,int32_t n) -{ - CTransaction tx; uint256 hashBlock; - if ( !GetTransaction(hash,tx,hashBlock,true) ) - { - printf("null GetTransaction\n"); - return(0); - } - if ( n >= 0 && n < tx.vout.size() ) - return((char *)tx.vout[n].scriptPubKey.ToString().c_str()); - else printf("getspendscript illegal n.%d\n",n); - return(0); -}*/ + { + CTransaction tx; uint256 hashBlock; + if ( !GetTransaction(hash,tx,hashBlock,true) ) + { + printf("null GetTransaction\n"); + return(0); + } + if ( n >= 0 && n < tx.vout.size() ) + return((char *)tx.vout[n].scriptPubKey.ToString().c_str()); + else printf("getspendscript illegal n.%d\n",n); + return(0); + }*/ ////////////////////////////////////////////////////////////////////////////// @@ -1573,18 +1640,18 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) return error("WriteBlockToDisk: OpenBlockFile failed"); - + // Write index header unsigned int nSize = fileout.GetSerializeSize(block); fileout << FLATDATA(messageStart) << nSize; - + // Write block long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) return error("WriteBlockToDisk: ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << block; - + return true; } @@ -1592,7 +1659,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) { uint8_t pubkey33[33]; block.SetNull(); - + // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) @@ -1600,7 +1667,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) //fprintf(stderr,"readblockfromdisk err A\n"); return false;//error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); } - + // Read block try { filein >> block; @@ -1614,7 +1681,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus()))) { int32_t i; for (i=0; i<33; i++) - printf("%02x",pubkey33[i]); + fprintf(stderr,"%02x",pubkey33[i]); fprintf(stderr," warning unexpected diff at ht.%d\n",height); return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); @@ -1630,7 +1697,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return false; if (block.GetHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", - pindex->ToString(), pindex->GetBlockPos().ToString()); + pindex->ToString(), pindex->GetBlockPos().ToString()); return true; } @@ -1695,27 +1762,27 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) return(nSubsidy); } else return(0); } -/* - // Mining slow start - // The subsidy is ramped up linearly, skipping the middle payout of - // MAX_SUBSIDY/2 to keep the monetary curve consistent with no slow start. - if (nHeight < consensusParams.nSubsidySlowStartInterval / 2) { - nSubsidy /= consensusParams.nSubsidySlowStartInterval; - nSubsidy *= nHeight; - return nSubsidy; - } else if (nHeight < consensusParams.nSubsidySlowStartInterval) { - nSubsidy /= consensusParams.nSubsidySlowStartInterval; - nSubsidy *= (nHeight+1); - return nSubsidy; - } - - assert(nHeight > consensusParams.SubsidySlowStartShift()); - int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ + /* + // Mining slow start + // The subsidy is ramped up linearly, skipping the middle payout of + // MAX_SUBSIDY/2 to keep the monetary curve consistent with no slow start. + if (nHeight < consensusParams.nSubsidySlowStartInterval / 2) { + nSubsidy /= consensusParams.nSubsidySlowStartInterval; + nSubsidy *= nHeight; + return nSubsidy; + } else if (nHeight < consensusParams.nSubsidySlowStartInterval) { + nSubsidy /= consensusParams.nSubsidySlowStartInterval; + nSubsidy *= (nHeight+1); + return nSubsidy; + } + + assert(nHeight > consensusParams.SubsidySlowStartShift()); + int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ // Force block reward to zero when right shift is undefined. //int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; //if (halvings >= 64) // return 0; - + // Subsidy is cut in half every 840,000 blocks which will occur approximately every 4 years. //nSubsidy >>= halvings; return nSubsidy; @@ -1747,8 +1814,8 @@ bool IsInitialBlockDownload() else if ( pindexBestHeader != 0 && pindexBestHeader->nHeight > ptr->nHeight ) ptr = pindexBestHeader; //if ( ASSETCHAINS_SYMBOL[0] == 0 ) - state = ((chainActive.Height() < ptr->nHeight - 24*60) || - ptr->GetBlockTime() < (GetTime() - chainParams.MaxTipAge())); + state = ((chainActive.Height() < ptr->nHeight - 24*60) || + ptr->GetBlockTime() < (GetTime() - chainParams.MaxTipAge())); //else state = (chainActive.Height() < ptr->nHeight - 24*60); //fprintf(stderr,"state.%d ht.%d vs %d, t.%u %u\n",state,(int32_t)chainActive.Height(),(uint32_t)ptr->nHeight,(int32_t)ptr->GetBlockTime(),(uint32_t)(GetTime() - chainParams.MaxTipAge())); if (!state) @@ -1769,25 +1836,25 @@ void CheckForkWarningConditions() // (we assume we don't get stuck on a fork before the last checkpoint) if (IsInitialBlockDownload()) return; - + // If our best fork is no longer within 288 blocks (+/- 12 hours if no one mines it) // of our head, drop it if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 288) pindexBestForkTip = NULL; - + if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) { if (!fLargeWorkForkFound && pindexBestForkBase) { std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); + pindexBestForkBase->phashBlock->ToString() + std::string("'"); CAlert::Notify(warning, true); } if (pindexBestForkTip && pindexBestForkBase) { LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); + pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), + pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); fLargeWorkForkFound = true; } else @@ -1819,7 +1886,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) break; pfork = pfork->pprev; } - + // We define a condition where we should warn the user about as a fork of at least 7 blocks // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network @@ -1828,13 +1895,13 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) // We define it this way because it allows us to only store the highest fork tip (+ base) which meets // the 7-block condition and from this always have the most-likely-to-cause-warning fork if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && - pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && - chainActive.Height() - pindexNewForkTip->nHeight < 72) + pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && + chainActive.Height() - pindexNewForkTip->nHeight < 72) { pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; } - + CheckForkWarningConditions(); } @@ -1843,13 +1910,13 @@ void Misbehaving(NodeId pnode, int howmuch) { if (howmuch == 0) return; - + CNodeState *state = State(pnode); if (state == NULL) return; - + state->nMisbehavior += howmuch; - int banscore = GetArg("-banscore", 100); + int banscore = GetArg("-banscore", 101); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); @@ -1862,16 +1929,16 @@ void static InvalidChainFound(CBlockIndex* pindexNew) { if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; - + LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, - pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, - log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", - pindexNew->GetBlockTime())); + pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, + log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", + pindexNew->GetBlockTime())); CBlockIndex *tip = chainActive.Tip(); assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, - tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); + tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); CheckForkWarningConditions(); } @@ -1902,7 +1969,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); unsigned nPos = txin.prevout.n; - + if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull()) assert(false); // mark an outpoint spent, and construct undo information @@ -1946,17 +2013,17 @@ int GetSpendHeight(const CCoinsViewCache& inputs) } namespace Consensus { -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams) -{ + bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams) + { // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); - + // are the JoinSplit's requirements met? if (!inputs.HaveJoinSplitRequirements(tx)) return state.Invalid(error("CheckInputs(): %s JoinSplit requirements not met", tx.GetHash().ToString())); - + CAmount nValueIn = 0; CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1964,26 +2031,26 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins const COutPoint &prevout = tx.vin[i].prevout; const CCoins *coins = inputs.AccessCoins(prevout.hash); assert(coins); - + if (coins->IsCoinBase()) { // Ensure that coinbases are matured if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); } - + // Ensure that coinbases cannot be spent to transparent outputs // Disabled on regtest if (fCoinbaseEnforcedProtectionEnabled && consensusParams.fCoinbaseMustBeProtected && !tx.vout.empty()) { return state.Invalid( - error("CheckInputs(): tried to spend coinbase with transparent outputs"), - REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); + error("CheckInputs(): tried to spend coinbase with transparent outputs"), + REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); } } - + // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; #ifdef KOMODO_ENABLE_INTEREST @@ -1994,7 +2061,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins int64_t interest; int32_t txheight; uint32_t locktime; if ( (interest= komodo_accrued_interest(&txheight,&locktime,prevout.hash,prevout.n,0,coins->vout[prevout.n].nValue,(int32_t)nSpendHeight-1)) != 0 ) { -//fprintf(stderr,"checkResult %.8f += val %.8f interest %.8f ht.%d lock.%u tip.%u\n",(double)nValueIn/COIN,(double)coins->vout[prevout.n].nValue/COIN,(double)interest/COIN,txheight,locktime,chainActive.Tip()->nTime); + //fprintf(stderr,"checkResult %.8f += val %.8f interest %.8f ht.%d lock.%u tip.%u\n",(double)nValueIn/COIN,(double)coins->vout[prevout.n].nValue/COIN,(double)interest/COIN,txheight,locktime,chainActive.Tip()->nTime); nValueIn += interest; } } @@ -2003,14 +2070,14 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs(): txin values out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - + } - + nValueIn += tx.GetJoinSplitValueIn(); if (!MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs(): vpub_old values out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - + if (nValueIn < tx.GetValueOut()) { fprintf(stderr,"spentheight.%d valuein %s vs %s error\n",nSpendHeight,FormatMoney(nValueIn).c_str(), FormatMoney(tx.GetValueOut()).c_str()); @@ -2026,35 +2093,35 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs(): nFees out of range"), REJECT_INVALID, "bad-txns-fee-outofrange"); - return true; -} + return true; + } }// namespace Consensus bool ContextualCheckInputs( - const CTransaction& tx, - CValidationState &state, - const CCoinsViewCache &inputs, - bool fScriptChecks, - unsigned int flags, - bool cacheStore, - PrecomputedTransactionData& txdata, - const Consensus::Params& consensusParams, - uint32_t consensusBranchId, - std::vector *pvChecks) + const CTransaction& tx, + CValidationState &state, + const CCoinsViewCache &inputs, + bool fScriptChecks, + unsigned int flags, + bool cacheStore, + PrecomputedTransactionData& txdata, + const Consensus::Params& consensusParams, + uint32_t consensusBranchId, + std::vector *pvChecks) { if (!tx.IsCoinBase()) { if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) { return false; } - + if (pvChecks) pvChecks->reserve(tx.vin.size()); - + // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. - + // Skip ECDSA signature verification when connecting blocks // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. @@ -2063,7 +2130,7 @@ bool ContextualCheckInputs( const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = inputs.AccessCoins(prevout.hash); assert(coins); - + // Verify signature CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, &txdata); if (pvChecks) { @@ -2078,7 +2145,7 @@ bool ContextualCheckInputs( // avoid splitting the network between upgraded and // non-upgraded nodes. CScriptCheck check2(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, &txdata); + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } @@ -2094,123 +2161,123 @@ bool ContextualCheckInputs( } } } - + return true; } /*bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector *pvChecks) -{ - if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) { - fprintf(stderr,"ContextualCheckInputs failure.0\n"); - return false; - } - - if (!tx.IsCoinBase()) - { - // While checking, GetBestBlock() refers to the parent block. - // This is also true for mempool checks. - CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; - int nSpendHeight = pindexPrev->nHeight + 1; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins *coins = inputs.AccessCoins(prevout.hash); - // Assertion is okay because NonContextualCheckInputs ensures the inputs - // are available. - assert(coins); - - // If prev is coinbase, check that it's matured - if (coins->IsCoinBase()) { - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - COINBASE_MATURITY = _COINBASE_MATURITY; - if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { - fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); - - return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); - } - } - } - } - - return true; -}*/ + { + if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) { + fprintf(stderr,"ContextualCheckInputs failure.0\n"); + return false; + } + + if (!tx.IsCoinBase()) + { + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + int nSpendHeight = pindexPrev->nHeight + 1; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins *coins = inputs.AccessCoins(prevout.hash); + // Assertion is okay because NonContextualCheckInputs ensures the inputs + // are available. + assert(coins); + + // If prev is coinbase, check that it's matured + if (coins->IsCoinBase()) { + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + COINBASE_MATURITY = _COINBASE_MATURITY; + if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { + fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); + + return state.Invalid( + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + } + } + } + } + + return true; + }*/ namespace { - -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) -{ - // Open history file to append - CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: OpenUndoFile failed", __func__); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(blockundo); - fileout << FLATDATA(messageStart) << nSize; - - // Write undo data - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("%s: ftell failed", __func__); - pos.nPos = (unsigned int)fileOutPos; - fileout << blockundo; - - // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - fileout << hasher.GetHash(); - - return true; -} - -bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) -{ - // Open history file to read - CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - - // Read block - uint256 hashChecksum; - try { - filein >> blockundo; - filein >> hashChecksum; + + bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) + { + // Open history file to append + CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: OpenUndoFile failed", __func__); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(blockundo); + fileout << FLATDATA(messageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("%s: ftell failed", __func__); + pos.nPos = (unsigned int)fileOutPos; + fileout << blockundo; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + fileout << hasher.GetHash(); + + return true; } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + + bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) + { + // Open history file to read + CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + + // Read block + uint256 hashChecksum; + try { + filein >> blockundo; + filein >> hashChecksum; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + if (hashChecksum != hasher.GetHash()) + return error("%s: Checksum mismatch", __func__); + + return true; } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - if (hashChecksum != hasher.GetHash()) - return error("%s: Checksum mismatch", __func__); - - return true; -} - -/** Abort with a message */ -bool AbortNode(const std::string& strMessage, const std::string& userMessage="") -{ - strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; -} - -bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") -{ - AbortNode(strMessage, userMessage); - return state.Error(strMessage); -} - + + /** Abort with a message */ + bool AbortNode(const std::string& strMessage, const std::string& userMessage="") + { + strMiscWarning = strMessage; + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox( + userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + + bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") + { + AbortNode(strMessage, userMessage); + return state.Error(strMessage); + } + } // anon namespace /** @@ -2223,7 +2290,7 @@ bool AbortNode(CValidationState& state, const std::string& strMessage, const std static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out) { bool fClean = true; - + CCoinsModifier coins = view.ModifyCoins(out.hash); if (undo.nHeight != 0) { // undo data contains height: this is the last output of the prevout tx being spent @@ -2242,17 +2309,17 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO if (coins->vout.size() < out.n+1) coins->vout.resize(out.n+1); coins->vout[out.n] = undo.txout; - + return fClean; } bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); - + if (pfClean) *pfClean = false; - + bool fClean = true; komodo_disconnect(pindex,block); CBlockUndo blockUndo; @@ -2261,41 +2328,76 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex return error("DisconnectBlock(): no undo data available"); if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) return error("DisconnectBlock(): failure reading undo data"); - + if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) return error("DisconnectBlock(): block and undo data inconsistent"); + std::vector > addressIndex; + std::vector > addressUnspentIndex; + std::vector > spentIndex; + // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = block.vtx[i]; uint256 hash = tx.GetHash(); + if (fAddressIndex) { + + for (unsigned int k = tx.vout.size(); k-- > 0;) { + const CTxOut &out = tx.vout[k]; + + if (out.scriptPubKey.IsPayToScriptHash()) { + vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); + + // undo receiving activity + addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); + + // undo unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), hash, k), CAddressUnspentValue())); + + } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + + // undo receiving activity + addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); + + // undo unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), hash, k), CAddressUnspentValue())); + + } else { + continue; + } + + } + + } + // Check that all outputs are available and match the outputs in the block itself // exactly. { - CCoinsModifier outs = view.ModifyCoins(hash); - outs->ClearUnspendable(); - - CCoins outsBlock(tx, pindex->nHeight); - // The CCoins serialization does not serialize negative numbers. - // No network rules currently depend on the version here, so an inconsistency is harmless - // but it must be corrected before txout nversion ever influences a network rule. - if (outsBlock.nVersion < 0) - outs->nVersion = outsBlock.nVersion; - if (*outs != outsBlock) - fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); - - // remove outputs - outs->Clear(); + CCoinsModifier outs = view.ModifyCoins(hash); + outs->ClearUnspendable(); + + CCoins outsBlock(tx, pindex->nHeight); + // The CCoins serialization does not serialize negative numbers. + // No network rules currently depend on the version here, so an inconsistency is harmless + // but it must be corrected before txout nversion ever influences a network rule. + if (outsBlock.nVersion < 0) + outs->nVersion = outsBlock.nVersion; + if (*outs != outsBlock) + fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); + + // remove outputs + outs->Clear(); } - + // unspend nullifiers BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { view.SetNullifier(nf, false); } } - + // restore inputs if (i > 0) { // not coinbases const CTxUndo &txundo = blockUndo.vtxundo[i-1]; @@ -2306,30 +2408,71 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex const CTxInUndo &undo = txundo.vprevout[j]; if (!ApplyTxInUndo(undo, view, out)) fClean = false; + + const CTxIn input = tx.vin[j]; + + if (fSpentIndex) { + // undo and delete the spent index + spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue())); + } + + if (fAddressIndex) { + const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); + if (prevout.scriptPubKey.IsPayToScriptHash()) { + vector hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22); + + // undo spending activity + addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); + + // restore unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); + + + } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); + + // undo spending activity + addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); + + // restore unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); + + } else { + continue; + } + } } } } - + // set the old best anchor back view.PopAnchor(blockUndo.old_tree_root); - + // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); - + if (pfClean) { *pfClean = fClean; return true; } + if (fAddressIndex) { + if (!pblocktree->EraseAddressIndex(addressIndex)) { + return AbortNode(state, "Failed to delete address index"); + } + if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) { + return AbortNode(state, "Failed to write address unspent index"); + } + } return fClean; } void static FlushBlockFile(bool fFinalize = false) { LOCK(cs_LastBlockFile); - + CDiskBlockPos posOld(nLastBlockFile, 0); - + FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) @@ -2337,7 +2480,7 @@ void static FlushBlockFile(bool fFinalize = false) FileCommit(fileOld); fclose(fileOld); } - + fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) @@ -2365,20 +2508,20 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const int64_t nPowTargetSpacing) { if (bestHeader == NULL || initialDownloadCheck()) return; - + static int64_t lastAlertTime = 0; int64_t now = GetAdjustedTime(); if (lastAlertTime > now-60*60*24) return; // Alert at most once per day - + const int SPAN_HOURS=4; const int SPAN_SECONDS=SPAN_HOURS*60*60; int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; - + boost::math::poisson_distribution poisson(BLOCKS_EXPECTED); - + std::string strWarning; int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; - + LOCK(cs); const CBlockIndex* i = bestHeader; int nBlocks = 0; @@ -2387,17 +2530,17 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const i = i->pprev; if (i == NULL) return; // Ran out of chain, we must not be fully synced } - + // How likely is it to find that many by chance? double p = boost::math::pdf(poisson, nBlocks); - + LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS); LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p); - + // Aim for one false-positive about every fifty years of normal running: const int FIFTY_YEARS = 50*365*24*60*60; double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); - + if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED) { // Many fewer blocks than expected: alert! @@ -2424,9 +2567,10 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck,bool fCheckPOW) { const CChainParams& chainparams = Params(); + //fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->nHeight); AssertLockHeld(cs_main); bool fExpensiveChecks = true; @@ -2439,15 +2583,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); - + // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in - if (!CheckBlock(pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck)) + if (!CheckBlock(pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck)) //!fJustCheck, !fJustCheck)) return false; - + // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); assert(hashPrevBlock == view.GetBestBlock()); - + // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) { @@ -2461,7 +2605,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } return true; } - + bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); //if ( KOMODO_TESTNET_EXPIRATION != 0 && pindex->nHeight > KOMODO_TESTNET_EXPIRATION ) // "testnet" // return(false); @@ -2473,15 +2617,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); } - + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; - + // DERSIG (BIP66) is also always enforced, but does not have a flag. - + CBlockUndo blockundo; - + CCheckQueueControl control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - + int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; @@ -2491,7 +2635,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vPos; vPos.reserve(block.vtx.size()); blockundo.vtxundo.reserve(block.vtx.size() - 1); - + std::vector > addressIndex; + std::vector > addressUnspentIndex; + std::vector > spentIndex; // Construct the incremental merkle tree at the current // block position, auto old_tree_root = view.GetBestAnchor(); @@ -2503,38 +2649,76 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // This should never fail: we should always be able to get the root // that is on the tip of our chain assert(view.GetAnchorAt(old_tree_root, tree)); - + { // Consistency check: the root of the tree we're given should // match what we asked for. assert(tree.root() == old_tree_root); } - + // Grab the consensus branch ID for the block's height auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, Params().GetConsensus()); - + std::vector txdata; txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; + const uint256 txhash = tx.GetHash(); nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); -//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); + //fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); - + // are the JoinSplit's requirements met? if (!view.HaveJoinSplitRequirements(tx)) return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"), REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); + if (fAddressIndex || fSpentIndex) + { + for (size_t j = 0; j < tx.vin.size(); j++) { + + const CTxIn input = tx.vin[j]; + const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); + uint160 hashBytes; + int addressType; + + if (prevout.scriptPubKey.IsPayToScriptHash()) { + hashBytes = uint160(vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); + addressType = 2; + } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + hashBytes = uint160(vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); + addressType = 1; + } else { + hashBytes.SetNull(); + addressType = 0; + } + + if (fAddressIndex && addressType > 0) { + // record spending activity + addressIndex.push_back(make_pair(CAddressIndexKey(addressType, hashBytes, pindex->nHeight, i, txhash, j, true), prevout.nValue * -1)); + + // remove address from unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(addressType, hashBytes, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); + } + + if (fSpentIndex) { + // add the spent index to determine the txid and input that spent an output + // and to find the amount and address from an input + spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->nHeight, prevout.nValue, addressType, hashBytes))); + } + } + + } + // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. @@ -2543,19 +2727,49 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); } - + txdata.emplace_back(tx); - + if (!tx.IsCoinBase()) { nFees += view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime) - tx.GetValueOut(); sum += interest; - + std::vector vChecks; if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } + + if (fAddressIndex) { + for (unsigned int k = 0; k < tx.vout.size(); k++) { + const CTxOut &out = tx.vout[k]; + + if (out.scriptPubKey.IsPayToScriptHash()) { + vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); + + // record receiving activity + addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue)); + + // record unspent output + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); + + } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + + // record receiving activity + addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue)); + + // record unspent output + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); + + } else { + continue; + } + + } + } + //if ( ASSETCHAINS_SYMBOL[0] == 0 ) // komodo_earned_interest(pindex->nHeight,sum); CTxUndo undoDummy; @@ -2563,28 +2777,28 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin blockundo.vtxundo.push_back(CTxUndo()); } UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); - + BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 ¬e_commitment, joinsplit.commitments) { // Insert the note commitments into our temporary tree. - + tree.append(note_commitment); } } - + vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - + view.PushAnchor(tree); if (!fJustCheck) { pindex->hashAnchorEnd = tree.root(); } blockundo.old_tree_root = old_tree_root; - + int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); - + CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()) + sum; if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 && block.vtx[0].vout.size() > 1 ) { @@ -2601,9 +2815,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if ( ASSETCHAINS_SYMBOL[0] != 0 || pindex->nHeight >= KOMODO_NOTARIES_HEIGHT1 || block.vtx[0].vout[0].nValue > blockReward ) { return state.DoS(100, - error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", - block.vtx[0].GetValueOut(), blockReward), - REJECT_INVALID, "bad-cb-amount"); + error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", + block.vtx[0].GetValueOut(), blockReward), + REJECT_INVALID, "bad-cb-amount"); } else if ( NOTARY_PUBKEY33[0] != 0 ) fprintf(stderr,"allow nHeight.%d coinbase %.8f vs %.8f interest %.8f\n",(int32_t)pindex->nHeight,dstr(block.vtx[0].GetValueOut()),dstr(blockReward),dstr(sum)); } @@ -2611,10 +2825,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, false); int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart; LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001); - + if (fJustCheck) return true; - + // Write undo information to disk if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { @@ -2624,42 +2838,77 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return error("ConnectBlock(): FindUndoPos failed"); if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); - + // update nUndoPos in block index pindex->nUndoPos = pos.nPos; pindex->nStatus |= BLOCK_HAVE_UNDO; } - + // Now that all consensus rules have been validated, set nCachedBranchId. // Move this if BLOCK_VALID_CONSENSUS is ever altered. static_assert(BLOCK_VALID_CONSENSUS == BLOCK_VALID_SCRIPTS, - "nCachedBranchId must be set after all consensus rules have been validated."); + "nCachedBranchId must be set after all consensus rules have been validated."); if (IsActivationHeightForAnyUpgrade(pindex->nHeight, Params().GetConsensus())) { pindex->nStatus |= BLOCK_ACTIVATES_UPGRADE; pindex->nCachedBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus()); } else if (pindex->pprev) { pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; } - + pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); setDirtyBlockIndex.insert(pindex); } - + if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "Failed to write transaction index"); + if (fAddressIndex) { + if (!pblocktree->WriteAddressIndex(addressIndex)) { + return AbortNode(state, "Failed to write address index"); + } + + if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) { + return AbortNode(state, "Failed to write address unspent index"); + } + } + + if (fSpentIndex) + if (!pblocktree->UpdateSpentIndex(spentIndex)) + return AbortNode(state, "Failed to write transaction index"); + + if (fTimestampIndex) { + unsigned int logicalTS = pindex->nTime; + unsigned int prevLogicalTS = 0; + + // retrieve logical timestamp of the previous block + if (pindex->pprev) + if (!pblocktree->ReadTimestampBlockIndex(pindex->pprev->GetBlockHash(), prevLogicalTS)) + LogPrintf("%s: Failed to read previous block's logical timestamp\n", __func__); + + if (logicalTS <= prevLogicalTS) { + logicalTS = prevLogicalTS + 1; + LogPrintf("%s: Previous logical timestamp is newer Actual[%d] prevLogical[%d] Logical[%d]\n", __func__, pindex->nTime, prevLogicalTS, logicalTS); + } + + if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(logicalTS, pindex->GetBlockHash()))) + return AbortNode(state, "Failed to write timestamp index"); + + if (!pblocktree->WriteTimestampBlockIndex(CTimestampBlockIndexKey(pindex->GetBlockHash()), CTimestampBlockIndexValue(logicalTS))) + return AbortNode(state, "Failed to write blockhash index"); + } + // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); - + int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2; LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); - + // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.vtx[0].GetHash(); - + int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3; LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); @@ -2689,88 +2938,88 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune); - fCheckForPruning = false; - if (!setFilesToPrune.empty()) { - fFlushForPrune = true; - if (!fHavePruned) { - pblocktree->WriteFlag("prunedblockfiles", true); - fHavePruned = true; + if (fPruneMode && fCheckForPruning && !fReindex) { + FindFilesToPrune(setFilesToPrune); + fCheckForPruning = false; + if (!setFilesToPrune.empty()) { + fFlushForPrune = true; + if (!fHavePruned) { + pblocktree->WriteFlag("prunedblockfiles", true); + fHavePruned = true; + } } } - } - int64_t nNow = GetTimeMicros(); - // Avoid writing/flushing immediately after startup. - if (nLastWrite == 0) { - nLastWrite = nNow; - } - if (nLastFlush == 0) { - nLastFlush = nNow; - } - if (nLastSetChain == 0) { - nLastSetChain = nNow; - } - size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); - // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). - bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; - // The cache is over the limit, we have to write now. - bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; - // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. - bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; - // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. - bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; - // Combine all conditions that result in a full cache flush. - bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; - // Write blocks and block index to disk. - if (fDoFullFlush || fPeriodicWrite) { - // Depend on nMinDiskSpace to ensure we can write block index - if (!CheckDiskSpace(0)) - return state.Error("out of disk space"); - // First make sure all block and undo data is flushed to disk. - FlushBlockFile(); - // Then update all block file information (which may refer to block and undo files). - { - std::vector > vFiles; - vFiles.reserve(setDirtyFileInfo.size()); - for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); - setDirtyFileInfo.erase(it++); - } - std::vector vBlocks; - vBlocks.reserve(setDirtyBlockIndex.size()); - for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { - vBlocks.push_back(*it); - setDirtyBlockIndex.erase(it++); - } - if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return AbortNode(state, "Files to write to block index database"); - } + int64_t nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) { + nLastWrite = nNow; + } + if (nLastFlush == 0) { + nLastFlush = nNow; + } + if (nLastSetChain == 0) { + nLastSetChain = nNow; + } + size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); + // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). + bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; + // The cache is over the limit, we have to write now. + bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. + bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // Combine all conditions that result in a full cache flush. + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) { + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) + return state.Error("out of disk space"); + // First make sure all block and undo data is flushed to disk. + FlushBlockFile(); + // Then update all block file information (which may refer to block and undo files). + { + std::vector > vFiles; + vFiles.reserve(setDirtyFileInfo.size()); + for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); + setDirtyFileInfo.erase(it++); + } + std::vector vBlocks; + vBlocks.reserve(setDirtyBlockIndex.size()); + for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + vBlocks.push_back(*it); + setDirtyBlockIndex.erase(it++); + } + if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { + return AbortNode(state, "Files to write to block index database"); + } + } + // Finally remove any pruned files + if (fFlushForPrune) + UnlinkPrunedFiles(setFilesToPrune); + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) { + // Typical CCoins structures on disk are around 128 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error("out of disk space"); + // Flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return AbortNode(state, "Failed to write to coin database"); + nLastFlush = nNow; + } + if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { + // Update best block in wallet (so we can detect restored wallets). + GetMainSignals().SetBestChain(chainActive.GetLocator()); + nLastSetChain = nNow; } - // Finally remove any pruned files - if (fFlushForPrune) - UnlinkPrunedFiles(setFilesToPrune); - nLastWrite = nNow; - } - // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) { - // Typical CCoins structures on disk are around 128 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) - return state.Error("out of disk space"); - // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return AbortNode(state, "Failed to write to coin database"); - nLastFlush = nNow; - } - if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { - // Update best block in wallet (so we can detect restored wallets). - GetMainSignals().SetBestChain(chainActive.GetLocator()); - nLastSetChain = nNow; - } } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error while flushing: ") + e.what()); } @@ -2792,18 +3041,18 @@ void PruneAndFlush() { void static UpdateTip(CBlockIndex *pindexNew) { const CChainParams& chainParams = Params(); chainActive.SetTip(pindexNew); - + // New best block nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - + KOMODO_NEWBLOCKS++; LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); - + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + cvBlockChange.notify_all(); - + // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; if (!IsInitialBlockDownload() && !fWarned) @@ -2853,7 +3102,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; - + if (!fBare) { // Resurrect mempool transactions from the disconnected block. BOOST_FOREACH(const CTransaction &tx, block.vtx) { @@ -2869,7 +3118,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { mempool.removeWithAnchor(anchorBeforeDisconnect); } } - + // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); // Get the current commitment tree @@ -2917,7 +3166,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); - bool rv = ConnectBlock(*pblock, state, pindexNew, view); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, false, true); GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) @@ -2939,10 +3188,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Remove conflicting transactions from the mempool. list txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); - + // Remove transactions that expire at new block height from mempool mempool.removeExpired(pindexNew->nHeight); - + // Update chainActive & related variables. UpdateTip(pindexNew); // Tell wallet about transactions that went from mempool @@ -2957,9 +3206,9 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Update cached incremental witnesses //fprintf(stderr,"chaintip true\n"); GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true); - + EnforceNodeDeprecation(pindexNew->nHeight); - + int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); @@ -2973,7 +3222,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * static CBlockIndex* FindMostWorkChain() { do { CBlockIndex *pindexNew = NULL; - + // Find the best candidate header. { std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); @@ -2981,14 +3230,14 @@ static CBlockIndex* FindMostWorkChain() { return NULL; pindexNew = *it; } - + // Check whether all blocks on the path between the currently active chain and the candidate are valid. // Just going until the active chain is an optimization, as we know all blocks in it are valid already. CBlockIndex *pindexTest = pindexNew; bool fInvalidAncestor = false; while (pindexTest && !chainActive.Contains(pindexTest)) { assert(pindexTest->nChainTx || pindexTest->nHeight == 0); - + // Pruned nodes may have entries in setBlockIndexCandidates for // which block files have been deleted. Remove those as candidates // for the most work chain if we come across them; we can't switch @@ -3045,7 +3294,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); - + // - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks. // - If pindexMostWork is in a chain that doesn't have the same genesis block as our chain, // then pindexFork will be null, and we would need to remove the entire chain including @@ -3054,23 +3303,23 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo static_assert(MAX_REORG_LENGTH > 0, "We must be able to reorg some distance"); if (reorgLength > MAX_REORG_LENGTH) { auto msg = strprintf(_( - "A block chain reorganization has been detected that would roll back %d blocks! " - "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." - ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + - _("Reorganization details") + ":\n" + - "- " + strprintf(_("Current tip: %s, height %d, work %s"), - pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight, pindexOldTip->nChainWork.GetHex()) + "\n" + - "- " + strprintf(_("New tip: %s, height %d, work %s"), - pindexMostWork->phashBlock->GetHex(), pindexMostWork->nHeight, pindexMostWork->nChainWork.GetHex()) + "\n" + - "- " + strprintf(_("Fork point: %s, height %d"), - pindexFork->phashBlock->GetHex(), pindexFork->nHeight) + "\n\n" + - _("Please help, human!"); + "A block chain reorganization has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + + _("Reorganization details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d, work %s"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight, pindexOldTip->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("New tip: %s, height %d, work %s"), + pindexMostWork->phashBlock->GetHex(), pindexMostWork->nHeight, pindexMostWork->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("Fork point: %s, height %d"), + pindexFork->phashBlock->GetHex(), pindexFork->nHeight) + "\n\n" + + _("Please help, human!"); LogPrintf("*** %s\n", msg); uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; } - + // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { @@ -3080,21 +3329,21 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo } if ( KOMODO_REWIND != 0 ) { + CBlockIndex *tipindex; fprintf(stderr,">>>>>>>>>>> rewind start ht.%d -> KOMODO_REWIND.%d\n",chainActive.Tip()->nHeight,KOMODO_REWIND); - while ( KOMODO_REWIND > 0 && chainActive.Tip()->nHeight > KOMODO_REWIND ) + while ( KOMODO_REWIND > 0 && (tipindex= chainActive.Tip()) != 0 && tipindex->nHeight > KOMODO_REWIND ) { - fprintf(stderr,"%d ",(int32_t)chainActive.Tip()->nHeight); + fBlocksDisconnected = true; + fprintf(stderr,"%d ",(int32_t)tipindex->nHeight); + InvalidateBlock(state,tipindex); if ( !DisconnectTip(state) ) - { - InvalidateBlock(state,chainActive.Tip()); break; - } } fprintf(stderr,"reached rewind.%d, best to do: ./komodo-cli -ac_name=%s stop\n",KOMODO_REWIND,ASSETCHAINS_SYMBOL); - sleep(60); + sleep(20); fprintf(stderr,"resuming normal operations\n"); KOMODO_REWIND = 0; - return(true); + //return(true); } // Build list of new blocks to connect. std::vector vpindexToConnect; @@ -3112,7 +3361,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo pindexIter = pindexIter->pprev; } nHeight = nTargetHeight; - + // Connect new blocks. BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { @@ -3138,20 +3387,20 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo } } } - + if (fBlocksDisconnected) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); } mempool.removeWithoutBranchId( - CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); mempool.check(pcoinsTip); - + // Callbacks/notifications for a new best chain. if (fInvalidFound) CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); else CheckForkWarningConditions(); - + return true; } @@ -3166,23 +3415,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { const CChainParams& chainParams = Params(); do { boost::this_thread::interruption_point(); - + bool fInitialDownload; { LOCK(cs_main); pindexMostWork = FindMostWorkChain(); - + // Whether we have anything to do at all. if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - + if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) return false; pindexNewTip = chainActive.Tip(); fInitialDownload = IsInitialBlockDownload(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). - + // Notifications/callbacks that can run without cs_main if (!fInitialDownload) { uint256 hashNewTip = pindexNewTip->GetBlockHash(); @@ -3195,8 +3444,8 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (nLocalServices & NODE_NETWORK) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip); @@ -3204,23 +3453,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { } //else fprintf(stderr,"initial download skips propagation\n"); } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(); - + // Write changes periodically to disk, after relay. if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { return false; } - + return true; } bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { AssertLockHeld(cs_main); - + // Mark the block itself as invalid. pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - + while (chainActive.Contains(pindex)) { CBlockIndex *pindexWalk = chainActive.Tip(); pindexWalk->nStatus |= BLOCK_FAILED_CHILD; @@ -3231,12 +3480,12 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { if (!DisconnectTip(state)) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); mempool.removeWithoutBranchId( - CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); return false; } } //LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - + // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. BlockMap::iterator it = mapBlockIndex.begin(); @@ -3246,19 +3495,19 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + InvalidChainFound(pindex); mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); mempool.removeWithoutBranchId( - CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); return true; } bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { AssertLockHeld(cs_main); - + int nHeight = pindex->nHeight; - + // Remove the invalidity flag from this block and all its descendants. BlockMap::iterator it = mapBlockIndex.begin(); while (it != mapBlockIndex.end()) { @@ -3275,7 +3524,7 @@ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + // Remove the invalidity flag from all ancestors too. while (pindex != NULL) { if (pindex->nStatus & BLOCK_FAILED_MASK) { @@ -3294,7 +3543,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end()) return it->second; - + // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(block); assert(pindexNew); @@ -3315,9 +3564,9 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) pindexBestHeader = pindexNew; - + setDirtyBlockIndex.insert(pindexNew); - + return pindexNew; } @@ -3341,12 +3590,12 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl pindexNew->nStatus |= BLOCK_HAVE_DATA; pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); setDirtyBlockIndex.insert(pindexNew); - + if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. deque queue; queue.push_back(pindexNew); - + // Recursively process any descendant blocks that now may be eligible to be connected. while (!queue.empty()) { CBlockIndex *pindex = queue.front(); @@ -3381,19 +3630,19 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); } } - + return true; } bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) { LOCK(cs_LastBlockFile); - + unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); } - + if (!fKnown) { while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { nFile++; @@ -3404,7 +3653,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd pos.nFile = nFile; pos.nPos = vinfoBlockFile[nFile].nSize; } - + if (nFile != nLastBlockFile) { if (!fKnown) { LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); @@ -3412,13 +3661,13 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd FlushBlockFile(!fKnown); nLastBlockFile = nFile; } - + vinfoBlockFile[nFile].AddBlock(nHeight, nTime); if (fKnown) vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); else vinfoBlockFile[nFile].nSize += nAddSize; - + if (!fKnown) { unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; @@ -3437,7 +3686,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd return state.Error("out of disk space"); } } - + setDirtyFileInfo.insert(nFile); return true; } @@ -3445,14 +3694,14 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) { pos.nFile = nFile; - + LOCK(cs_LastBlockFile); - + unsigned int nNewSize; pos.nPos = vinfoBlockFile[nFile].nUndoSize; nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; setDirtyFileInfo.insert(nFile); - + unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { @@ -3469,13 +3718,12 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne else return state.Error("out of disk space"); } - + return true; } bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& blockhdr, CValidationState& state, bool fCheckPOW) { - uint8_t pubkey33[33]; // Check timestamp if ( 0 ) { @@ -3502,35 +3750,73 @@ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& bl // Check block version //if (block.nVersion < MIN_BLOCK_VERSION) // return state.DoS(100, error("CheckBlockHeader(): block version too low"),REJECT_INVALID, "version-too-low"); - + // Check Equihash solution is valid if ( fCheckPOW && !CheckEquihashSolution(&blockhdr, Params()) ) return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution"); // Check proof of work matches claimed amount /*komodo_index2pubkey33(pubkey33,pindex,height); - if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,blockhdr.GetHash(), blockhdr.nBits, Params().GetConsensus()) ) - return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),REJECT_INVALID, "high-hash");*/ + if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,blockhdr.GetHash(), blockhdr.nBits, Params().GetConsensus()) ) + return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),REJECT_INVALID, "high-hash");*/ return true; } int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtime); +int32_t komodo_reverify_blockcheck(CValidationState& state,int32_t height,CBlockIndex *pindex) +{ + static int32_t oneshot; + CBlockIndex *tipindex; int32_t rewindtarget; + if ( KOMODO_REWIND != 0 ) + oneshot = KOMODO_REWIND; + if ( oneshot == 0 && IsInitialBlockDownload() == 0 && (tipindex= chainActive.Tip()) != 0 ) + { + // if 200 blocks behind longestchain and no blocks for 2 hours + if ( KOMODO_LONGESTCHAIN > height+200 && KOMODO_NEWBLOCKS == 0 ) + { + if ( GetAdjustedTime() > tipindex->nTime+3600*2 ) + { + fprintf(stderr,"possible fork: tip.%d longest.%d newblock.%d lag.%d blocktime.%u\n",tipindex->nHeight,KOMODO_LONGESTCHAIN,height,(int32_t)(GetAdjustedTime() - tipindex->nTime),tipindex->nTime); + /*KOMODO_REWIND = tipindex->nHeight - 11; + rewindtarget = tipindex->nHeight - 11; + fprintf(stderr,"rewindtarget <- %d\n",rewindtarget); + oneshot = 1; + while ( rewindtarget > 0 && (tipindex= chainActive.Tip()) != 0 && tipindex->nHeight > rewindtarget ) + { + fprintf(stderr,"%d ",(int32_t)tipindex->nHeight); + InvalidateBlock(state,tipindex); + if ( !DisconnectTip(state) ) + break; + } + tipindex = chainActive.Tip(); + fprintf(stderr,"rewind done to %d\n",tipindex!=0?tipindex->nHeight:-1);*/ + } + } + } + return(0); +} + bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, libzcash::ProofVerifier& verifier, bool fCheckPOW, bool fCheckMerkleRoot) { uint8_t pubkey33[33]; // These are checks that are independent of context. - + // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. if (!CheckBlockHeader(height,pindex,block,state,fCheckPOW)) + { + fprintf(stderr,"checkblockheader error PoW.%d\n",fCheckPOW); return false; + } komodo_block2pubkey33(pubkey33,(CBlock *)&block); if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus()) ) - return state.DoS(50, error("CheckBlock(): proof of work failed"),REJECT_INVALID, "high-hash"); - + { + //komodo_reverify_blockcheck(state,height,pindex); + return state.DoS(1, error("CheckBlock(): proof of work failed"),REJECT_INVALID, "high-hash"); + } // Check the merkle root. if (fCheckMerkleRoot) { bool mutated; @@ -3538,7 +3824,7 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat if (block.hashMerkleRoot != hashMerkleRoot2) return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); - + // Check for merkle tree malleability (CVE-2012-2459): repeating sequences // of transactions in a block without affecting the merkle root of a block, // while still invalidating it. @@ -3546,16 +3832,16 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat return state.DoS(100, error("CheckBlock(): duplicate transaction"), REJECT_INVALID, "bad-txns-duplicate", true); } - + // All potential-corruption validation must be done before we do any // transaction validation, as otherwise we may mark the header as invalid // because we receive the wrong transactions for it. - + // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckBlock(): size limits failed"), REJECT_INVALID, "bad-blk-length"); - + // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock(): first tx is not coinbase"), @@ -3564,12 +3850,12 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock(): more than one coinbase"), REJECT_INVALID, "bad-cb-multiple"); - + // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) { if ( komodo_validate_interest(tx,height == 0 ? komodo_block2height((CBlock *)&block) : height,block.nTime,1) < 0 ) - return error("CheckBlock: komodo_validate_interest failed"); + return error("CheckBlock: komodo_validate_interest failed"); if (!CheckTransaction(tx, state, verifier)) return error("CheckBlock(): CheckTransaction failed"); } @@ -3582,10 +3868,10 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); if ( komodo_check_deposit(height,block,(pindex==0||pindex->pprev==0)?0:pindex->pprev->nTime) < 0 ) - //if ( komodo_check_deposit(ASSETCHAINS_SYMBOL[0] == 0 ? height : pindex != 0 ? (int32_t)pindex->nHeight : chainActive.Tip()->nHeight+1,block,pindex==0||pindex->pprev==0?0:pindex->pprev->nTime) < 0 ) + //if ( komodo_check_deposit(ASSETCHAINS_SYMBOL[0] == 0 ? height : pindex != 0 ? (int32_t)pindex->nHeight : chainActive.Tip()->nHeight+1,block,pindex==0||pindex->pprev==0?0:pindex->pprev->nTime) < 0 ) { static uint32_t counter; - if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) + //if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) fprintf(stderr,"check deposit rejection\n"); return(false); } @@ -3599,11 +3885,11 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta uint256 hash = block.GetHash(); if (hash == consensusParams.hashGenesisBlock) return true; - + assert(pindexPrev); - + int nHeight = pindexPrev->nHeight+1; - + // Check proof of work if ( (nHeight < 235300 || nHeight > 236000) && block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) { @@ -3611,29 +3897,30 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); } - + // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); - + if (fCheckpointsEnabled) { // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) { - CBlockIndex *heightblock = chainActive[nHeight]; - if ( heightblock != 0 && heightblock->GetBlockHash() == hash ) - { - //fprintf(stderr,"got a pre notarization block that matches height.%d\n",(int32_t)nHeight); - return true; - } return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight),REJECT_CHECKPOINT, "checkpoint mismatch"); + /*CBlockIndex *heightblock = chainActive[nHeight]; + if ( heightblock != 0 && heightblock->GetBlockHash() == hash ) + { + //fprintf(stderr,"got a pre notarization block that matches height.%d\n",(int32_t)nHeight); + return true; + }*/ + return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight),REJECT_CHECKPOINT, "checkpoint mismatch"); } // Don't accept any forks from the main chain prior to last checkpoint CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); int32_t notarized_height; if (pcheckpoint && nHeight > 1 && nHeight < pcheckpoint->nHeight ) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d) vs %d", __func__, nHeight,pcheckpoint->nHeight)); + return state.DoS(1, error("%s: forked chain older than last checkpoint (height %d) vs %d", __func__, nHeight,pcheckpoint->nHeight)); else if ( komodo_checkpoint(¬arized_height,nHeight,hash) < 0 ) { CBlockIndex *heightblock = chainActive[nHeight]; @@ -3641,14 +3928,14 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta { //fprintf(stderr,"got a pre notarization block that matches height.%d\n",(int32_t)nHeight); return true; - } else return state.DoS(100, error("%s: forked chain %d older than last notarized (height %d) vs %d", __func__,nHeight, notarized_height)); + } else return state.DoS(1, error("%s: forked chain %d older than last notarized (height %d) vs %d", __func__,nHeight, notarized_height)); } } // Reject block.nVersion < 4 blocks if (block.nVersion < 4) return state.Invalid(error("%s : rejected nVersion<4 block", __func__), REJECT_OBSOLETE, "bad-version"); - + return true; } @@ -3656,24 +3943,24 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; const Consensus::Params& consensusParams = Params().GetConsensus(); - + // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) { - + // Check transaction contextually against consensus rules at block height if (!ContextualCheckTransaction(tx, state, nHeight, 100)) { return false; // Failure reason has been set in validation state object } - + int nLockTimeFlags = 0; int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) - ? pindexPrev->GetMedianTimePast() - : block.GetBlockTime(); + ? pindexPrev->GetMedianTimePast() + : block.GetBlockTime(); if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); } } - + // Enforce BIP 34 rule that the coinbase starts with serialized block height. // In Zcash this has been enforced since launch, except that the genesis // block didn't include the height in the coinbase (see Zcash protocol spec @@ -3686,7 +3973,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); } } - + return true; } @@ -3703,11 +3990,12 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc pindex = miSelf->second; if (ppindex) *ppindex = pindex; - if (pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK) + if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK ) return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); +#ifdef DEXcode if ( pindex != 0 && IsInitialBlockDownload() == 0 ) // jl777 debug test { - if (!CheckBlockHeader(pindex->nHeight,pindex, block, state)) + if (!CheckBlockHeader(pindex->nHeight,pindex, block, state,0)) { pindex->nStatus |= BLOCK_FAILED_MASK; fprintf(stderr,"known block failing CheckBlockHeader %d\n",(int32_t)pindex->nHeight); @@ -3734,17 +4022,22 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (!ContextualCheckBlockHeader(block, state, pindexPrev)) { pindex->nStatus |= BLOCK_FAILED_MASK; - fprintf(stderr,"known block.%d failing ContextualCheckBlockHeader\n",(int32_t)pindex->nHeight); + //fprintf(stderr,"known block.%d failing ContextualCheckBlockHeader\n",(int32_t)pindex->nHeight); return false; } } - - return true; + if ( *ppindex == 0 ) + fprintf(stderr,"unexpected null *ppindex\n"); +#endif + if ( pindex != 0 ) + return true; } - - if (!CheckBlockHeader(*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state)) + + if (!CheckBlockHeader(*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state,0)) + { + fprintf(stderr,"CheckBlockHeader failed\n"); return false; - + } // Get prev block index CBlockIndex* pindexPrev = NULL; if (hash != chainparams.GetConsensus().hashGenesisBlock) { @@ -3758,9 +4051,15 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + { + //fprintf(stderr,"ContextualCheckBlockHeader failed\n"); return false; + } if (pindex == NULL) - pindex = AddToBlockIndex(block); + { + if ( (pindex= AddToBlockIndex(block)) == 0 ) + fprintf(stderr,"couldnt add to block index\n"); + } if (ppindex) *ppindex = pindex; return true; @@ -3770,15 +4069,19 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); - + CBlockIndex *&pindex = *ppindex; if (!AcceptBlockHeader(block, state, &pindex)) - return false; - if ( pindex == 0 ) { - fprintf(stderr,"AcceptBlock error null pindex\n"); + fprintf(stderr,"AcceptBlockHeader rejected\n"); return false; } + if ( pindex == 0 ) + { + fprintf(stderr,"unexpected AcceptBlock error null pindex\n"); + return false; + } + //fprintf(stderr,"acceptblockheader passed\n"); // Try to process all requested blocks that we don't have, but only // process an unrequested block if it's new and has enough work to // advance our tip, and isn't too many blocks ahead. @@ -3790,7 +4093,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // regardless of whether pruning is enabled; it should generally be safe to // not process unrequested blocks. bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); - + // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. if (fAlreadyHave) return true; @@ -3799,19 +4102,21 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!fHasMoreWork) return true; // Don't process less-work chains if (fTooFarAhead) return true; // Block height is too high } - + // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); - if ((!CheckBlock(pindex->nHeight,pindex,block, state, verifier)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if ((!CheckBlock(pindex->nHeight,pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) + { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); } + fprintf(stderr,"CheckBlock or ContextualCheckBlock failed\n"); return false; } - + int nHeight = pindex->nHeight; - + // Write block to history file try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); @@ -3828,10 +4133,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); } - + if (fCheckForPruning) FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files - + return true; } @@ -3857,18 +4162,21 @@ bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBloc if ( chainActive.Tip() != 0 ) komodo_currentheight_set(chainActive.Tip()->nHeight); if ( ASSETCHAINS_SYMBOL[0] == 0 ) - checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier); - else checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier); + checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); + else checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); { LOCK(cs_main); bool fRequested = MarkBlockAsReceived(pblock->GetHash()); fRequested |= fForceProcessing; - if (!checked) { + if (!checked) + { if ( pfrom != 0 ) + { Misbehaving(pfrom->GetId(), 1); + } return error("%s: CheckBlock FAILED", __func__); } - + // Store to disk CBlockIndex *pindex = NULL; bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); @@ -3879,10 +4187,10 @@ bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBloc if (!ret) return error("%s: AcceptBlock FAILED", __func__); } - + if (!ActivateBestChain(state, pblock)) return error("%s: ActivateBestChain failed", __func__); - + return true; } @@ -3890,14 +4198,14 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex { AssertLockHeld(cs_main); assert(pindexPrev == chainActive.Tip()); - + CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; // JoinSplit proofs are verified in ConnectBlock auto verifier = libzcash::ProofVerifier::Disabled(); - + // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) { @@ -3906,7 +4214,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex } if (!CheckBlock(indexDummy.nHeight,0,block, state, verifier, fCheckPOW, fCheckMerkleRoot)) { - //fprintf(stderr,"TestBlockValidity failure B\n"); + fprintf(stderr,"TestBlockValidity failure B\n"); return false; } if (!ContextualCheckBlock(block, state, pindexPrev)) @@ -3914,13 +4222,13 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex fprintf(stderr,"TestBlockValidity failure C\n"); return false; } - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + if (!ConnectBlock(block, state, &indexDummy, viewNew, true,fCheckPOW)) { fprintf(stderr,"TestBlockValidity failure D\n"); return false; } assert(state.IsValid()); - + return true; } @@ -3950,7 +4258,7 @@ void PruneOneBlockFile(const int fileNumber) pindex->nDataPos = 0; pindex->nUndoPos = 0; setDirtyBlockIndex.insert(pindex); - + // Prune from mapBlocksUnlinked -- any block we prune would have // to be downloaded again in order to consider its chain, at which // point it would be considered as a candidate for @@ -3965,7 +4273,7 @@ void PruneOneBlockFile(const int fileNumber) } } } - + vinfoBlockFile[fileNumber].SetNull(); setDirtyFileInfo.insert(fileNumber); } @@ -3991,7 +4299,7 @@ void FindFilesToPrune(std::set& setFilesToPrune) if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { return; } - + unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files @@ -4000,21 +4308,21 @@ void FindFilesToPrune(std::set& setFilesToPrune) uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; uint64_t nBytesToPrune; int count=0; - + if (nCurrentUsage + nBuffer >= nPruneTarget) { for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; - + if (vinfoBlockFile[fileNumber].nSize == 0) continue; - + if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? break; - + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) continue; - + PruneOneBlockFile(fileNumber); // Queue up the files for removal setFilesToPrune.insert(fileNumber); @@ -4022,24 +4330,25 @@ void FindFilesToPrune(std::set& setFilesToPrune) count++; } } - + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", - nPruneTarget/1024/1024, nCurrentUsage/1024/1024, - ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, - nLastBlockWeCanPrune, count); + nPruneTarget/1024/1024, nCurrentUsage/1024/1024, + ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, + nLastBlockWeCanPrune, count); } bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; - + // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) return AbortNode("Disk space is low!", _("Error: Disk space is low!")); - + return true; } + FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { static int32_t didinit[1000]; long fsize,fpos; int32_t incr = 16*1024*1024; @@ -4101,30 +4410,32 @@ CBlockIndex * InsertBlockIndex(uint256 hash) { if (hash.IsNull()) return NULL; - + // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) return (*mi).second; - + // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) throw runtime_error("LoadBlockIndex(): new CBlockIndex failed"); mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); - + return pindexNew; } +//void komodo_pindex_init(CBlockIndex *pindex,int32_t height); + bool static LoadBlockIndexDB() { const CChainParams& chainparams = Params(); if (!pblocktree->LoadBlockIndexGuts()) return false; - + boost::this_thread::interruption_point(); - + // Calculate nChainWork vector > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); @@ -4132,6 +4443,7 @@ bool static LoadBlockIndexDB() { CBlockIndex* pindex = item.second; vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) @@ -4180,8 +4492,9 @@ bool static LoadBlockIndexDB() pindex->BuildSkip(); if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) pindexBestHeader = pindex; + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } - + // Load block file info pblocktree->ReadLastBlockFile(nLastBlockFile); vinfoBlockFile.resize(nLastBlockFile + 1); @@ -4198,7 +4511,7 @@ bool static LoadBlockIndexDB() break; } } - + // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); set setBlkDataFiles; @@ -4208,6 +4521,7 @@ bool static LoadBlockIndexDB() if (pindex->nStatus & BLOCK_HAVE_DATA) { setBlkDataFiles.insert(pindex->nFile); } + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) { @@ -4216,21 +4530,33 @@ bool static LoadBlockIndexDB() return false; } } - + // Check whether we have ever pruned block & undo files pblocktree->ReadFlag("prunedblockfiles", fHavePruned); if (fHavePruned) LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); - + // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); fReindex |= fReindexing; - + // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + // Check whether we have an address index + pblocktree->ReadFlag("addressindex", fAddressIndex); + LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled"); + + // Check whether we have a timestamp index + pblocktree->ReadFlag("timestampindex", fTimestampIndex); + LogPrintf("%s: timestamp index %s\n", __func__, fTimestampIndex ? "enabled" : "disabled"); + + // Check whether we have a spent index + pblocktree->ReadFlag("spentindex", fSpentIndex); + LogPrintf("%s: spent index %s\n", __func__, fSpentIndex ? "enabled" : "disabled"); + // Fill in-memory data BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { @@ -4243,8 +4569,9 @@ bool static LoadBlockIndexDB() if (pindex->pprev) { pindex->pprev->hashAnchorEnd = pindex->hashAnchor; } + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } - + // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); if (it == mapBlockIndex.end()) @@ -4252,16 +4579,16 @@ bool static LoadBlockIndexDB() chainActive.SetTip(it->second); // Set hashAnchorEnd for the end of best chain it->second->hashAnchorEnd = pcoinsTip->GetBestAnchor(); - + PruneBlockIndexCandidates(); - + LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); - + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); + EnforceNodeDeprecation(chainActive.Height(), true); - + return true; } @@ -4280,7 +4607,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth LOCK(cs_main); if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; - + // Verify blocks in the best chain if (nCheckDepth <= 0) nCheckDepth = 1000000000; // suffices until the year 19000 @@ -4306,7 +4633,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,block, state, verifier)) + if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,block, state, verifier,0)) return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -4334,7 +4661,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } if (pindexFailure) return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); - + // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { CBlockIndex *pindex = pindexState; @@ -4345,20 +4672,20 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth CBlock block; if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins)) + if (!ConnectBlock(block, state, pindex, coins,false, true)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } - + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); - + return true; } bool RewindBlockIndex(const CChainParams& params) { LOCK(cs_main); - + // RewindBlockIndex is called after LoadBlockIndex, so at this point every block // index will have nCachedBranchId set based on the values previously persisted // to disk. By definition, a set nCachedBranchId means that the block was @@ -4373,10 +4700,10 @@ bool RewindBlockIndex(const CChainParams& params) bool fFlagSet = pindex->nStatus & BLOCK_ACTIVATES_UPGRADE; bool fFlagExpected = IsActivationHeightForAnyUpgrade(pindex->nHeight, consensus); return fFlagSet == fFlagExpected && - pindex->nCachedBranchId && - *pindex->nCachedBranchId == CurrentEpochBranchId(pindex->nHeight, consensus); + pindex->nCachedBranchId && + *pindex->nCachedBranchId == CurrentEpochBranchId(pindex->nHeight, consensus); }; - + int nHeight = 1; while (nHeight <= chainActive.Height()) { if (!sufficientlyValidated(chainActive[nHeight])) { @@ -4384,28 +4711,28 @@ bool RewindBlockIndex(const CChainParams& params) } nHeight++; } - + // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 auto rewindLength = chainActive.Height() - nHeight; if (rewindLength > 0 && rewindLength > MAX_REORG_LENGTH) { auto pindexOldTip = chainActive.Tip(); auto pindexRewind = chainActive[nHeight - 1]; auto msg = strprintf(_( - "A block chain rewind has been detected that would roll back %d blocks! " - "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." - ), rewindLength, MAX_REORG_LENGTH) + "\n\n" + - _("Rewind details") + ":\n" + - "- " + strprintf(_("Current tip: %s, height %d"), - pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight) + "\n" + - "- " + strprintf(_("Rewinding to: %s, height %d"), - pindexRewind->phashBlock->GetHex(), pindexRewind->nHeight) + "\n\n" + - _("Please help, human!"); + "A block chain rewind has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), rewindLength, MAX_REORG_LENGTH) + "\n\n" + + _("Rewind details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight) + "\n" + + "- " + strprintf(_("Rewinding to: %s, height %d"), + pindexRewind->phashBlock->GetHex(), pindexRewind->nHeight) + "\n\n" + + _("Please help, human!"); LogPrintf("*** %s\n", msg); uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; } - + CValidationState state; CBlockIndex* pindex = chainActive.Tip(); while (chainActive.Height() >= nHeight) { @@ -4424,13 +4751,13 @@ bool RewindBlockIndex(const CChainParams& params) if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) return false; } - + // Reduce validity flag and have-data flags. // We do this after actual disconnecting, otherwise we'll end up writing the lack of data // to disk before writing the chainstate, resulting in a failure to continue if interrupted. for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { CBlockIndex* pindexIter = it->second; - + // Note: If we encounter an insufficiently validated block that // is on chainActive, it must be because we are a pruning node, and // this block or some successor doesn't HAVE_DATA, so we were unable to @@ -4439,8 +4766,8 @@ bool RewindBlockIndex(const CChainParams& params) if (!sufficientlyValidated(pindexIter) && !chainActive.Contains(pindexIter)) { // Reduce validity pindexIter->nStatus = - std::min(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | - (pindexIter->nStatus & ~BLOCK_VALID_MASK); + std::min(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | + (pindexIter->nStatus & ~BLOCK_VALID_MASK); // Remove have-data flags pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO); // Remove branch ID @@ -4458,6 +4785,12 @@ bool RewindBlockIndex(const CChainParams& params) pindexIter->nSequenceId = 0; // Make sure it gets written setDirtyBlockIndex.insert(pindexIter); + if (pindexIter == pindexBestInvalid) + { + //fprintf(stderr,"Reset invalid block marker if it was pointing to this block\n"); + pindexBestInvalid = NULL; + } + // Update indices setBlockIndexCandidates.erase(pindexIter); auto ret = mapBlocksUnlinked.equal_range(pindexIter->pprev); @@ -4472,15 +4805,15 @@ bool RewindBlockIndex(const CChainParams& params) setBlockIndexCandidates.insert(pindexIter); } } - + PruneBlockIndexCandidates(); - + CheckBlockIndex(); - + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { return false; } - + return true; } @@ -4507,7 +4840,7 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); - + BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; } @@ -4532,19 +4865,31 @@ bool LoadBlockIndex() bool InitBlockIndex() { const CChainParams& chainparams = Params(); LOCK(cs_main); - + // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); - + // Check whether we're already initialized if (chainActive.Genesis() != NULL) return true; - + // Use the provided setting for -txindex in the new database fTxIndex = GetBoolArg("-txindex", true); pblocktree->WriteFlag("txindex", fTxIndex); - LogPrintf("Initializing databases...\n"); + // Use the provided setting for -addressindex in the new database + fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX); + pblocktree->WriteFlag("addressindex", fAddressIndex); + + // Use the provided setting for -timestampindex in the new database + fTimestampIndex = GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX); + pblocktree->WriteFlag("timestampindex", fTimestampIndex); + + fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX); + pblocktree->WriteFlag("spentindex", fSpentIndex); + + LogPrintf("Initializing databases...\n"); + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { try { @@ -4570,7 +4915,7 @@ bool InitBlockIndex() { return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); } } - + return true; } @@ -4582,7 +4927,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); - + int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor @@ -4590,7 +4935,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); - + blkdat.SetPos(nRewind); nRewind++; // start one byte further next time, in case of failure blkdat.SetLimit(); // remove former limit @@ -4621,17 +4966,17 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) CBlock block; blkdat >> block; nRewind = blkdat.GetPos(); - + // detect out of order blocks, and store them for later uint256 hash = block.GetHash(); if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), - block.hashPrevBlock.ToString()); + block.hashPrevBlock.ToString()); if (dbp) mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); continue; } - + // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { CValidationState state; @@ -4642,7 +4987,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } - + // Recursively process earlier encountered successors of this block deque queue; queue.push_back(hash); @@ -4655,7 +5000,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) if (ReadBlockFromDisk(mapBlockIndex[hash]!=0?mapBlockIndex[hash]->nHeight:0,block, it->second)) { LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), - head.ToString()); + head.ToString()); CValidationState dummy; if (ProcessNewBlock(0,dummy, NULL, &block, true, &it->second)) { @@ -4685,9 +5030,9 @@ void static CheckBlockIndex() if (!fCheckBlockIndex) { return; } - + LOCK(cs_main); - + // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when // iterating the block tree require that chainActive has been initialized.) @@ -4695,20 +5040,20 @@ void static CheckBlockIndex() assert(mapBlockIndex.size() <= 1); return; } - + // Build forward-pointing map of the entire block tree. std::multimap forward; for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { - forward.insert(std::make_pair(it->second->pprev, it->second)); + forward.insert(std::make_pair(it->second->pprev, it->second)); } - + assert(forward.size() == mapBlockIndex.size()); - + std::pair::iterator,std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); CBlockIndex *pindex = rangeGenesis.first->second; rangeGenesis.first++; assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. - + // Iterate over the entire block tree, using depth-first search. // Along the way, remember whether there are blocks on the path from genesis // block being explored which are the first to have certain properties. @@ -4730,7 +5075,7 @@ void static CheckBlockIndex() if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; - + // Begin: actual consistency checks. if (pindex->pprev == NULL) { // Genesis block checks. @@ -4816,7 +5161,7 @@ void static CheckBlockIndex() } // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow // End: actual consistency checks. - + // Try descending into the first subnode. std::pair::iterator,std::multimap::iterator> range = forward.equal_range(pindex); if (range.first != range.second) { @@ -4859,7 +5204,7 @@ void static CheckBlockIndex() } } } - + // Check that we actually traversed the entire map. assert(nNodes == forward.size()); } @@ -4874,20 +5219,20 @@ std::string GetWarnings(const std::string& strFor) int nPriority = 0; string strStatusBar; string strRPC; - + if (!CLIENT_VERSION_IS_RELEASE) strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); - + if (GetBoolArg("-testsafemode", false)) strStatusBar = strRPC = "testsafemode enabled"; - + // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { nPriority = 1000; strStatusBar = strMiscWarning; } - + if (fLargeWorkForkFound) { nPriority = 2000; @@ -4898,7 +5243,7 @@ std::string GetWarnings(const std::string& strFor) nPriority = 2000; strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); } - + // Alerts { LOCK(cs_mapAlerts); @@ -4915,7 +5260,7 @@ std::string GetWarnings(const std::string& strFor) } } } - + if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") @@ -4941,7 +5286,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { switch (inv.type) { - case MSG_TX: + case MSG_TX: { assert(recentRejects); if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) @@ -4953,14 +5298,14 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); recentRejects->reset(); } - + return recentRejects->contains(inv.hash) || - mempool.exists(inv.hash) || - mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); + mempool.exists(inv.hash) || + mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); } - case MSG_BLOCK: - return mapBlockIndex.count(inv.hash); + case MSG_BLOCK: + return mapBlockIndex.count(inv.hash); } // Don't know what it is, just say we already got one return true; @@ -4969,21 +5314,21 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); - + vector vNotFound; - + LOCK(cs_main); - + while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; - + const CInv &inv = *it; { boost::this_thread::interruption_point(); it++; - + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { bool send = false; @@ -4998,8 +5343,8 @@ void static ProcessGetData(CNode* pfrom) // chain if they are valid, and no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about. send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && - (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); + (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } @@ -5087,17 +5432,17 @@ void static ProcessGetData(CNode* pfrom) vNotFound.push_back(inv); } } - + // Track requests for our stuff. GetMainSignals().Inventory(inv.hash); - + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) break; } } - + pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); - + if (!vNotFound.empty()) { // Let the peer know that we didn't find what it asked for, so it doesn't // have to wait around forever. Currently only SPV clients actually care @@ -5120,10 +5465,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } - - - - + + + + if (strCommand == "version") { // Each connection can only send one version message @@ -5133,7 +5478,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 1); return false; } - + int64_t nTime; CAddress addrMe; CAddress addrFrom; @@ -5148,7 +5493,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return false; } - + // When Overwinter is active, reject incoming connections from non-Overwinter nodes const Consensus::Params& params = Params().GetConsensus(); if (NetworkUpgradeActive(GetHeight(), params, Consensus::UPGRADE_OVERWINTER) @@ -5156,12 +5501,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", - params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + strprintf("Version must be %d or greater", + params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); pfrom->fDisconnect = true; return false; } - + if (pfrom->nVersion == 10300) pfrom->nVersion = 300; if (!vRecv.empty()) @@ -5176,7 +5521,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message else pfrom->fRelayTxes = true; - + // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { @@ -5184,26 +5529,26 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return true; } - + pfrom->addrLocal = addrMe; if (pfrom->fInbound && addrMe.IsRoutable()) { SeenLocal(addrMe); } - + // Be shy and don't send version until we hear if (pfrom->fInbound) pfrom->PushVersion(); - + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - + // Potentially mark this peer as a preferred download peer. UpdatePreferredDownload(pfrom, State(pfrom->GetId())); - + // Change version pfrom->PushMessage("verack"); pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - + if (!pfrom->fInbound) { // Advertise our address @@ -5220,7 +5565,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushAddress(addr); } } - + // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { @@ -5235,72 +5580,72 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, addrman.Good(addrFrom); } } - + // Relay alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - item.second.RelayTo(pfrom); + item.second.RelayTo(pfrom); } - + pfrom->fSuccessfullyConnected = true; - + string remoteAddr; if (fLogIPs) remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); - + LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); - + int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); } - - + + else if (pfrom->nVersion == 0) { // Must have a version message before anything else Misbehaving(pfrom->GetId(), 1); return false; } - - + + else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - + // Mark this node as currently connected, so we update its timestamp later. if (pfrom->fNetworkNode) { LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; } } - - + + // Disconnect existing peer connection when: // 1. The version message has been received // 2. Overwinter is active // 3. Peer version is pre-Overwinter else if (NetworkUpgradeActive(GetHeight(), chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER) - && (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)) + && (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)) { LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", - chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + strprintf("Version must be %d or greater", + chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); pfrom->fDisconnect = true; return false; } - - + + else if (strCommand == "addr") { vector vAddr; vRecv >> vAddr; - + // Don't want addr from older versions unless seeding if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; @@ -5309,7 +5654,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } - + // Store the new addresses vector vAddrOk; int64_t nNow = GetAdjustedTime(); @@ -5317,7 +5662,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, BOOST_FOREACH(CAddress& addr, vAddr) { boost::this_thread::interruption_point(); - + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); @@ -5361,8 +5706,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->fOneShot) pfrom->fDisconnect = true; } - - + + else if (strCommand == "inv") { vector vInv; @@ -5372,24 +5717,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } - + LOCK(cs_main); - + std::vector vToFetch; - + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; - + boost::this_thread::interruption_point(); pfrom->AddInventoryKnown(inv); - + bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); - + if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK) pfrom->AskFor(inv); - + if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { @@ -5413,21 +5758,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } } - + // Track requests for our stuff GetMainSignals().Inventory(inv.hash); - + if (pfrom->nSendSize > (SendBufferSize() * 2)) { Misbehaving(pfrom->GetId(), 50); return error("send buffer size() = %u", pfrom->nSendSize); } } - + if (!vToFetch.empty()) pfrom->PushMessage("getdata", vToFetch); } - - + + else if (strCommand == "getdata") { vector vInv; @@ -5437,29 +5782,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } - + if (fDebug || (vInv.size() != 1)) LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); - + if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); - + pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom); } - - + + else if (strCommand == "getblocks") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; - + LOCK(cs_main); - + // Find the last block the caller has in the main chain CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); - + // Send the rest of the chain if (pindex) pindex = chainActive.Next(pindex); @@ -5483,19 +5828,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - - + + else if (strCommand == "getheaders") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; - + LOCK(cs_main); - + if (IsInitialBlockDownload()) return true; - + CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -5512,7 +5857,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindex) pindex = chainActive.Next(pindex); } - + // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector vHeaders; int nLimit = MAX_HEADERS_RESULTS; @@ -5535,37 +5880,37 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, fprintf(stderr,"you can ignore redundant getheaders from peer.%d %d prev.%d\n",(int32_t)pfrom->id,(int32_t)(pindex ? pindex->nHeight : -1),pfrom->lasthdrsreq); } } - - + + else if (strCommand == "tx") { vector vWorkQueue; vector vEraseQueue; CTransaction tx; vRecv >> tx; - + CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); - + LOCK(cs_main); - + bool fMissingInputs = false; CValidationState state; - + pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv); - + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { mempool.check(pcoinsTip); RelayTransaction(tx); vWorkQueue.push_back(inv.hash); - + LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", - pfrom->id, pfrom->cleanSubVer, - tx.GetHash().ToString(), - mempool.mapTx.size()); - + pfrom->id, pfrom->cleanSubVer, + tx.GetHash().ToString(), + mempool.mapTx.size()); + // Recursively process any orphan transactions that depended on this one set setMisbehaving; for (unsigned int i = 0; i < vWorkQueue.size(); i++) @@ -5585,8 +5930,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get // anyone relaying LegitTxX banned) CValidationState stateDummy; - - + + if (setMisbehaving.count(fromPeer)) continue; if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) @@ -5616,15 +5961,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.check(pcoinsTip); } } - + BOOST_FOREACH(uint256 hash, vEraseQueue) - EraseOrphanTx(hash); + EraseOrphanTx(hash); } // TODO: currently, prohibit joinsplits from entering mapOrphans else if (fMissingInputs && tx.vjoinsplit.size() == 0) { AddOrphanTx(tx, pfrom->GetId()); - + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); @@ -5633,7 +5978,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { assert(recentRejects); recentRejects->insert(tx.GetHash()); - + if (pfrom->fWhitelisted) { // Always relay transactions received from whitelisted peers, even // if they were already in the mempool or rejected from it due @@ -5649,7 +5994,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s (code %d))\n", - tx.GetHash().ToString(), pfrom->id, state.GetRejectReason(), state.GetRejectCode()); + tx.GetHash().ToString(), pfrom->id, state.GetRejectReason(), state.GetRejectCode()); } } } @@ -5657,20 +6002,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (state.IsInvalid(nDoS)) { LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), - pfrom->id, pfrom->cleanSubVer, - state.GetRejectReason()); + pfrom->id, pfrom->cleanSubVer, + state.GetRejectReason()); pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } } - - + + else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing { std::vector headers; - + // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { @@ -5682,14 +6027,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> headers[n]; ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - + LOCK(cs_main); - + if (nCount == 0) { // Nothing interesting. Stop asking this peers for more headers. return true; } - + CBlockIndex *pindexLast = NULL; BOOST_FOREACH(const CBlockHeader& header, headers) { CValidationState state; @@ -5699,17 +6044,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (!AcceptBlockHeader(header, state, &pindexLast)) { int nDoS; - if (state.IsInvalid(nDoS)) { + if (state.IsInvalid(nDoS)) + { if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS/nDoS); return error("invalid header received"); } } } - + if (pindexLast) UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - + if (nCount == MAX_HEADERS_RESULTS && pindexLast) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue @@ -5721,20 +6067,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } } - + CheckBlockIndex(); } - + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; - + CInv inv(MSG_BLOCK, block.GetHash()); LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - + pfrom->AddInventoryKnown(inv); - + CValidationState state; // Process all blocks from whitelisted peers, even if not requested, // unless we're still syncing with the network. @@ -5751,10 +6097,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), nDoS); } } - + } - - + + // This asymmetric behavior for inbound and outbound connections was introduced // to prevent a fingerprinting attack: an attacker can send specific fake addresses // to users' AddrMan and later request them by sending getaddr messages. @@ -5769,18 +6115,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } pfrom->fSentAddr = true; - + pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); + pfrom->PushAddress(addr); } - - + + else if (strCommand == "mempool") { LOCK2(cs_main, pfrom->cs_filter); - + std::vector vtxid; mempool.queryHashes(vtxid); vector vInv; @@ -5790,7 +6136,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fInMemPool = mempool.lookup(hash, tx); if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || - (!pfrom->pfilter)) + (!pfrom->pfilter)) vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pfrom->PushMessage("inv", vInv); @@ -5800,8 +6146,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); } - - + + else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) @@ -5822,8 +6168,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("pong", nonce); } } - - + + else if (strCommand == "pong") { int64_t pingUsecEnd = nTimeReceived; @@ -5831,10 +6177,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, size_t nAvail = vRecv.in_avail(); bool bPingFinished = false; std::string sProblem; - + if (nAvail >= sizeof(nonce)) { vRecv >> nonce; - + // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) if (pfrom->nPingNonceSent != 0) { if (nonce == pfrom->nPingNonceSent) { @@ -5866,27 +6212,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bPingFinished = true; sProblem = "Short payload"; } - + if (!(sProblem.empty())) { LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", - pfrom->id, - pfrom->cleanSubVer, - sProblem, - pfrom->nPingNonceSent, - nonce, - nAvail); + pfrom->id, + pfrom->cleanSubVer, + sProblem, + pfrom->nPingNonceSent, + nonce, + nAvail); } if (bPingFinished) { pfrom->nPingNonceSent = 0; } } - - + + else if (fAlerts && strCommand == "alert") { CAlert alert; vRecv >> alert; - + uint256 alertHash = alert.GetHash(); if (pfrom->setKnown.count(alertHash) == 0) { @@ -5897,7 +6243,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); + alert.RelayTo(pnode); } } else { @@ -5911,13 +6257,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - - + + else if (strCommand == "filterload") { CBloomFilter filter; vRecv >> filter; - + if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter Misbehaving(pfrom->GetId(), 100); @@ -5930,13 +6276,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } pfrom->fRelayTxes = true; } - - + + else if (strCommand == "filteradd") { vector vData; vRecv >> vData; - + // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) @@ -5950,8 +6296,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 100); } } - - + + else if (strCommand == "filterclear") { LOCK(pfrom->cs_filter); @@ -5959,18 +6305,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->pfilter = new CBloomFilter(); pfrom->fRelayTxes = true; } - - + + else if (strCommand == "reject") { if (fDebug) { try { string strMsg; unsigned char ccode; string strReason; vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); - + ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; - + if (strMsg == "block" || strMsg == "tx") { uint256 hash; @@ -5988,14 +6334,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // We do not care about the NOTFOUND message, but logging an Unknown Command // message would be undesirable as we transmit it ourselves. } - + else { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } - - - + + + return true; } @@ -6004,7 +6350,7 @@ bool ProcessMessages(CNode* pfrom) { //if (fDebug) // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); - + // // Message format // (4) message start @@ -6014,41 +6360,41 @@ bool ProcessMessages(CNode* pfrom) // (x) data // bool fOk = true; - + if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom); - + // this maintains the order of responses if (!pfrom->vRecvGetData.empty()) return fOk; - + std::deque::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; - + // get next message CNetMessage& msg = *it; - + //if (fDebug) // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); - + // end, if an incomplete message is found if (!msg.complete()) break; - + // at this point, any failure means we can delete the current message it++; - + // Scan for message start if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); fOk = false; break; } - + // Read header CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid(Params().MessageStart())) @@ -6057,10 +6403,10 @@ bool ProcessMessages(CNode* pfrom) continue; } string strCommand = hdr.GetCommand(); - + // Message size unsigned int nMessageSize = hdr.nMessageSize; - + // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); @@ -6068,10 +6414,10 @@ bool ProcessMessages(CNode* pfrom) if (nChecksum != hdr.nChecksum) { LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__, - SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); + SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } - + // Process message bool fRet = false; try @@ -6105,17 +6451,17 @@ bool ProcessMessages(CNode* pfrom) } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); } - + if (!fRet) LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); - + break; } - + // In case the connection got shut down, its receive buffer was wiped if (!pfrom->fDisconnect) pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); - + return fOk; } @@ -6127,7 +6473,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Don't send anything until we get its version message if (pto->nVersion == 0) return true; - + // // Message: ping // @@ -6156,11 +6502,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("ping"); } } - + TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() if (!lockMain) return true; - + // Address refresh broadcast static int64_t nLastRebroadcast; if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) @@ -6171,14 +6517,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Periodically clear addrKnown to allow refresh broadcasts if (nLastRebroadcast) pnode->addrKnown.reset(); - + // Rebroadcast our address AdvertizeLocal(pnode); } if (!vNodes.empty()) nLastRebroadcast = GetTime(); } - + // // Message: addr // @@ -6204,7 +6550,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!vAddr.empty()) pto->PushMessage("addr", vAddr); } - + CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { if (pto->fWhitelisted) @@ -6220,11 +6566,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } state.fShouldBan = false; } - + BOOST_FOREACH(const CBlockReject& reject, state.rejects) - pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); + pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); - + // Start block sync if (pindexBestHeader == NULL) pindexBestHeader = chainActive.Tip(); @@ -6239,7 +6585,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256()); } } - + // Resend wallet transactions that haven't gotten in a block yet // Except during reindex, importing and IBD, when old wallet // transactions become unconfirmed and spams other nodes. @@ -6247,7 +6593,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { GetMainSignals().Broadcast(nTimeBestReceived); } - + // // Message: inventory // @@ -6261,7 +6607,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { if (pto->setInventoryKnown.count(inv)) continue; - + // trickle out tx inv to protect privacy if (inv.type == MSG_TX && !fSendTrickle) { @@ -6272,14 +6618,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0); - + if (fTrickleWait) { vInvWait.push_back(inv); continue; } } - + // returns true if wasn't already contained in the set if (pto->setInventoryKnown.insert(inv).second) { @@ -6295,7 +6641,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } if (!vInv.empty()) pto->PushMessage("inv", vInv); - + // Detect whether we're stalling int64_t nNow = GetTimeMicros(); if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { @@ -6327,7 +6673,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->fDisconnect = true; } } - + // // Message: getdata (blocks) // @@ -6340,7 +6686,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), - pindex->nHeight, pto->id); + pindex->nHeight, pto->id); } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { @@ -6349,7 +6695,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } } - + // // Message: getdata (non-blocks) // @@ -6374,14 +6720,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } if (!vGetData.empty()) pto->PushMessage("getdata", vGetData); - + } return true; } - std::string CBlockFileInfo::ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); - } +std::string CBlockFileInfo::ToString() const { + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); +} @@ -6395,7 +6741,7 @@ public: for (; it1 != mapBlockIndex.end(); it1++) delete (*it1).second; mapBlockIndex.clear(); - + // orphan transactions mapOrphanTransactions.clear(); mapOrphanTransactionsByPrev.clear(); @@ -6404,7 +6750,7 @@ public: extern "C" const char* getDataDir() { - return GetDataDir().string().c_str(); + return GetDataDir().string().c_str(); } @@ -6412,14 +6758,14 @@ extern "C" const char* getDataDir() CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight) { CMutableTransaction mtx; - + bool isOverwintered = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_OVERWINTER); if (isOverwintered) { mtx.fOverwintered = true; mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; mtx.nVersion = 3; // Expiry height is not set. Only fields required for a parser to treat as a valid Overwinter V3 tx. - + // TODO: In future, when moving from Overwinter to Sapling, it will be useful // to set the expiry height to: min(activation_height - 1, default_expiry_height) } diff --git a/src/main.h b/src/main.h index 1c864de20..9ae87710a 100644 --- a/src/main.h +++ b/src/main.h @@ -22,6 +22,7 @@ #include "script/script.h" #include "script/serverchecker.h" #include "script/standard.h" +#include "spentindex.h" #include "sync.h" #include "tinyformat.h" #include "txmempool.h" @@ -101,6 +102,12 @@ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60; /** Maximum length of reject messages. */ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; +static const bool DEFAULT_ADDRESSINDEX = false; +static const bool DEFAULT_TIMESTAMPINDEX = false; +static const bool DEFAULT_SPENTINDEX = false; +static const unsigned int DEFAULT_DB_MAX_OPEN_FILES = 1000; +static const bool DEFAULT_DB_COMPRESSION = true; + // Sanity check the magic numbers when we change them BOOST_STATIC_ASSERT(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE); BOOST_STATIC_ASSERT(DEFAULT_BLOCK_PRIORITY_SIZE <= DEFAULT_BLOCK_MAX_SIZE); @@ -268,6 +275,338 @@ struct CNodeStateStats { std::vector vHeightInFlight; }; +struct CTimestampIndexIteratorKey { + unsigned int timestamp; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 4; + } + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata32be(s, timestamp); + } + template + void Unserialize(Stream& s, int nType, int nVersion) { + timestamp = ser_readdata32be(s); + } + + CTimestampIndexIteratorKey(unsigned int time) { + timestamp = time; + } + + CTimestampIndexIteratorKey() { + SetNull(); + } + + void SetNull() { + timestamp = 0; + } +}; + +struct CTimestampIndexKey { + unsigned int timestamp; + uint256 blockHash; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 36; + } + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata32be(s, timestamp); + blockHash.Serialize(s, nType, nVersion); + } + template + void Unserialize(Stream& s, int nType, int nVersion) { + timestamp = ser_readdata32be(s); + blockHash.Unserialize(s, nType, nVersion); + } + + CTimestampIndexKey(unsigned int time, uint256 hash) { + timestamp = time; + blockHash = hash; + } + + CTimestampIndexKey() { + SetNull(); + } + + void SetNull() { + timestamp = 0; + blockHash.SetNull(); + } +}; + +struct CTimestampBlockIndexKey { + uint256 blockHash; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 32; + } + + template + void Serialize(Stream& s, int nType, int nVersion) const { + blockHash.Serialize(s, nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType, int nVersion) { + blockHash.Unserialize(s, nType, nVersion); + } + + CTimestampBlockIndexKey(uint256 hash) { + blockHash = hash; + } + + CTimestampBlockIndexKey() { + SetNull(); + } + + void SetNull() { + blockHash.SetNull(); + } +}; + +struct CTimestampBlockIndexValue { + unsigned int ltimestamp; + size_t GetSerializeSize(int nType, int nVersion) const { + return 4; + } + + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata32be(s, ltimestamp); + } + + template + void Unserialize(Stream& s, int nType, int nVersion) { + ltimestamp = ser_readdata32be(s); + } + + CTimestampBlockIndexValue (unsigned int time) { + ltimestamp = time; + } + + CTimestampBlockIndexValue() { + SetNull(); + } + + void SetNull() { + ltimestamp = 0; + } +}; + +struct CAddressUnspentKey { + unsigned int type; + uint160 hashBytes; + uint256 txhash; + size_t index; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 57; + } + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata8(s, type); + hashBytes.Serialize(s, nType, nVersion); + txhash.Serialize(s, nType, nVersion); + ser_writedata32(s, index); + } + template + void Unserialize(Stream& s, int nType, int nVersion) { + type = ser_readdata8(s); + hashBytes.Unserialize(s, nType, nVersion); + txhash.Unserialize(s, nType, nVersion); + index = ser_readdata32(s); + } + + CAddressUnspentKey(unsigned int addressType, uint160 addressHash, uint256 txid, size_t indexValue) { + type = addressType; + hashBytes = addressHash; + txhash = txid; + index = indexValue; + } + + CAddressUnspentKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + txhash.SetNull(); + index = 0; + } +}; + +struct CAddressUnspentValue { + CAmount satoshis; + CScript script; + int blockHeight; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(satoshis); + READWRITE(script); + READWRITE(blockHeight); + } + + CAddressUnspentValue(CAmount sats, CScript scriptPubKey, int height) { + satoshis = sats; + script = scriptPubKey; + blockHeight = height; + } + + CAddressUnspentValue() { + SetNull(); + } + + void SetNull() { + satoshis = -1; + script.clear(); + blockHeight = 0; + } + + bool IsNull() const { + return (satoshis == -1); + } +}; + +struct CAddressIndexKey { + unsigned int type; + uint160 hashBytes; + int blockHeight; + unsigned int txindex; + uint256 txhash; + size_t index; + bool spending; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 66; + } + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata8(s, type); + hashBytes.Serialize(s, nType, nVersion); + // Heights are stored big-endian for key sorting in LevelDB + ser_writedata32be(s, blockHeight); + ser_writedata32be(s, txindex); + txhash.Serialize(s, nType, nVersion); + ser_writedata32(s, index); + char f = spending; + ser_writedata8(s, f); + } + template + void Unserialize(Stream& s, int nType, int nVersion) { + type = ser_readdata8(s); + hashBytes.Unserialize(s, nType, nVersion); + blockHeight = ser_readdata32be(s); + txindex = ser_readdata32be(s); + txhash.Unserialize(s, nType, nVersion); + index = ser_readdata32(s); + char f = ser_readdata8(s); + spending = f; + } + + CAddressIndexKey(unsigned int addressType, uint160 addressHash, int height, int blockindex, + uint256 txid, size_t indexValue, bool isSpending) { + type = addressType; + hashBytes = addressHash; + blockHeight = height; + txindex = blockindex; + txhash = txid; + index = indexValue; + spending = isSpending; + } + + CAddressIndexKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + blockHeight = 0; + txindex = 0; + txhash.SetNull(); + index = 0; + spending = false; + } + +}; + +struct CAddressIndexIteratorKey { + unsigned int type; + uint160 hashBytes; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 21; + } + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata8(s, type); + hashBytes.Serialize(s, nType, nVersion); + } + template + void Unserialize(Stream& s, int nType, int nVersion) { + type = ser_readdata8(s); + hashBytes.Unserialize(s, nType, nVersion); + } + + CAddressIndexIteratorKey(unsigned int addressType, uint160 addressHash) { + type = addressType; + hashBytes = addressHash; + } + + CAddressIndexIteratorKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + } +}; + +struct CAddressIndexIteratorHeightKey { + unsigned int type; + uint160 hashBytes; + int blockHeight; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 25; + } + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata8(s, type); + hashBytes.Serialize(s, nType, nVersion); + ser_writedata32be(s, blockHeight); + } + template + void Unserialize(Stream& s, int nType, int nVersion) { + type = ser_readdata8(s); + hashBytes.Unserialize(s, nType, nVersion); + blockHeight = ser_readdata32be(s); + } + + CAddressIndexIteratorHeightKey(unsigned int addressType, uint160 addressHash, int height) { + type = addressType; + hashBytes = addressHash; + blockHeight = height; + } + + CAddressIndexIteratorHeightKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + blockHeight = 0; + } +}; + struct CDiskTxPos : public CDiskBlockPos { unsigned int nTxOffset; // after header @@ -431,6 +770,13 @@ public: ScriptError GetScriptError() const { return error; } }; +bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes); +bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); +bool GetAddressIndex(uint160 addressHash, int type, + std::vector > &addressIndex, + int start = 0, int end = 0); +bool GetAddressUnspent(uint160 addressHash, int type, + std::vector > &unspentOutputs); /** Functions for disk access for blocks */ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); @@ -447,7 +793,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); /** Apply the effects of this block (with given index) on the UTXO set represented by coins */ -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false,bool fCheckPOW = false); /** Context-independent validity checks */ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex,const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); diff --git a/src/miner.cpp b/src/miner.cpp index f95933117..176fb83a7 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -385,7 +385,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; blocktime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nTime = blocktime + 1; + //pblock->nTime = blocktime + 1; pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); //LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits); if ( ASSETCHAINS_SYMBOL[0] != 0 && ASSETCHAINS_STAKED != 0 && NOTARY_PUBKEY33[0] != 0 ) @@ -479,8 +479,11 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashReserved = uint256(); - //UpdateTime(pblock, Params().GetConsensus(), pindexPrev); - //pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + if ( ASSETCHAINS_SYMBOL[0] == 0 || ASSETCHAINS_STAKED == 0 || NOTARY_PUBKEY33[0] == 0 ) + { + UpdateTime(pblock, Params().GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + } pblock->nSolution.clear(); pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); @@ -488,8 +491,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) { static uint32_t counter; - if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) - fprintf(stderr,"warning: testblockvalidity failed\n"); + //if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) + fprintf(stderr,"warning: miner testblockvalidity failed\n"); return(0); } } @@ -914,6 +917,12 @@ void static BitcoinMiner() // fprintf(stderr," missed target\n"); return false; } + CValidationState state; + if ( !TestBlockValidity(state, *pblock, chainActive.Tip(), true, false)) + { + fprintf(stderr,"Invalid block mined, try again\n"); + return(false); + } if ( ASSETCHAINS_STAKED == 0 ) { if ( Mining_start != 0 && time(NULL) < Mining_start+roundrobin_delay ) @@ -931,12 +940,6 @@ void static BitcoinMiner() } else { - CValidationState state; - if ( !TestBlockValidity(state, *pblock, chainActive.Tip(), false, false)) - { - //fprintf(stderr,"Invalid block mined, try again\n"); - return(false); - } if ( NOTARY_PUBKEY33[0] != 0 ) { printf("need to wait %d seconds to submit\n",(int32_t)(pblock->nTime - GetAdjustedTime())); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index a2ac900d2..c850492b5 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -7,6 +7,7 @@ #include "chain.h" #include "chainparams.h" #include "checkpoints.h" +#include "base58.h" #include "consensus/validation.h" #include "cc/betprotocol.h" #include "main.h" @@ -14,6 +15,10 @@ #include "rpcserver.h" #include "sync.h" #include "util.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" #include @@ -124,6 +129,112 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) return result; } +UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hash", block.GetHash().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; + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); + } + result.push_back(Pair("confirmations", confirmations)); + result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); + result.push_back(Pair("height", blockindex->nHeight)); + result.push_back(Pair("version", block.nVersion)); + result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + + UniValue deltas(UniValue::VARR); + + for (unsigned int i = 0; i < block.vtx.size(); i++) { + const CTransaction &tx = block.vtx[i]; + const uint256 txhash = tx.GetHash(); + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", txhash.GetHex())); + entry.push_back(Pair("index", (int)i)); + + UniValue inputs(UniValue::VARR); + + if (!tx.IsCoinBase()) { + + for (size_t j = 0; j < tx.vin.size(); j++) { + const CTxIn input = tx.vin[j]; + + UniValue delta(UniValue::VOBJ); + + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(input.prevout.hash, input.prevout.n); + + if (GetSpentIndex(spentKey, spentInfo)) { + if (spentInfo.addressType == 1) { + delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); + } else if (spentInfo.addressType == 2) { + delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); + } else { + continue; + } + delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis)); + delta.push_back(Pair("index", (int)j)); + delta.push_back(Pair("prevtxid", input.prevout.hash.GetHex())); + delta.push_back(Pair("prevout", (int)input.prevout.n)); + + inputs.push_back(delta); + } else { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Spent information not available"); + } + + } + } + + entry.push_back(Pair("inputs", inputs)); + + UniValue outputs(UniValue::VARR); + + for (unsigned int k = 0; k < tx.vout.size(); k++) { + const CTxOut &out = tx.vout[k]; + + UniValue delta(UniValue::VOBJ); + + if (out.scriptPubKey.IsPayToScriptHash()) { + vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); + delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString())); + + } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); + } else { + continue; + } + + delta.push_back(Pair("satoshis", out.nValue)); + delta.push_back(Pair("index", (int)k)); + + outputs.push_back(delta); + } + + entry.push_back(Pair("outputs", outputs)); + deltas.push_back(entry); + + } + result.push_back(Pair("deltas", deltas)); + result.push_back(Pair("time", block.GetBlockTime())); + result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); + result.push_back(Pair("nonce", block.nNonce.GetHex())); + result.push_back(Pair("bits", strprintf("%08x", block.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) { UniValue result(UniValue::VOBJ); @@ -310,6 +421,102 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) return mempoolToJSON(fVerbose); } +UniValue getblockdeltas(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error(""); + + std::string strHash = params[0].get_str(); + uint256 hash(uint256S(strHash)); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, pblockindex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + return blockToDeltasJSON(block, pblockindex); +} + +UniValue getblockhashes(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2) + throw runtime_error( + "getblockhashes timestamp\n" + "\nReturns array of hashes of blocks within the timestamp range provided.\n" + "\nArguments:\n" + "1. high (numeric, required) The newer block timestamp\n" + "2. low (numeric, required) The older block timestamp\n" + "3. options (string, required) A json object\n" + " {\n" + " \"noOrphans\":true (boolean) will only include blocks on the main chain\n" + " \"logicalTimes\":true (boolean) will include logical timestamps with hashes\n" + " }\n" + "\nResult:\n" + "[\n" + " \"hash\" (string) The block hash\n" + "]\n" + "[\n" + " {\n" + " \"blockhash\": (string) The block hash\n" + " \"logicalts\": (numeric) The logical timestamp\n" + " }\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getblockhashes", "1231614698 1231024505") + + HelpExampleRpc("getblockhashes", "1231614698, 1231024505") + + HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'") + ); + + unsigned int high = params[0].get_int(); + unsigned int low = params[1].get_int(); + bool fActiveOnly = false; + bool fLogicalTS = false; + + if (params.size() > 2) { + if (params[2].isObject()) { + UniValue noOrphans = find_value(params[2].get_obj(), "noOrphans"); + UniValue returnLogical = find_value(params[2].get_obj(), "logicalTimes"); + + if (noOrphans.isBool()) + fActiveOnly = noOrphans.get_bool(); + + if (returnLogical.isBool()) + fLogicalTS = returnLogical.get_bool(); + } + } + + std::vector > blockHashes; + + if (fActiveOnly) + LOCK(cs_main); + + if (!GetTimestampIndex(high, low, fActiveOnly, blockHashes)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes"); + } + + UniValue result(UniValue::VARR); + + for (std::vector >::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { + if (fLogicalTS) { + UniValue item(UniValue::VOBJ); + item.push_back(Pair("blockhash", it->first.GetHex())); + item.push_back(Pair("logicalts", (int)it->second)); + result.push_back(item); + } else { + result.push_back(it->first.GetHex()); + } + } + + return result; +} + UniValue getblockhash(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 6a83e8c19..9eb731b1c 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -96,6 +96,15 @@ static const CRPCConvertParam vRPCConvertParams[] = { "prioritisetransaction", 2 }, { "setban", 2 }, { "setban", 3 }, + { "getblockhashes", 0 }, + { "getblockhashes", 1 }, + { "getblockhashes", 2 }, + { "getspentinfo", 0}, + { "getaddresstxids", 0}, + { "getaddressbalance", 0}, + { "getaddressdeltas", 0}, + { "getaddressutxos", 0}, + { "getaddressmempool", 0}, { "zcrawjoinsplit", 1 }, { "zcrawjoinsplit", 2 }, { "zcrawjoinsplit", 3 }, diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 1159c12c1..20ea27f0a 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -11,6 +11,7 @@ #include "netbase.h" #include "rpcserver.h" #include "timedata.h" +#include "txmempool.h" #include "util.h" #ifdef ENABLE_WALLET #include "wallet/wallet.h" @@ -557,3 +558,530 @@ UniValue setmocktime(const UniValue& params, bool fHelp) return NullUniValue; } + +bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &address) +{ + if (type == 2) { + address = CBitcoinAddress(CScriptID(hash)).ToString(); + } else if (type == 1) { + address = CBitcoinAddress(CKeyID(hash)).ToString(); + } else { + return false; + } + return true; +} + +bool getAddressesFromParams(const UniValue& params, std::vector > &addresses) +{ + if (params[0].isStr()) { + CBitcoinAddress address(params[0].get_str()); + uint160 hashBytes; + int type = 0; + if (!address.GetIndexKey(hashBytes, type)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + addresses.push_back(std::make_pair(hashBytes, type)); + } else if (params[0].isObject()) { + + UniValue addressValues = find_value(params[0].get_obj(), "addresses"); + if (!addressValues.isArray()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Addresses is expected to be an array"); + } + + std::vector values = addressValues.getValues(); + + for (std::vector::iterator it = values.begin(); it != values.end(); ++it) { + + CBitcoinAddress address(it->get_str()); + uint160 hashBytes; + int type = 0; + if (!address.GetIndexKey(hashBytes, type)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + addresses.push_back(std::make_pair(hashBytes, type)); + } + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + return true; +} + +bool heightSort(std::pair a, + std::pair b) { + return a.second.blockHeight < b.second.blockHeight; +} + +bool timestampSort(std::pair a, + std::pair b) { + return a.second.time < b.second.time; +} + +UniValue getaddressmempool(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressmempool\n" + "\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n" + "\nArguments:\n" + "{\n" + " \"addresses\"\n" + " [\n" + " \"address\" (string) The base58check encoded address\n" + " ,...\n" + " ]\n" + "}\n" + "\nResult:\n" + "[\n" + " {\n" + " \"address\" (string) The base58check encoded address\n" + " \"txid\" (string) The related txid\n" + " \"index\" (number) The related input or output index\n" + " \"satoshis\" (number) The difference of satoshis\n" + " \"timestamp\" (number) The time the transaction entered the mempool (seconds)\n" + " \"prevtxid\" (string) The previous txid (if spending)\n" + " \"prevout\" (string) The previous transaction output index (if spending)\n" + " }\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + ); + + std::vector > addresses; + + if (!getAddressesFromParams(params, addresses)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::vector > indexes; + + if (!mempool.getAddressIndex(addresses, indexes)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + + std::sort(indexes.begin(), indexes.end(), timestampSort); + + UniValue result(UniValue::VARR); + + for (std::vector >::iterator it = indexes.begin(); + it != indexes.end(); it++) { + + std::string address; + if (!getAddressFromIndex(it->first.type, it->first.addressBytes, address)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); + } + + UniValue delta(UniValue::VOBJ); + delta.push_back(Pair("address", address)); + delta.push_back(Pair("txid", it->first.txhash.GetHex())); + delta.push_back(Pair("index", (int)it->first.index)); + delta.push_back(Pair("satoshis", it->second.amount)); + delta.push_back(Pair("timestamp", it->second.time)); + if (it->second.amount < 0) { + delta.push_back(Pair("prevtxid", it->second.prevhash.GetHex())); + delta.push_back(Pair("prevout", (int)it->second.prevout)); + } + result.push_back(delta); + } + + return result; +} + +UniValue getaddressutxos(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressutxos\n" + "\nReturns all unspent outputs for an address (requires addressindex to be enabled).\n" + "\nArguments:\n" + "{\n" + " \"addresses\"\n" + " [\n" + " \"address\" (string) The base58check encoded address\n" + " ,...\n" + " ],\n" + " \"chainInfo\" (boolean) Include chain info with results\n" + "}\n" + "\nResult\n" + "[\n" + " {\n" + " \"address\" (string) The address base58check encoded\n" + " \"txid\" (string) The output txid\n" + " \"height\" (number) The block height\n" + " \"outputIndex\" (number) The output index\n" + " \"script\" (strin) The script hex encoded\n" + " \"satoshis\" (number) The number of satoshis of the output\n" + " }\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + ); + + bool includeChainInfo = false; + if (params[0].isObject()) { + UniValue chainInfo = find_value(params[0].get_obj(), "chainInfo"); + if (chainInfo.isBool()) { + includeChainInfo = chainInfo.get_bool(); + } + } + + std::vector > addresses; + + if (!getAddressesFromParams(params, addresses)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::vector > unspentOutputs; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (!GetAddressUnspent((*it).first, (*it).second, unspentOutputs)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } + + std::sort(unspentOutputs.begin(), unspentOutputs.end(), heightSort); + + UniValue utxos(UniValue::VARR); + + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { + UniValue output(UniValue::VOBJ); + std::string address; + if (!getAddressFromIndex(it->first.type, it->first.hashBytes, address)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); + } + + output.push_back(Pair("address", address)); + output.push_back(Pair("txid", it->first.txhash.GetHex())); + output.push_back(Pair("outputIndex", (int)it->first.index)); + output.push_back(Pair("script", HexStr(it->second.script.begin(), it->second.script.end()))); + output.push_back(Pair("satoshis", it->second.satoshis)); + output.push_back(Pair("height", it->second.blockHeight)); + utxos.push_back(output); + } + + if (includeChainInfo) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("utxos", utxos)); + + LOCK(cs_main); + result.push_back(Pair("hash", chainActive.Tip()->GetBlockHash().GetHex())); + result.push_back(Pair("height", (int)chainActive.Height())); + return result; + } else { + return utxos; + } +} + +UniValue getaddressdeltas(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1 || !params[0].isObject()) + throw runtime_error( + "getaddressdeltas\n" + "\nReturns all changes for an address (requires addressindex to be enabled).\n" + "\nArguments:\n" + "{\n" + " \"addresses\"\n" + " [\n" + " \"address\" (string) The base58check encoded address\n" + " ,...\n" + " ]\n" + " \"start\" (number) The start block height\n" + " \"end\" (number) The end block height\n" + " \"chainInfo\" (boolean) Include chain info in results, only applies if start and end specified\n" + "}\n" + "\nResult:\n" + "[\n" + " {\n" + " \"satoshis\" (number) The difference of satoshis\n" + " \"txid\" (string) The related txid\n" + " \"index\" (number) The related input or output index\n" + " \"height\" (number) The block height\n" + " \"address\" (string) The base58check encoded address\n" + " }\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + ); + + + UniValue startValue = find_value(params[0].get_obj(), "start"); + UniValue endValue = find_value(params[0].get_obj(), "end"); + + UniValue chainInfo = find_value(params[0].get_obj(), "chainInfo"); + bool includeChainInfo = false; + if (chainInfo.isBool()) { + includeChainInfo = chainInfo.get_bool(); + } + + int start = 0; + int end = 0; + + if (startValue.isNum() && endValue.isNum()) { + start = startValue.get_int(); + end = endValue.get_int(); + if (start <= 0 || end <= 0) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start and end is expected to be greater than zero"); + } + if (end < start) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "End value is expected to be greater than start"); + } + } + + std::vector > addresses; + + if (!getAddressesFromParams(params, addresses)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::vector > addressIndex; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (start > 0 && end > 0) { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } else { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } + } + + UniValue deltas(UniValue::VARR); + + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { + std::string address; + if (!getAddressFromIndex(it->first.type, it->first.hashBytes, address)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); + } + + UniValue delta(UniValue::VOBJ); + delta.push_back(Pair("satoshis", it->second)); + delta.push_back(Pair("txid", it->first.txhash.GetHex())); + delta.push_back(Pair("index", (int)it->first.index)); + delta.push_back(Pair("blockindex", (int)it->first.txindex)); + delta.push_back(Pair("height", it->first.blockHeight)); + delta.push_back(Pair("address", address)); + deltas.push_back(delta); + } + + UniValue result(UniValue::VOBJ); + + if (includeChainInfo && start > 0 && end > 0) { + LOCK(cs_main); + + if (start > chainActive.Height() || end > chainActive.Height()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start or end is outside chain range"); + } + + CBlockIndex* startIndex = chainActive[start]; + CBlockIndex* endIndex = chainActive[end]; + + UniValue startInfo(UniValue::VOBJ); + UniValue endInfo(UniValue::VOBJ); + + startInfo.push_back(Pair("hash", startIndex->GetBlockHash().GetHex())); + startInfo.push_back(Pair("height", start)); + + endInfo.push_back(Pair("hash", endIndex->GetBlockHash().GetHex())); + endInfo.push_back(Pair("height", end)); + + result.push_back(Pair("deltas", deltas)); + result.push_back(Pair("start", startInfo)); + result.push_back(Pair("end", endInfo)); + + return result; + } else { + return deltas; + } +} + +UniValue getaddressbalance(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressbalance\n" + "\nReturns the balance for an address(es) (requires addressindex to be enabled).\n" + "\nArguments:\n" + "{\n" + " \"addresses\"\n" + " [\n" + " \"address\" (string) The base58check encoded address\n" + " ,...\n" + " ]\n" + "}\n" + "\nResult:\n" + "{\n" + " \"balance\" (string) The current balance in satoshis\n" + " \"received\" (string) The total number of satoshis received (including change)\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + ); + + std::vector > addresses; + + if (!getAddressesFromParams(params, addresses)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::vector > addressIndex; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } + + CAmount balance = 0; + CAmount received = 0; + + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { + if (it->second > 0) { + received += it->second; + } + balance += it->second; + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("balance", balance)); + result.push_back(Pair("received", received)); + + return result; + +} + +UniValue getaddresstxids(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddresstxids\n" + "\nReturns the txids for an address(es) (requires addressindex to be enabled).\n" + "\nArguments:\n" + "{\n" + " \"addresses\"\n" + " [\n" + " \"address\" (string) The base58check encoded address\n" + " ,...\n" + " ]\n" + " \"start\" (number) The start block height\n" + " \"end\" (number) The end block height\n" + "}\n" + "\nResult:\n" + "[\n" + " \"transactionid\" (string) The transaction id\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'") + + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}") + ); + + std::vector > addresses; + + if (!getAddressesFromParams(params, addresses)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + int start = 0; + int end = 0; + if (params[0].isObject()) { + UniValue startValue = find_value(params[0].get_obj(), "start"); + UniValue endValue = find_value(params[0].get_obj(), "end"); + if (startValue.isNum() && endValue.isNum()) { + start = startValue.get_int(); + end = endValue.get_int(); + } + } + + std::vector > addressIndex; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (start > 0 && end > 0) { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } else { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } + } + + std::set > txids; + UniValue result(UniValue::VARR); + + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { + int height = it->first.blockHeight; + std::string txid = it->first.txhash.GetHex(); + + if (addresses.size() > 1) { + txids.insert(std::make_pair(height, txid)); + } else { + if (txids.insert(std::make_pair(height, txid)).second) { + result.push_back(txid); + } + } + } + + if (addresses.size() > 1) { + for (std::set >::const_iterator it=txids.begin(); it!=txids.end(); it++) { + result.push_back(it->second); + } + } + + return result; + +} + +UniValue getspentinfo(const UniValue& params, bool fHelp) +{ + + if (fHelp || params.size() != 1 || !params[0].isObject()) + throw runtime_error( + "getspentinfo\n" + "\nReturns the txid and index where an output is spent.\n" + "\nArguments:\n" + "{\n" + " \"txid\" (string) The hex string of the txid\n" + " \"index\" (number) The start block height\n" + "}\n" + "\nResult:\n" + "{\n" + " \"txid\" (string) The transaction id\n" + " \"index\" (number) The spending input index\n" + " ,...\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getspentinfo", "'{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}'") + + HelpExampleRpc("getspentinfo", "{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}") + ); + + UniValue txidValue = find_value(params[0].get_obj(), "txid"); + UniValue indexValue = find_value(params[0].get_obj(), "index"); + + if (!txidValue.isStr() || !indexValue.isNum()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid or index"); + } + + uint256 txid = ParseHashV(txidValue, "txid"); + int outputIndex = indexValue.get_int(); + + CSpentIndexKey key(txid, outputIndex); + CSpentIndexValue value; + + if (!GetSpentIndex(key, value)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get spent info"); + } + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("txid", value.txid.GetHex())); + obj.push_back(Pair("index", (int)value.inputIndex)); + obj.push_back(Pair("height", value.blockHeight)); + + return obj; +} \ No newline at end of file diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index d2798f10c..a7a4bf051 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -165,6 +165,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) return ret; } +int32_t KOMODO_LONGESTCHAIN; int32_t komodo_longestchain() { int32_t ht,n=0,num=0,maxheight=0,height = 0; @@ -191,8 +192,12 @@ int32_t komodo_longestchain() height = ht; } if ( num > (n >> 1) ) + { + KOMODO_LONGESTCHAIN = height; return(height); - else return(0); + } + KOMODO_LONGESTCHAIN = 0; + return(0); } UniValue addnode(const UniValue& params, bool fHelp) diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index e918df2c9..3f93e07d6 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -65,7 +65,9 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) { UniValue joinsplit(UniValue::VOBJ); joinsplit.push_back(Pair("vpub_old", ValueFromAmount(jsdescription.vpub_old))); + joinsplit.push_back(Pair("vpub_oldZat", jsdescription.vpub_old)); joinsplit.push_back(Pair("vpub_new", ValueFromAmount(jsdescription.vpub_new))); + joinsplit.push_back(Pair("vpub_newZat", jsdescription.vpub_new)); joinsplit.push_back(Pair("anchor", jsdescription.anchor.GetHex())); @@ -115,9 +117,10 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) { uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); -void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) +void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, int nHeight = 0, int nConfirmations = 0, int nBlockTime = 0) { - entry.push_back(Pair("txid", tx.GetHash().GetHex())); + uint256 txid = tx.GetHash(); + entry.push_back(Pair("txid", txid.GetHex())); entry.push_back(Pair("overwintered", tx.fOverwintered)); entry.push_back(Pair("version", tx.nVersion)); if (tx.fOverwintered) { @@ -139,6 +142,19 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) o.push_back(Pair("asm", txin.scriptSig.ToString())); o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); in.push_back(Pair("scriptSig", o)); + + // Add address and value info if spentindex enabled + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(txin.prevout.hash, txin.prevout.n); + if (GetSpentIndex(spentKey, spentInfo)) { + in.push_back(Pair("value", ValueFromAmount(spentInfo.satoshis))); + in.push_back(Pair("valueSat", spentInfo.satoshis)); + if (spentInfo.addressType == 1) { + in.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); + } else if (spentInfo.addressType == 2) { + in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); + } + } } in.push_back(Pair("sequence", (int64_t)txin.nSequence)); vin.push_back(in); @@ -162,6 +178,16 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(txout.scriptPubKey, o, true); out.push_back(Pair("scriptPubKey", o)); + + // Add spent information if spentindex is enabled + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(txid, i); + if (GetSpentIndex(spentKey, spentInfo)) { + out.push_back(Pair("spentTxId", spentInfo.txid.GetHex())); + out.push_back(Pair("spentIndex", (int)spentInfo.inputIndex)); + out.push_back(Pair("spentHeight", spentInfo.blockHeight)); + } + vout.push_back(out); } entry.push_back(Pair("vout", vout)); @@ -169,18 +195,76 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) UniValue vjoinsplit = TxJoinSplitToJSON(tx); entry.push_back(Pair("vjoinsplit", vjoinsplit)); + if (!hashBlock.IsNull()) { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + + if (nConfirmations > 0) { + entry.push_back(Pair("height", nHeight)); + entry.push_back(Pair("confirmations", nConfirmations)); + entry.push_back(Pair("time", nBlockTime)); + entry.push_back(Pair("blocktime", nBlockTime)); + } else { + entry.push_back(Pair("height", -1)); + entry.push_back(Pair("confirmations", 0)); + } + } + +} + +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) +{ + uint256 txid = tx.GetHash(); + entry.push_back(Pair("txid", txid.GetHex())); + entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); + entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + + UniValue vin(UniValue::VARR); + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + UniValue in(UniValue::VOBJ); + if (tx.IsCoinBase()) + in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + else { + in.push_back(Pair("txid", txin.prevout.hash.GetHex())); + in.push_back(Pair("vout", (int64_t)txin.prevout.n)); + UniValue o(UniValue::VOBJ); + o.push_back(Pair("asm", txin.scriptSig.ToString())); + o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + in.push_back(Pair("scriptSig", o)); + } + in.push_back(Pair("sequence", (int64_t)txin.nSequence)); + vin.push_back(in); + } + entry.push_back(Pair("vin", vin)); + + UniValue vout(UniValue::VARR); + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut& txout = tx.vout[i]; + UniValue out(UniValue::VOBJ); + out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + out.push_back(Pair("valueSat", txout.nValue)); + out.push_back(Pair("n", (int64_t)i)); + UniValue o(UniValue::VOBJ); + ScriptPubKeyToJSON(txout.scriptPubKey, o, true); + out.push_back(Pair("scriptPubKey", o)); + vout.push_back(out); + } + entry.push_back(Pair("vout", vout)); + if (!hashBlock.IsNull()) { entry.push_back(Pair("blockhash", hashBlock.GetHex())); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; if (chainActive.Contains(pindex)) { + entry.push_back(Pair("height", pindex->nHeight)); entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); entry.push_back(Pair("time", pindex->GetBlockTime())); entry.push_back(Pair("blocktime", pindex->GetBlockTime())); - } - else + } else { + entry.push_back(Pair("height", -1)); entry.push_back(Pair("confirmations", 0)); + } } } } @@ -279,7 +363,6 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") ); - LOCK(cs_main); uint256 hash = ParseHashV(params[0], "parameter 1"); @@ -289,8 +372,29 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) CTransaction tx; uint256 hashBlock; - if (!GetTransaction(hash, tx, hashBlock, true)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + int nHeight = 0; + int nConfirmations = 0; + int nBlockTime = 0; + + { + LOCK(cs_main); + if (!GetTransaction(hash, tx, hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) { + nHeight = pindex->nHeight; + nConfirmations = 1 + chainActive.Height() - pindex->nHeight; + nBlockTime = pindex->GetBlockTime(); + } else { + nHeight = -1; + nConfirmations = 0; + nBlockTime = pindex->GetBlockTime(); + } + } + } string strHex = EncodeHexTx(tx); @@ -299,7 +403,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", strHex)); - TxToJSON(tx, hashBlock, result); + TxToJSONExpanded(tx, hashBlock, result, nHeight, nConfirmations, nBlockTime); return result; } diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 5d573f62b..bac3adc97 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -281,6 +281,8 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "getbestblockhash", &getbestblockhash, true }, { "blockchain", "getblockcount", &getblockcount, true }, { "blockchain", "getblock", &getblock, true }, + { "blockchain", "getblockdeltas", &getblockdeltas, false }, + { "blockchain", "getblockhashes", &getblockhashes, true }, { "blockchain", "getblockhash", &getblockhash, true }, { "blockchain", "getblockheader", &getblockheader, true }, { "blockchain", "getchaintips", &getchaintips, true }, @@ -292,6 +294,7 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "verifytxoutproof", &verifytxoutproof, true }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, { "blockchain", "verifychain", &verifychain, true }, + { "blockchain", "getspentinfo", &getspentinfo, false }, { "blockchain", "paxprice", &paxprice, true }, { "blockchain", "paxpending", &paxpending, true }, { "blockchain", "paxprices", &paxprices, true }, @@ -330,6 +333,13 @@ static const CRPCCommand vRPCCommands[] = { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false }, #endif +/* Address index */ + { "addressindex", "getaddressmempool", &getaddressmempool, true }, + { "addressindex", "getaddressutxos", &getaddressutxos, false }, + { "addressindex", "getaddressdeltas", &getaddressdeltas, false }, + { "addressindex", "getaddresstxids", &getaddresstxids, false }, + { "addressindex", "getaddressbalance", &getaddressbalance, false }, + /* Utility functions */ { "util", "createmultisig", &createmultisig, true }, { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ diff --git a/src/rpcserver.h b/src/rpcserver.h index b4e6ed8fc..7766e6a31 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -173,6 +173,11 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri extern void EnsureWalletIsUnlocked(); extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp +extern UniValue getaddressmempool(const UniValue& params, bool fHelp); +extern UniValue getaddressutxos(const UniValue& params, bool fHelp); +extern UniValue getaddressdeltas(const UniValue& params, bool fHelp); +extern UniValue getaddresstxids(const UniValue& params, bool fHelp); +extern UniValue getaddressbalance(const UniValue& params, bool fHelp); extern UniValue getpeerinfo(const UniValue& params, bool fHelp); extern UniValue ping(const UniValue& params, bool fHelp); extern UniValue addnode(const UniValue& params, bool fHelp); @@ -271,6 +276,8 @@ extern UniValue getdifficulty(const UniValue& params, bool fHelp); 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 getblockhashes(const UniValue& params, bool fHelp); +extern UniValue getblockdeltas(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); @@ -280,6 +287,7 @@ extern UniValue verifychain(const UniValue& params, bool fHelp); extern UniValue getchaintips(const UniValue& params, bool fHelp); extern UniValue invalidateblock(const UniValue& params, bool fHelp); extern UniValue reconsiderblock(const UniValue& params, bool fHelp); +extern UniValue getspentinfo(const UniValue& params, bool fHelp); extern UniValue getblocksubsidy(const UniValue& params, bool fHelp); diff --git a/src/script/cc.h b/src/script/cc.h index ad1666b86..dc019beba 100644 --- a/src/script/cc.h +++ b/src/script/cc.h @@ -6,7 +6,7 @@ #include "cryptoconditions/include/cryptoconditions.h" -extern int32_t ASSETCHAINS_CC; +extern uint32_t ASSETCHAINS_CC; bool IsCryptoConditionsEnabled(); // Limit acceptable condition types diff --git a/src/script/script.cpp b/src/script/script.cpp index df29b6244..a130df4aa 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -216,6 +216,17 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const return subscript.GetSigOpCount(true); } +bool CScript::IsPayToPublicKeyHash() const +{ + // Extra-fast test for pay-to-pubkey-hash CScripts: + return (this->size() == 25 && + (*this)[0] == OP_DUP && + (*this)[1] == OP_HASH160 && + (*this)[2] == 0x14 && + (*this)[23] == OP_EQUALVERIFY && + (*this)[24] == OP_CHECKSIG); +} + bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: diff --git a/src/script/script.h b/src/script/script.h index 6b8c07b07..e5328cf06 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -566,6 +566,8 @@ public: */ unsigned int GetSigOpCount(const CScript& scriptSig) const; + bool IsPayToPublicKeyHash() const; + bool IsPayToScriptHash() const; bool IsPayToCryptoCondition() const; bool MayAcceptCryptoCondition() const; diff --git a/src/serialize.h b/src/serialize.h index f776d6d99..4501103c9 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -95,6 +95,11 @@ template inline void ser_writedata32(Stream &s, uint32_t obj) obj = htole32(obj); s.write((char*)&obj, 4); } +template inline void ser_writedata32be(Stream &s, uint32_t obj) +{ + obj = htobe32(obj); + s.write((char*)&obj, 4); +} template inline void ser_writedata64(Stream &s, uint64_t obj) { obj = htole64(obj); @@ -118,6 +123,12 @@ template inline uint32_t ser_readdata32(Stream &s) s.read((char*)&obj, 4); return le32toh(obj); } +template inline uint32_t ser_readdata32be(Stream &s) +{ + uint32_t obj; + s.read((char*)&obj, 4); + return be32toh(obj); +} template inline uint64_t ser_readdata64(Stream &s) { uint64_t obj; diff --git a/src/spentindex.h b/src/spentindex.h new file mode 100644 index 000000000..310ef188b --- /dev/null +++ b/src/spentindex.h @@ -0,0 +1,98 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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. + +#ifndef BITCOIN_SPENTINDEX_H +#define BITCOIN_SPENTINDEX_H + +#include "uint256.h" +#include "amount.h" + +struct CSpentIndexKey { + uint256 txid; + unsigned int outputIndex; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(txid); + READWRITE(outputIndex); + } + + CSpentIndexKey(uint256 t, unsigned int i) { + txid = t; + outputIndex = i; + } + + CSpentIndexKey() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + outputIndex = 0; + } + +}; + +struct CSpentIndexValue { + uint256 txid; + unsigned int inputIndex; + int blockHeight; + CAmount satoshis; + int addressType; + uint160 addressHash; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(txid); + READWRITE(inputIndex); + READWRITE(blockHeight); + READWRITE(satoshis); + READWRITE(addressType); + READWRITE(addressHash); + } + + CSpentIndexValue(uint256 t, unsigned int i, int h, CAmount s, int type, uint160 a) { + txid = t; + inputIndex = i; + blockHeight = h; + satoshis = s; + addressType = type; + addressHash = a; + } + + CSpentIndexValue() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + inputIndex = 0; + blockHeight = 0; + satoshis = 0; + addressType = 0; + addressHash.SetNull(); + } + + bool IsNull() const { + return txid.IsNull(); + } +}; + +struct CSpentIndexKeyCompare +{ + bool operator()(const CSpentIndexKey& a, const CSpentIndexKey& b) const { + if (a.txid == b.txid) { + return a.outputIndex < b.outputIndex; + } else { + return a.txid < b.txid; + } + } +}; + +#endif // BITCOIN_SPENTINDEX_H \ No newline at end of file diff --git a/src/test/script_P2PKH_tests.cpp b/src/test/script_P2PKH_tests.cpp new file mode 100644 index 000000000..8fbb054b9 --- /dev/null +++ b/src/test/script_P2PKH_tests.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2012-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. + +#include "script/script.h" +#include "test/test_bitcoin.h" + +#include + +using namespace std; + +BOOST_FIXTURE_TEST_SUITE(script_P2PKH_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(IsPayToPublicKeyHash) +{ + // Test CScript::IsPayToPublicKeyHash() + uint160 dummy; + CScript p2pkh; + p2pkh << OP_DUP << OP_HASH160 << ToByteVector(dummy) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(p2pkh.IsPayToPublicKeyHash()); + + static const unsigned char direct[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG + }; + BOOST_CHECK(CScript(direct, direct+sizeof(direct)).IsPayToPublicKeyHash()); + + static const unsigned char notp2pkh1[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG + }; + BOOST_CHECK(!CScript(notp2pkh1, notp2pkh1+sizeof(notp2pkh1)).IsPayToPublicKeyHash()); + + static const unsigned char p2sh[] = { + OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL + }; + BOOST_CHECK(!CScript(p2sh, p2sh+sizeof(p2sh)).IsPayToPublicKeyHash()); + + static const unsigned char extra[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG + }; + BOOST_CHECK(!CScript(extra, extra+sizeof(extra)).IsPayToPublicKeyHash()); + + static const unsigned char missing[] = { + OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_RETURN + }; + BOOST_CHECK(!CScript(missing, missing+sizeof(missing)).IsPayToPublicKeyHash()); + + static const unsigned char missing2[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }; + BOOST_CHECK(!CScript(missing2, missing2+sizeof(missing)).IsPayToPublicKeyHash()); + + static const unsigned char tooshort[] = { + OP_DUP, OP_HASH160, 2, 0,0, OP_EQUALVERIFY, OP_CHECKSIG + }; + BOOST_CHECK(!CScript(tooshort, tooshort+sizeof(direct)).IsPayToPublicKeyHash()); + +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 02a3a50d2..63f8ab0f4 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -11,6 +11,7 @@ #include "key.h" #include "main.h" #include "random.h" +#include "chainparamsbase.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" diff --git a/src/test/wallet-utility.py b/src/test/wallet-utility.py new file mode 100644 index 000000000..c0a7bfb9e --- /dev/null +++ b/src/test/wallet-utility.py @@ -0,0 +1,61 @@ +#!/usr/bin/python +# Copyright 2014 BitPay, Inc. +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import subprocess +import os +import json +import sys +import buildenv +import shutil + +def assert_equal(thing1, thing2): + if thing1 != thing2: + raise AssertionError("%s != %s"%(str(thing1),str(thing2))) + +if __name__ == '__main__': + datadir = os.environ["srcdir"] + "/test/data" + execprog = './wallet-utility' + buildenv.exeext + execargs = '-datadir=' + datadir + execrun = execprog + ' ' + execargs + + proc = subprocess.Popen(execrun, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) + try: + outs = proc.communicate() + except OSError: + print("OSError, Failed to execute " + execprog) + sys.exit(1) + + output = json.loads(outs[0]) + + assert_equal(output[0], "13EngsxkRi7SJPPqCyJsKf34U8FoX9E9Av") + assert_equal(output[1], "1FKCLGTpPeYBUqfNxktck8k5nqxB8sjim8") + assert_equal(output[2], "13cdtE9tnNeXCZJ8KQ5WELgEmLSBLnr48F") + + execargs = '-datadir=' + datadir + ' -dumppass' + execrun = execprog + ' ' + execargs + + proc = subprocess.Popen(execrun, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) + try: + outs = proc.communicate() + except OSError: + print("OSError, Failed to execute " + execprog) + sys.exit(1) + + output = json.loads(outs[0]) + + assert_equal(output[0]['addr'], "13EngsxkRi7SJPPqCyJsKf34U8FoX9E9Av") + assert_equal(output[0]['pkey'], "5Jz5BWE2WQxp1hGqDZeisQFV1mRFR2AVBAgiXCbNcZyXNjD9aUd") + assert_equal(output[1]['addr'], "1FKCLGTpPeYBUqfNxktck8k5nqxB8sjim8") + assert_equal(output[1]['pkey'], "5HsX2b3v2GjngYQ5ZM4mLp2b2apw6aMNVaPELV1YmpiYR1S4jzc") + assert_equal(output[2]['addr'], "13cdtE9tnNeXCZJ8KQ5WELgEmLSBLnr48F") + assert_equal(output[2]['pkey'], "5KCWAs1wX2ESiL4PfDR8XYVSSETHFd2jaRGxt1QdanBFTit4XcH") + + if os.path.exists(datadir + '/database'): + if os.path.isdir(datadir + '/database'): + shutil.rmtree(datadir + '/database') + + if os.path.exists(datadir + '/db.log'): + os.remove(datadir + '/db.log') + sys.exit(0) \ No newline at end of file diff --git a/src/txdb.cpp b/src/txdb.cpp index f1adb11be..f37f66daf 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -22,6 +22,11 @@ static const char DB_NULLIFIER = 's'; static const char DB_COINS = 'c'; static const char DB_BLOCK_FILES = 'f'; static const char DB_TXINDEX = 't'; +static const char DB_ADDRESSINDEX = 'd'; +static const char DB_ADDRESSUNSPENTINDEX = 'u'; +static const char DB_TIMESTAMPINDEX = 'S'; +static const char DB_BLOCKHASHINDEX = 'z'; +static const char DB_SPENTINDEX = 'p'; static const char DB_BLOCK_INDEX = 'b'; static const char DB_BEST_BLOCK = 'B'; @@ -68,7 +73,7 @@ void static BatchWriteHashBestAnchor(CLevelDBBatch &batch, const uint256 &hash) CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { } -CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, false, 64) { } @@ -158,7 +163,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, return db.WriteBatch(batch); } -CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { +CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe, bool compression, int maxOpenFiles) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe, compression, maxOpenFiles) { } bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { @@ -260,6 +265,197 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector return WriteBatch(batch); } +bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) { + return Read(make_pair(DB_SPENTINDEX, key), value); +} + +bool CBlockTreeDB::UpdateSpentIndex(const std::vector >&vect) { + CLevelDBBatch batch; + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) { + if (it->second.IsNull()) { + batch.Erase(make_pair(DB_SPENTINDEX, it->first)); + } else { + batch.Write(make_pair(DB_SPENTINDEX, it->first), it->second); + } + } + return WriteBatch(batch); +} + +bool CBlockTreeDB::UpdateAddressUnspentIndex(const std::vector >&vect) { + CLevelDBBatch batch; + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) { + if (it->second.IsNull()) { + batch.Erase(make_pair(DB_ADDRESSUNSPENTINDEX, it->first)); + } else { + batch.Write(make_pair(DB_ADDRESSUNSPENTINDEX, it->first), it->second); + } + } + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadAddressUnspentIndex(uint160 addressHash, int type, + std::vector > &unspentOutputs) { + + boost::scoped_ptr pcursor(NewIterator()); + + CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); + ssKeySet << make_pair(DB_ADDRESSUNSPENTINDEX, CAddressIndexIteratorKey(type, addressHash)); + pcursor->Seek(ssKeySet.str()); + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + CAddressUnspentKey indexKey; + ssKey >> chType; + ssKey >> indexKey; + if (chType == DB_ADDRESSUNSPENTINDEX && indexKey.hashBytes == addressHash) { + try { + leveldb::Slice slValue = pcursor->value(); + CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); + CAddressUnspentValue nValue; + ssValue >> nValue; + unspentOutputs.push_back(make_pair(indexKey, nValue)); + pcursor->Next(); + } catch (const std::exception& e) { + return error("failed to get address unspent value"); + } + } else { + break; + } + } catch (const std::exception& e) { + break; + } + } + + return true; +} + +bool CBlockTreeDB::WriteAddressIndex(const std::vector >&vect) { + CLevelDBBatch batch; + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Write(make_pair(DB_ADDRESSINDEX, it->first), it->second); + return WriteBatch(batch); +} + +bool CBlockTreeDB::EraseAddressIndex(const std::vector >&vect) { + CLevelDBBatch batch; + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Erase(make_pair(DB_ADDRESSINDEX, it->first)); + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, + std::vector > &addressIndex, + int start, int end) { + + boost::scoped_ptr pcursor(NewIterator()); + + CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); + if (start > 0 && end > 0) { + ssKeySet << make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, addressHash, start)); + } else { + ssKeySet << make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorKey(type, addressHash)); + } + pcursor->Seek(ssKeySet.str()); + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + CAddressIndexKey indexKey; + ssKey >> chType; + ssKey >> indexKey; + if (chType == DB_ADDRESSINDEX && indexKey.hashBytes == addressHash) { + if (end > 0 && indexKey.blockHeight > end) { + break; + } + try { + leveldb::Slice slValue = pcursor->value(); + CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); + CAmount nValue; + ssValue >> nValue; + + addressIndex.push_back(make_pair(indexKey, nValue)); + pcursor->Next(); + } catch (const std::exception& e) { + return error("failed to get address index value"); + } + } else { + break; + } + } catch (const std::exception& e) { + break; + } + } + + return true; +} + +bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) { + CLevelDBBatch batch; + batch.Write(make_pair(DB_TIMESTAMPINDEX, timestampIndex), 0); + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes) { + + boost::scoped_ptr pcursor(NewIterator()); + + CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); + ssKeySet << make_pair(DB_TIMESTAMPINDEX, CTimestampIndexIteratorKey(low)); + pcursor->Seek(ssKeySet.str()); + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + CTimestampIndexKey indexKey; + ssKey >> chType; + ssKey >> indexKey; + if (chType == DB_TIMESTAMPINDEX && indexKey.timestamp < high) { + if (fActiveOnly) { + if (blockOnchainActive(indexKey.blockHash)) { + hashes.push_back(std::make_pair(indexKey.blockHash, indexKey.timestamp)); + } + } else { + hashes.push_back(std::make_pair(indexKey.blockHash, indexKey.timestamp)); + } + + pcursor->Next(); + } else { + break; + } + } catch (const std::exception& e) { + break; + } + } + + return true; +} + +bool CBlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts) { + CLevelDBBatch batch; + batch.Write(make_pair(DB_BLOCKHASHINDEX, blockhashIndex), logicalts); + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) { + + CTimestampBlockIndexValue(lts); + if (!Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts)) + return false; + + ltimestamp = lts.ltimestamp; + return true; +} + bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); } @@ -274,6 +470,16 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height); +bool CBlockTreeDB::blockOnchainActive(const uint256 &hash) { + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (!chainActive.Contains(pblockindex)) { + return false; + } + + return true; +} + bool CBlockTreeDB::LoadBlockIndexGuts() { boost::scoped_ptr pcursor(NewIterator()); diff --git a/src/txdb.h b/src/txdb.h index 04ac8627b..401f92144 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -17,6 +17,17 @@ class CBlockFileInfo; class CBlockIndex; struct CDiskTxPos; +struct CAddressUnspentKey; +struct CAddressUnspentValue; +struct CAddressIndexKey; +struct CAddressIndexIteratorKey; +struct CAddressIndexIteratorHeightKey; +struct CTimestampIndexKey; +struct CTimestampIndexIteratorKey; +struct CTimestampBlockIndexKey; +struct CTimestampBlockIndexValue; +struct CSpentIndexKey; +struct CSpentIndexValue; class uint256; //! -dbcache default (MiB) @@ -53,7 +64,7 @@ public: class CBlockTreeDB : public CLevelDBWrapper { public: - CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool compression = true, int maxOpenFiles = 1000); private: CBlockTreeDB(const CBlockTreeDB&); void operator=(const CBlockTreeDB&); @@ -65,9 +76,24 @@ public: bool ReadReindexing(bool &fReindex); bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); bool WriteTxIndex(const std::vector > &list); + bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); + bool UpdateSpentIndex(const std::vector >&vect); + bool UpdateAddressUnspentIndex(const std::vector >&vect); + bool ReadAddressUnspentIndex(uint160 addressHash, int type, + std::vector > &vect); + bool WriteAddressIndex(const std::vector > &vect); + bool EraseAddressIndex(const std::vector > &vect); + bool ReadAddressIndex(uint160 addressHash, int type, + std::vector > &addressIndex, + int start = 0, int end = 0); + bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex); + bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &vect); + bool WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts); + bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); bool LoadBlockIndexGuts(); + bool blockOnchainActive(const uint256 &hash); }; #endif // BITCOIN_TXDB_H diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 2dc9a4642..164d5bb47 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -119,6 +119,144 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, return true; } +void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view) +{ + LOCK(cs); + const CTransaction& tx = entry.GetTx(); + std::vector inserted; + + uint256 txhash = tx.GetHash(); + for (unsigned int j = 0; j < tx.vin.size(); j++) { + const CTxIn input = tx.vin[j]; + const CTxOut &prevout = view.GetOutputFor(input); + if (prevout.scriptPubKey.IsPayToScriptHash()) { + vector hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22); + CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, 1); + CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); + mapAddress.insert(make_pair(key, delta)); + inserted.push_back(key); + } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); + CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1); + CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); + mapAddress.insert(make_pair(key, delta)); + inserted.push_back(key); + } + } + + for (unsigned int k = 0; k < tx.vout.size(); k++) { + const CTxOut &out = tx.vout[k]; + if (out.scriptPubKey.IsPayToScriptHash()) { + vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); + CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0); + mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); + inserted.push_back(key); + } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + std::pair ret; + CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0); + mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); + inserted.push_back(key); + } + } + + mapAddressInserted.insert(make_pair(txhash, inserted)); +} + +bool CTxMemPool::getAddressIndex(std::vector > &addresses, + std::vector > &results) +{ + LOCK(cs); + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + addressDeltaMap::iterator ait = mapAddress.lower_bound(CMempoolAddressDeltaKey((*it).second, (*it).first)); + while (ait != mapAddress.end() && (*ait).first.addressBytes == (*it).first && (*ait).first.type == (*it).second) { + results.push_back(*ait); + ait++; + } + } + return true; +} + +bool CTxMemPool::removeAddressIndex(const uint256 txhash) +{ + LOCK(cs); + addressDeltaMapInserted::iterator it = mapAddressInserted.find(txhash); + + if (it != mapAddressInserted.end()) { + std::vector keys = (*it).second; + for (std::vector::iterator mit = keys.begin(); mit != keys.end(); mit++) { + mapAddress.erase(*mit); + } + mapAddressInserted.erase(it); + } + + return true; +} + +void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view) +{ + LOCK(cs); + + const CTransaction& tx = entry.GetTx(); + std::vector inserted; + + uint256 txhash = tx.GetHash(); + for (unsigned int j = 0; j < tx.vin.size(); j++) { + const CTxIn input = tx.vin[j]; + const CTxOut &prevout = view.GetOutputFor(input); + uint160 addressHash; + int addressType; + + if (prevout.scriptPubKey.IsPayToScriptHash()) { + addressHash = uint160(vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); + addressType = 2; + } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + addressHash = uint160(vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); + addressType = 1; + } else { + addressHash.SetNull(); + addressType = 0; + } + + CSpentIndexKey key = CSpentIndexKey(input.prevout.hash, input.prevout.n); + CSpentIndexValue value = CSpentIndexValue(txhash, j, -1, prevout.nValue, addressType, addressHash); + + mapSpent.insert(make_pair(key, value)); + inserted.push_back(key); + + } + + mapSpentInserted.insert(make_pair(txhash, inserted)); +} + +bool CTxMemPool::getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) +{ + LOCK(cs); + mapSpentIndex::iterator it; + + it = mapSpent.find(key); + if (it != mapSpent.end()) { + value = it->second; + return true; + } + return false; +} + +bool CTxMemPool::removeSpentIndex(const uint256 txhash) +{ + LOCK(cs); + mapSpentIndexInserted::iterator it = mapSpentInserted.find(txhash); + + if (it != mapSpentInserted.end()) { + std::vector keys = (*it).second; + for (std::vector::iterator mit = keys.begin(); mit != keys.end(); mit++) { + mapSpent.erase(*mit); + } + mapSpentInserted.erase(it); + } + + return true; +} void CTxMemPool::remove(const CTransaction &origTx, std::list& removed, bool fRecursive) { @@ -168,6 +306,8 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem mapTx.erase(hash); nTransactionsUpdated++; minerPolicyEstimator->removeTx(hash); + removeAddressIndex(hash); + removeSpentIndex(hash); } } } diff --git a/src/txmempool.h b/src/txmempool.h index 6b6434f1e..e9bb8f359 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -8,6 +8,8 @@ #include +#include "addressindex.h" +#include "spentindex.h" #include "amount.h" #include "coins.h" #include "primitives/transaction.h" @@ -147,6 +149,21 @@ public: mutable CCriticalSection cs; indexed_transaction_set mapTx; + +private: + typedef std::map addressDeltaMap; + addressDeltaMap mapAddress; + + typedef std::map > addressDeltaMapInserted; + addressDeltaMapInserted mapAddressInserted; + + typedef std::map mapSpentIndex; + mapSpentIndex mapSpent; + + typedef std::map > mapSpentIndexInserted; + mapSpentIndexInserted mapSpentInserted; + +public: std::map mapNextTx; std::map mapNullifiers; std::map > mapDeltas; @@ -164,6 +181,14 @@ public: void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967296.0; } bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true); + void addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view); + bool getAddressIndex(std::vector > &addresses, + std::vector > &results); + bool removeAddressIndex(const uint256 txhash); + + void addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view); + bool getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); + bool removeSpentIndex(const uint256 txhash); void remove(const CTransaction &tx, std::list& removed, bool fRecursive = false); void removeWithAnchor(const uint256 &invalidRoot); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); diff --git a/src/wallet-utility.cpp b/src/wallet-utility.cpp new file mode 100644 index 000000000..04b9edf45 --- /dev/null +++ b/src/wallet-utility.cpp @@ -0,0 +1,347 @@ +#include +#include + +// Include local headers +#include "wallet/walletdb.h" +#include "util.h" +#include "base58.h" +#include "wallet/crypter.h" +#include + +#include "komodo_defs.h" +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +int64_t MAX_MONEY = 200000000 * 100000000LL; +uint64_t ASSETCHAINS_SUPPLY; +uint16_t BITCOIND_PORT = 7771; +uint16_t ASSETCHAINS_PORT; +uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC; +uint32_t ASSETCHAINS_MAGIC = 2387029918; + +void show_help() +{ + std::cout << + "This program outputs Bitcoin addresses and private keys from a wallet.dat file" << std::endl + << std::endl + << "Usage and options: " + << std::endl + << " -datadir= to tell the program where your wallet is" + << std::endl + << " -wallet= (Optional) if your wallet is not named wallet.dat" + << std::endl + << " -regtest or -testnet (Optional) dumps addresses from regtest/testnet" + << std::endl + << " -dumppass (Optional)if you want to extract private keys associated with addresses" + << std::endl + << " -pass= if you have encrypted private keys stored in your wallet" + << std::endl; +} + + +class WalletUtilityDB : public CDB +{ + private: + typedef std::map MasterKeyMap; + MasterKeyMap mapMasterKeys; + unsigned int nMasterKeyMaxID; + SecureString mPass; + std::vector vMKeys; + + public: + WalletUtilityDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnClose = true) : CDB(strFilename, pszMode, fFlushOnClose) + { + nMasterKeyMaxID = 0; + mPass.reserve(100); + } + + std::string getAddress(CDataStream ssKey); + std::string getKey(CDataStream ssKey, CDataStream ssValue); + std::string getCryptedKey(CDataStream ssKey, CDataStream ssValue, std::string masterPass); + bool updateMasterKeys(CDataStream ssKey, CDataStream ssValue); + bool parseKeys(bool dumppriv, std::string masterPass); + + bool DecryptSecret(const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); + bool Unlock(); + bool DecryptKey(const std::vector& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key); +}; + + +/* + * Address from a public key in base58 + */ +std::string WalletUtilityDB::getAddress(CDataStream ssKey) +{ + CPubKey vchPubKey; + ssKey >> vchPubKey; + CKeyID id = vchPubKey.GetID(); + std::string strAddr = CBitcoinAddress(id).ToString(); + + return strAddr; +} + + +/* + * Non encrypted private key in WIF + */ +std::string WalletUtilityDB::getKey(CDataStream ssKey, CDataStream ssValue) +{ + std::string strKey; + CPubKey vchPubKey; + ssKey >> vchPubKey; + CPrivKey pkey; + CKey key; + + ssValue >> pkey; + if (key.Load(pkey, vchPubKey, true)) + strKey = CBitcoinSecret(key).ToString(); + + return strKey; +} + + +bool WalletUtilityDB::DecryptSecret(const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) +{ + CCrypter cKeyCrypter; + std::vector chIV(WALLET_CRYPTO_KEY_SIZE); + memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); + + BOOST_FOREACH(const CKeyingMaterial vMKey, vMKeys) + { + if(!cKeyCrypter.SetKey(vMKey, chIV)) + continue; + if (cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext))) + return true; + } + return false; +} + + +bool WalletUtilityDB::Unlock() +{ + CCrypter crypter; + CKeyingMaterial vMasterKey; + + BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(mPass, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + continue; // try another master key + vMKeys.push_back(vMasterKey); + } + return true; +} + + +bool WalletUtilityDB::DecryptKey(const std::vector& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) +{ + CKeyingMaterial vchSecret; + if(!DecryptSecret(vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + + if (vchSecret.size() != 32) + return false; + + key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + return true; +} + + +/* + * Encrypted private key in WIF format + */ +std::string WalletUtilityDB::getCryptedKey(CDataStream ssKey, CDataStream ssValue, std::string masterPass) +{ + mPass = masterPass.c_str(); + CPubKey vchPubKey; + ssKey >> vchPubKey; + CKey key; + + std::vector vKey; + ssValue >> vKey; + + if (!Unlock()) + return ""; + + if(!DecryptKey(vKey, vchPubKey, key)) + return ""; + + std::string strKey = CBitcoinSecret(key).ToString(); + return strKey; +} + + +/* + * Master key derivation + */ +bool WalletUtilityDB::updateMasterKeys(CDataStream ssKey, CDataStream ssValue) +{ + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + if (mapMasterKeys.count(nID) != 0) + { + std::cout << "Error reading wallet database: duplicate CMasterKey id " << nID << std::endl; + return false; + } + mapMasterKeys[nID] = kMasterKey; + + if (nMasterKeyMaxID < nID) + nMasterKeyMaxID = nID; + + return true; +} + + +/* + * Look at all the records and parse keys for addresses and private keys + */ +bool WalletUtilityDB::parseKeys(bool dumppriv, std::string masterPass) +{ + DBErrors result = DB_LOAD_OK; + std::string strType; + bool first = true; + + try { + Dbc* pcursor = GetCursor(); + if (!pcursor) + { + LogPrintf("Error getting wallet database cursor\n"); + result = DB_CORRUPT; + } + + if (dumppriv) + { + while (result == DB_LOAD_OK && true) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int result = ReadAtCursor(pcursor, ssKey, ssValue); + + if (result == DB_NOTFOUND) { + break; + } + else if (result != 0) + { + LogPrintf("Error reading next record from wallet database\n"); + result = DB_CORRUPT; + break; + } + + ssKey >> strType; + if (strType == "mkey") + { + updateMasterKeys(ssKey, ssValue); + } + } + pcursor->close(); + pcursor = GetCursor(); + } + + while (result == DB_LOAD_OK && true) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + + if (ret == DB_NOTFOUND) + { + std::cout << " ]" << std::endl; + first = true; + break; + } + else if (ret != DB_LOAD_OK) + { + LogPrintf("Error reading next record from wallet database\n"); + result = DB_CORRUPT; + break; + } + + ssKey >> strType; + + if (strType == "key" || strType == "ckey") + { + std::string strAddr = getAddress(ssKey); + std::string strKey = ""; + + + if (dumppriv && strType == "key") + strKey = getKey(ssKey, ssValue); + if (dumppriv && strType == "ckey") + { + if (masterPass == "") + { + std::cout << "Encrypted wallet, please provide a password. See help below" << std::endl; + show_help(); + result = DB_LOAD_FAIL; + break; + } + strKey = getCryptedKey(ssKey, ssValue, masterPass); + } + + if (strAddr != "") + { + if (first) + std::cout << "[ "; + else + std::cout << ", "; + } + + if (dumppriv) + { + std::cout << "{\"addr\" : \"" + strAddr + "\", " + << "\"pkey\" : \"" + strKey + "\"}" + << std::flush; + } + else + { + std::cout << "\"" + strAddr + "\""; + } + + first = false; + } + } + + pcursor->close(); + } catch (DbException &e) { + std::cout << "DBException caught " << e.get_errno() << std::endl; + } catch (std::exception &e) { + std::cout << "Exception caught " << std::endl; + } + + if (result == DB_LOAD_OK) + return true; + else + return false; +} + + +int main(int argc, char* argv[]) +{ + ParseParameters(argc, argv); + std::string walletFile = GetArg("-wallet", "wallet.dat"); + std::string masterPass = GetArg("-pass", ""); + bool fDumpPass = GetBoolArg("-dumppass", false); + bool help = GetBoolArg("-h", false); + bool result = false; + + if (help) + { + show_help(); + return 0; + } + + try { + SelectParamsFromCommandLine(); + result = WalletUtilityDB(walletFile, "r").parseKeys(fDumpPass, masterPass); + } + catch (const std::exception& e) { + std::cout << "Error opening wallet file " << walletFile << std::endl; + std::cout << e.what() << std::endl; + } + + if (result) + return 0; + else + return -1; +} diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index 2685569d3..b96bb2979 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -17,6 +17,7 @@ #include "tinyformat.h" #include "sync.h" #include "amount.h" +extern int64_t MAX_MONEY; using namespace libsnark;