Bitcore port
This commit is contained in:
3
.vs/ProjectSettings.json
Normal file
3
.vs/ProjectSettings.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"CurrentProjectSetting": "x86-Debug"
|
||||
}
|
||||
6
.vs/VSWorkspaceState.json
Normal file
6
.vs/VSWorkspaceState.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ExpandedNodes": [
|
||||
""
|
||||
],
|
||||
"PreviewInSolutionExplorer": false
|
||||
}
|
||||
BIN
.vs/komodo/v15/.suo
Normal file
BIN
.vs/komodo/v15/.suo
Normal file
Binary file not shown.
BIN
.vs/komodo/v15/Browse.VC.db
Normal file
BIN
.vs/komodo/v15/Browse.VC.db
Normal file
Binary file not shown.
BIN
.vs/slnx.sqlite
Normal file
BIN
.vs/slnx.sqlite
Normal file
Binary file not shown.
199
Makefile.am
199
Makefile.am
@@ -14,6 +14,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
|
||||
@@ -31,32 +32,32 @@ 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 \
|
||||
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
|
||||
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
|
||||
endif
|
||||
|
||||
dist-hook:
|
||||
@@ -76,8 +77,9 @@ $(BITCOIN_WIN_INSTALLER): all-recursive
|
||||
$(MKDIR_P) $(top_builddir)/release
|
||||
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIND_BIN) $(top_builddir)/release
|
||||
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release
|
||||
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(WALLET_UTILITY_BIN) $(top_builddir)/release
|
||||
@test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \
|
||||
echo error: could not build $@
|
||||
echo error: could not build $@
|
||||
@echo built $@
|
||||
|
||||
$(if $(findstring src/,$(MAKECMDGOALS)),$(MAKECMDGOALS), none): FORCE
|
||||
@@ -110,8 +112,8 @@ $(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
|
||||
|
||||
@@ -161,6 +163,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:
|
||||
@@ -169,27 +174,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
|
||||
@@ -198,27 +203,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
|
||||
@@ -233,27 +238,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
|
||||
|
||||
block_test.info: test_bitcoin_filtered.info
|
||||
@@ -266,31 +271,31 @@ 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
|
||||
@@ -299,7 +304,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
|
||||
@@ -307,19 +312,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
|
||||
|
||||
@@ -201,7 +201,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])
|
||||
|
||||
@@ -817,7 +817,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)
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ testScripts=(
|
||||
'walletbackup.py'
|
||||
'nodehandling.py'
|
||||
'reindex.py'
|
||||
'addressindex.py'
|
||||
'timestampindex.py'
|
||||
'spentindex.py'
|
||||
'decodescript.py'
|
||||
'disablewallet.py'
|
||||
'zcjoinsplit.py'
|
||||
|
||||
349
qa/rpc-tests/addressindex.py
Normal file
349
qa/rpc-tests/addressindex.py
Normal file
@@ -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()
|
||||
139
qa/rpc-tests/spentindex.py
Normal file
139
qa/rpc-tests/spentindex.py
Normal file
@@ -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()
|
||||
61
qa/rpc-tests/timestampindex.py
Normal file
61
qa/rpc-tests/timestampindex.py
Normal file
@@ -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()
|
||||
73
qa/rpc-tests/txindex.py
Normal file
73
qa/rpc-tests/txindex.py
Normal file
@@ -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()
|
||||
263
qa/zcash/create_benchmark_archive.py
Normal file
263
qa/zcash/create_benchmark_archive.py
Normal file
@@ -0,0 +1,263 @@
|
||||
import binascii
|
||||
import calendar
|
||||
import json
|
||||
import plyvel
|
||||
import progressbar
|
||||
import os
|
||||
import stat
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import time
|
||||
|
||||
ZCASH_CLI = './src/zcash-cli'
|
||||
USAGE = """
|
||||
Requirements:
|
||||
- find
|
||||
- xz
|
||||
- %s (edit ZCASH_CLI in this script to alter the path)
|
||||
- A running mainnet zcashd using the default datadir with -txindex=1
|
||||
|
||||
Example usage:
|
||||
|
||||
make -C src/leveldb/
|
||||
virtualenv venv
|
||||
. venv/bin/activate
|
||||
pip install --global-option=build_ext --global-option="-L$(pwd)/src/leveldb/" --global-option="-I$(pwd)/src/leveldb/include/" plyvel
|
||||
pip install progressbar2
|
||||
LD_LIBRARY_PATH=src/leveldb python qa/zcash/create_benchmark_archive.py
|
||||
""" % ZCASH_CLI
|
||||
|
||||
def check_deps():
|
||||
if subprocess.call(['which', 'find', 'xz', ZCASH_CLI], stdout=subprocess.PIPE):
|
||||
print USAGE
|
||||
sys.exit()
|
||||
|
||||
def encode_varint(n):
|
||||
v = bytearray()
|
||||
l = 0
|
||||
while True:
|
||||
v.append((n & 0x7F) | (0x80 if l else 0x00))
|
||||
if (n <= 0x7F):
|
||||
break
|
||||
n = (n >> 7) - 1
|
||||
l += 1
|
||||
return bytes(v)[::-1]
|
||||
|
||||
def decode_varint(v):
|
||||
n = 0
|
||||
for ch in range(len(v)):
|
||||
n = (n << 7) | (ord(v[ch]) & 0x7F)
|
||||
if (ord(v[ch]) & 0x80):
|
||||
n += 1
|
||||
else:
|
||||
return n
|
||||
|
||||
def compress_amount(n):
|
||||
if n == 0:
|
||||
return 0
|
||||
e = 0
|
||||
while (((n % 10) == 0) and e < 9):
|
||||
n /= 10
|
||||
e += 1
|
||||
if e < 9:
|
||||
d = (n % 10)
|
||||
assert(d >= 1 and d <= 9)
|
||||
n /= 10
|
||||
return 1 + (n*9 + d - 1)*10 + e
|
||||
else:
|
||||
return 1 + (n - 1)*10 + 9
|
||||
|
||||
OP_DUP = 0x76
|
||||
OP_EQUAL = 0x87
|
||||
OP_EQUALVERIFY = 0x88
|
||||
OP_HASH160 = 0xa9
|
||||
OP_CHECKSIG = 0xac
|
||||
def to_key_id(script):
|
||||
if len(script) == 25 and \
|
||||
script[0] == OP_DUP and \
|
||||
script[1] == OP_HASH160 and \
|
||||
script[2] == 20 and \
|
||||
script[23] == OP_EQUALVERIFY and \
|
||||
script[24] == OP_CHECKSIG:
|
||||
return script[3:23]
|
||||
return bytes()
|
||||
|
||||
def to_script_id(script):
|
||||
if len(script) == 23 and \
|
||||
script[0] == OP_HASH160 and \
|
||||
script[1] == 20 and \
|
||||
script[22] == OP_EQUAL:
|
||||
return script[2:22]
|
||||
return bytes()
|
||||
|
||||
def to_pubkey(script):
|
||||
if len(script) == 35 and \
|
||||
script[0] == 33 and \
|
||||
script[34] == OP_CHECKSIG and \
|
||||
(script[1] == 0x02 or script[1] == 0x03):
|
||||
return script[1:34]
|
||||
if len(script) == 67 and \
|
||||
script[0] == 65 and \
|
||||
script[66] == OP_CHECKSIG and \
|
||||
script[1] == 0x04:
|
||||
return script[1:66] # assuming is fully valid
|
||||
return bytes()
|
||||
|
||||
def compress_script(script):
|
||||
result = bytearray()
|
||||
|
||||
key_id = to_key_id(script)
|
||||
if key_id:
|
||||
result.append(0x00)
|
||||
result.extend(key_id)
|
||||
return bytes(result)
|
||||
|
||||
script_id = to_script_id(script)
|
||||
if script_id:
|
||||
result.append(0x01)
|
||||
result.extend(script_id)
|
||||
return bytes(result)
|
||||
|
||||
pubkey = to_pubkey(script)
|
||||
if pubkey:
|
||||
result.append(0x00)
|
||||
result.extend(pubkey[1:33])
|
||||
if pubkey[0] == 0x02 or pubkey[0] == 0x03:
|
||||
result[0] = pubkey[0]
|
||||
return bytes(result)
|
||||
elif pubkey[0] == 0x04:
|
||||
result[0] = 0x04 | (pubkey[64] & 0x01)
|
||||
return bytes(result)
|
||||
|
||||
size = len(script) + 6
|
||||
result.append(encode_varint(size))
|
||||
result.extend(script)
|
||||
return bytes(result)
|
||||
|
||||
def deterministic_filter(tarinfo):
|
||||
tarinfo.uid = tarinfo.gid = 0
|
||||
tarinfo.uname = tarinfo.gname = "root"
|
||||
tarinfo.mtime = calendar.timegm(time.strptime('2017-05-17', '%Y-%m-%d'))
|
||||
tarinfo.mode |= stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
|
||||
tarinfo.mode &= ~stat.S_IWGRP
|
||||
if tarinfo.isdir():
|
||||
tarinfo.mode |= \
|
||||
stat.S_IXUSR | \
|
||||
stat.S_IXGRP | \
|
||||
stat.S_IXOTH
|
||||
else:
|
||||
tarinfo.mode &= \
|
||||
~stat.S_IXUSR & \
|
||||
~stat.S_IXGRP & \
|
||||
~stat.S_IXOTH
|
||||
return tarinfo
|
||||
|
||||
def create_benchmark_archive(blk_hash):
|
||||
blk = json.loads(subprocess.check_output([ZCASH_CLI, 'getblock', blk_hash]))
|
||||
print 'Height: %d' % blk['height']
|
||||
print 'Transactions: %d' % len(blk['tx'])
|
||||
|
||||
os.mkdir('benchmark')
|
||||
with open('benchmark/block-%d.dat' % blk['height'], 'wb') as f:
|
||||
f.write(binascii.unhexlify(subprocess.check_output([ZCASH_CLI, 'getblock', blk_hash, 'false']).strip()))
|
||||
|
||||
txs = [json.loads(subprocess.check_output([ZCASH_CLI, 'getrawtransaction', tx, '1'])
|
||||
) for tx in blk['tx']]
|
||||
|
||||
js_txs = len([tx for tx in txs if len(tx['vjoinsplit']) > 0])
|
||||
if js_txs:
|
||||
print 'Block contains %d JoinSplit-containing transactions' % js_txs
|
||||
return
|
||||
|
||||
inputs = [(x['txid'], x['vout']) for tx in txs for x in tx['vin'] if x.has_key('txid')]
|
||||
print 'Total inputs: %d' % len(inputs)
|
||||
|
||||
unique_inputs = {}
|
||||
for i in sorted(inputs):
|
||||
if unique_inputs.has_key(i[0]):
|
||||
unique_inputs[i[0]].append(i[1])
|
||||
else:
|
||||
unique_inputs[i[0]] = [i[1]]
|
||||
print 'Unique input transactions: %d' % len(unique_inputs)
|
||||
|
||||
db_path = 'benchmark/block-%d-inputs' % blk['height']
|
||||
db = plyvel.DB(db_path, create_if_missing=True)
|
||||
wb = db.write_batch()
|
||||
bar = progressbar.ProgressBar(redirect_stdout=True)
|
||||
print 'Collecting input coins for block'
|
||||
for tx in bar(unique_inputs.keys()):
|
||||
rawtx = json.loads(subprocess.check_output([ZCASH_CLI, 'getrawtransaction', tx, '1']))
|
||||
|
||||
mask_size = 0
|
||||
mask_code = 0
|
||||
b = 0
|
||||
while 2+b*8 < len(rawtx['vout']):
|
||||
zero = True
|
||||
i = 0
|
||||
while i < 8 and 2+b*8+i < len(rawtx['vout']):
|
||||
if 2+b*8+i in unique_inputs[tx]:
|
||||
zero = False
|
||||
i += 1
|
||||
if not zero:
|
||||
mask_size = b + 1
|
||||
mask_code += 1
|
||||
b += 1
|
||||
|
||||
coinbase = len(rawtx['vin']) == 1 and 'coinbase' in rawtx['vin'][0]
|
||||
first = len(rawtx['vout']) > 0 and 0 in unique_inputs[tx]
|
||||
second = len(rawtx['vout']) > 1 and 1 in unique_inputs[tx]
|
||||
code = 8*(mask_code - (0 if first or second else 1)) + \
|
||||
(1 if coinbase else 0) + \
|
||||
(2 if first else 0) + \
|
||||
(4 if second else 0)
|
||||
|
||||
coins = bytearray()
|
||||
# Serialized format:
|
||||
# - VARINT(nVersion)
|
||||
coins.extend(encode_varint(rawtx['version']))
|
||||
# - VARINT(nCode)
|
||||
coins.extend(encode_varint(code))
|
||||
# - unspentness bitvector, for vout[2] and further; least significant byte first
|
||||
for b in range(mask_size):
|
||||
avail = 0
|
||||
i = 0
|
||||
while i < 8 and 2+b*8+i < len(rawtx['vout']):
|
||||
if 2+b*8+i in unique_inputs[tx]:
|
||||
avail |= (1 << i)
|
||||
i += 1
|
||||
coins.append(avail)
|
||||
# - the non-spent CTxOuts (via CTxOutCompressor)
|
||||
for i in range(len(rawtx['vout'])):
|
||||
if i in unique_inputs[tx]:
|
||||
coins.extend(encode_varint(compress_amount(int(rawtx['vout'][i]['valueZat']))))
|
||||
coins.extend(compress_script(
|
||||
binascii.unhexlify(rawtx['vout'][i]['scriptPubKey']['hex'])))
|
||||
# - VARINT(nHeight)
|
||||
coins.extend(encode_varint(json.loads(
|
||||
subprocess.check_output([ZCASH_CLI, 'getblockheader', rawtx['blockhash']])
|
||||
)['height']))
|
||||
|
||||
db_key = b'c' + bytes(binascii.unhexlify(tx)[::-1])
|
||||
db_val = bytes(coins)
|
||||
wb.put(db_key, db_val)
|
||||
|
||||
wb.write()
|
||||
db.close()
|
||||
|
||||
# Make reproducible archive
|
||||
os.remove('%s/LOG' % db_path)
|
||||
files = subprocess.check_output(['find', 'benchmark']).strip().split('\n')
|
||||
archive_name = 'block-%d.tar' % blk['height']
|
||||
tar = tarfile.open(archive_name, 'w')
|
||||
for name in sorted(files):
|
||||
tar.add(name, recursive=False, filter=deterministic_filter)
|
||||
tar.close()
|
||||
subprocess.check_call(['xz', '-6', archive_name])
|
||||
print 'Created archive %s.xz' % archive_name
|
||||
subprocess.call(['rm', '-r', 'benchmark'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
check_deps()
|
||||
create_benchmark_archive('0000000007cdb809e48e51dd0b530e8f5073e0a9e9bd7ae920fe23e874658c74')
|
||||
60
qa/zcash/create_wallet_200k_utxos.py
Normal file
60
qa/zcash/create_wallet_200k_utxos.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python2
|
||||
# Copyright (c) 2017 The Zcash developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Create a large wallet
|
||||
#
|
||||
# To use:
|
||||
# - Copy to qa/rpc-tests/wallet_large.py
|
||||
# - Add wallet_large.py to RPC tests list
|
||||
# - ./qa/pull-tester/rpc-tests.sh wallet_large --nocleanup
|
||||
# - Archive the resulting /tmp/test###### directory
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
connect_nodes_bi,
|
||||
initialize_chain_clean,
|
||||
start_nodes,
|
||||
)
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class LargeWalletTest(BitcoinTestFramework):
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 2)
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = start_nodes(2, self.options.tmpdir)
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
self.is_network_split = False
|
||||
self.sync_all()
|
||||
|
||||
def run_test(self):
|
||||
self.nodes[1].generate(103)
|
||||
self.sync_all()
|
||||
|
||||
inputs = []
|
||||
for i in range(200000):
|
||||
taddr = self.nodes[0].getnewaddress()
|
||||
inputs.append(self.nodes[1].sendtoaddress(taddr, Decimal("0.001")))
|
||||
if i % 1000 == 0:
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
print('Node 0: %d transactions, %d UTXOs' %
|
||||
(len(self.nodes[0].listtransactions()), len(self.nodes[0].listunspent())))
|
||||
print('Node 1: %d transactions, %d UTXOs' %
|
||||
(len(self.nodes[1].listtransactions()), len(self.nodes[1].listunspent())))
|
||||
assert_equal(len(self.nodes[0].listunspent()), len(inputs))
|
||||
|
||||
if __name__ == '__main__':
|
||||
LargeWalletTest().main()
|
||||
@@ -1,47 +1,201 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env python2
|
||||
#
|
||||
# Execute all of the automated tests related to Zcash.
|
||||
#
|
||||
|
||||
set -eu
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
SUITE_EXIT_STATUS=0
|
||||
REPOROOT="$(readlink -f "$(dirname "$0")"/../../)"
|
||||
REPOROOT = os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.abspath(__file__)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
function run_test_phase
|
||||
{
|
||||
echo "===== BEGIN: $*"
|
||||
set +e
|
||||
eval "$@"
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "===== PASSED: $*"
|
||||
else
|
||||
echo "===== FAILED: $*"
|
||||
SUITE_EXIT_STATUS=1
|
||||
fi
|
||||
set -e
|
||||
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')],
|
||||
}
|
||||
|
||||
cd "${REPOROOT}"
|
||||
|
||||
# Test phases:
|
||||
run_test_phase "${REPOROOT}/qa/zcash/check-security-hardening.sh"
|
||||
run_test_phase "${REPOROOT}/qa/zcash/ensure-no-dot-so-in-depends.py"
|
||||
#
|
||||
# Test driver
|
||||
#
|
||||
|
||||
# If make check fails, show test-suite.log as part of our run_test_phase
|
||||
# output (and fail the phase with false):
|
||||
run_test_phase make check '||' \
|
||||
'{' \
|
||||
echo '=== ./src/test-suite.log ===' ';' \
|
||||
cat './src/test-suite.log' ';' \
|
||||
false ';' \
|
||||
'}'
|
||||
def run_stage(stage):
|
||||
print('Running stage %s' % stage)
|
||||
print('=' * (len(stage) + 14))
|
||||
print
|
||||
|
||||
exit $SUITE_EXIT_STATUS
|
||||
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()
|
||||
@@ -1,37 +1,113 @@
|
||||
#!/bin/bash
|
||||
set -u
|
||||
|
||||
set -e
|
||||
|
||||
DATADIR=./benchmark-datadir
|
||||
SHA256CMD="$(command -v sha256sum || echo shasum)"
|
||||
SHA256ARGS="$(command -v sha256sum >/dev/null || echo '-a 256')"
|
||||
|
||||
function zcash_rpc {
|
||||
./src/zcash-cli -datadir="$DATADIR" -rpcwait -rpcuser=user -rpcpassword=password -rpcport=5983 "$@"
|
||||
./src/zcash-cli -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 "$@"
|
||||
}
|
||||
|
||||
function zcash_rpc_slow {
|
||||
# Timeout of 1 hour
|
||||
zcash_rpc -rpcclienttimeout=3600 "$@"
|
||||
}
|
||||
|
||||
function zcash_rpc_veryslow {
|
||||
# Timeout of 2.5 hours
|
||||
zcash_rpc -rpcclienttimeout=9000 "$@"
|
||||
}
|
||||
|
||||
function zcash_rpc_wait_for_start {
|
||||
zcash_rpc -rpcwait getinfo > /dev/null
|
||||
}
|
||||
|
||||
function zcashd_generate {
|
||||
zcash_rpc generate 101 > /dev/null
|
||||
}
|
||||
|
||||
function extract_benchmark_datadir {
|
||||
if [ -f "$1.tar.xz" ]; then
|
||||
# Check the hash of the archive:
|
||||
"$SHA256CMD" $SHA256ARGS -c <<EOF
|
||||
$2 $1.tar.xz
|
||||
EOF
|
||||
ARCHIVE_RESULT=$?
|
||||
else
|
||||
echo "$1.tar.xz not found."
|
||||
ARCHIVE_RESULT=1
|
||||
fi
|
||||
if [ $ARCHIVE_RESULT -ne 0 ]; then
|
||||
zcashd_stop
|
||||
echo
|
||||
echo "Please download it and place it in the base directory of the repository."
|
||||
exit 1
|
||||
fi
|
||||
xzcat "$1.tar.xz" | tar x
|
||||
}
|
||||
|
||||
function use_200k_benchmark {
|
||||
rm -rf benchmark-200k-UTXOs
|
||||
extract_benchmark_datadir benchmark-200k-UTXOs dc8ab89eaa13730da57d9ac373c1f4e818a37181c1443f61fd11327e49fbcc5e
|
||||
DATADIR="./benchmark-200k-UTXOs/node$1"
|
||||
}
|
||||
|
||||
function zcashd_start {
|
||||
rm -rf "$DATADIR"
|
||||
mkdir -p "$DATADIR"
|
||||
touch "$DATADIR/zcash.conf"
|
||||
case "$1" in
|
||||
sendtoaddress|loadwallet|listunspent)
|
||||
case "$2" in
|
||||
200k-recv)
|
||||
use_200k_benchmark 0
|
||||
;;
|
||||
200k-send)
|
||||
use_200k_benchmark 1
|
||||
;;
|
||||
*)
|
||||
echo "Bad arguments to zcashd_start."
|
||||
exit 1
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
rm -rf "$DATADIR"
|
||||
mkdir -p "$DATADIR/regtest"
|
||||
touch "$DATADIR/zcash.conf"
|
||||
esac
|
||||
./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 -showmetrics=0 &
|
||||
ZCASHD_PID=$!
|
||||
zcash_rpc_wait_for_start
|
||||
}
|
||||
|
||||
function zcashd_stop {
|
||||
zcash_rpc stop > /dev/null
|
||||
wait $ZCASH_PID
|
||||
wait $ZCASHD_PID
|
||||
}
|
||||
|
||||
function zcashd_massif_start {
|
||||
rm -rf "$DATADIR"
|
||||
mkdir -p "$DATADIR"
|
||||
touch "$DATADIR/zcash.conf"
|
||||
case "$1" in
|
||||
sendtoaddress|loadwallet|listunspent)
|
||||
case "$2" in
|
||||
200k-recv)
|
||||
use_200k_benchmark 0
|
||||
;;
|
||||
200k-send)
|
||||
use_200k_benchmark 1
|
||||
;;
|
||||
*)
|
||||
echo "Bad arguments to zcashd_massif_start."
|
||||
exit 1
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
rm -rf "$DATADIR"
|
||||
mkdir -p "$DATADIR/regtest"
|
||||
touch "$DATADIR/zcash.conf"
|
||||
esac
|
||||
rm -f massif.out
|
||||
valgrind --tool=massif --time-unit=ms --massif-out-file=massif.out ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 -showmetrics=0 &
|
||||
ZCASHD_PID=$!
|
||||
zcash_rpc_wait_for_start
|
||||
}
|
||||
|
||||
function zcashd_massif_stop {
|
||||
@@ -42,11 +118,12 @@ function zcashd_massif_stop {
|
||||
|
||||
function zcashd_valgrind_start {
|
||||
rm -rf "$DATADIR"
|
||||
mkdir -p "$DATADIR"
|
||||
mkdir -p "$DATADIR/regtest"
|
||||
touch "$DATADIR/zcash.conf"
|
||||
rm -f valgrind.out
|
||||
valgrind --leak-check=yes -v --error-limit=no --log-file="valgrind.out" ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 -showmetrics=0 &
|
||||
ZCASHD_PID=$!
|
||||
zcash_rpc_wait_for_start
|
||||
}
|
||||
|
||||
function zcashd_valgrind_stop {
|
||||
@@ -55,12 +132,41 @@ function zcashd_valgrind_stop {
|
||||
cat valgrind.out
|
||||
}
|
||||
|
||||
function extract_benchmark_data {
|
||||
if [ -f "block-107134.tar.xz" ]; then
|
||||
# Check the hash of the archive:
|
||||
"$SHA256CMD" $SHA256ARGS -c <<EOF
|
||||
4bd5ad1149714394e8895fa536725ed5d6c32c99812b962bfa73f03b5ffad4bb block-107134.tar.xz
|
||||
EOF
|
||||
ARCHIVE_RESULT=$?
|
||||
else
|
||||
echo "block-107134.tar.xz not found."
|
||||
ARCHIVE_RESULT=1
|
||||
fi
|
||||
if [ $ARCHIVE_RESULT -ne 0 ]; then
|
||||
zcashd_stop
|
||||
echo
|
||||
echo "Please generate it using qa/zcash/create_benchmark_archive.py"
|
||||
echo "and place it in the base directory of the repository."
|
||||
echo "Usage details are inside the Python script."
|
||||
exit 1
|
||||
fi
|
||||
xzcat block-107134.tar.xz | tar x -C "$DATADIR/regtest"
|
||||
}
|
||||
|
||||
|
||||
if [ $# -lt 2 ]
|
||||
then
|
||||
echo "$0 : At least two arguments are required!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Precomputation
|
||||
case "$1" in
|
||||
*)
|
||||
case "$2" in
|
||||
verifyjoinsplit)
|
||||
zcashd_start
|
||||
zcashd_start "${@:2}"
|
||||
RAWJOINSPLIT=$(zcash_rpc zcsamplejoinsplit)
|
||||
zcashd_stop
|
||||
esac
|
||||
@@ -68,7 +174,7 @@ esac
|
||||
|
||||
case "$1" in
|
||||
time)
|
||||
zcashd_start
|
||||
zcashd_start "${@:2}"
|
||||
case "$2" in
|
||||
sleep)
|
||||
zcash_rpc zcbenchmark sleep 10
|
||||
@@ -83,7 +189,7 @@ case "$1" in
|
||||
zcash_rpc zcbenchmark verifyjoinsplit 1000 "\"$RAWJOINSPLIT\""
|
||||
;;
|
||||
solveequihash)
|
||||
zcash_rpc zcbenchmark solveequihash 50 "${@:3}"
|
||||
zcash_rpc_slow zcbenchmark solveequihash 50 "${@:3}"
|
||||
;;
|
||||
verifyequihash)
|
||||
zcash_rpc zcbenchmark verifyequihash 1000
|
||||
@@ -97,15 +203,28 @@ case "$1" in
|
||||
incnotewitnesses)
|
||||
zcash_rpc zcbenchmark incnotewitnesses 100 "${@:3}"
|
||||
;;
|
||||
connectblockslow)
|
||||
extract_benchmark_data
|
||||
zcash_rpc zcbenchmark connectblockslow 10
|
||||
;;
|
||||
sendtoaddress)
|
||||
zcash_rpc zcbenchmark sendtoaddress 10 "${@:4}"
|
||||
;;
|
||||
loadwallet)
|
||||
zcash_rpc zcbenchmark loadwallet 10
|
||||
;;
|
||||
listunspent)
|
||||
zcash_rpc zcbenchmark listunspent 10
|
||||
;;
|
||||
*)
|
||||
zcashd_stop
|
||||
echo "Bad arguments."
|
||||
echo "Bad arguments to time."
|
||||
exit 1
|
||||
esac
|
||||
zcashd_stop
|
||||
;;
|
||||
memory)
|
||||
zcashd_massif_start
|
||||
zcashd_massif_start "${@:2}"
|
||||
case "$2" in
|
||||
sleep)
|
||||
zcash_rpc zcbenchmark sleep 1
|
||||
@@ -114,26 +233,42 @@ case "$1" in
|
||||
zcash_rpc zcbenchmark parameterloading 1
|
||||
;;
|
||||
createjoinsplit)
|
||||
zcash_rpc zcbenchmark createjoinsplit 1 "${@:3}"
|
||||
zcash_rpc_slow zcbenchmark createjoinsplit 1 "${@:3}"
|
||||
;;
|
||||
verifyjoinsplit)
|
||||
zcash_rpc zcbenchmark verifyjoinsplit 1 "\"$RAWJOINSPLIT\""
|
||||
;;
|
||||
solveequihash)
|
||||
zcash_rpc zcbenchmark solveequihash 1 "${@:3}"
|
||||
zcash_rpc_slow zcbenchmark solveequihash 1 "${@:3}"
|
||||
;;
|
||||
verifyequihash)
|
||||
zcash_rpc zcbenchmark verifyequihash 1
|
||||
;;
|
||||
validatelargetx)
|
||||
zcash_rpc zcbenchmark validatelargetx 1
|
||||
;;
|
||||
trydecryptnotes)
|
||||
zcash_rpc zcbenchmark trydecryptnotes 1 "${@:3}"
|
||||
;;
|
||||
incnotewitnesses)
|
||||
zcash_rpc zcbenchmark incnotewitnesses 1 "${@:3}"
|
||||
;;
|
||||
connectblockslow)
|
||||
extract_benchmark_data
|
||||
zcash_rpc zcbenchmark connectblockslow 1
|
||||
;;
|
||||
sendtoaddress)
|
||||
zcash_rpc zcbenchmark sendtoaddress 1 "${@:4}"
|
||||
;;
|
||||
loadwallet)
|
||||
# The initial load is sufficient for measurement
|
||||
;;
|
||||
listunspent)
|
||||
zcash_rpc zcbenchmark listunspent 1
|
||||
;;
|
||||
*)
|
||||
zcashd_massif_stop
|
||||
echo "Bad arguments."
|
||||
echo "Bad arguments to memory."
|
||||
exit 1
|
||||
esac
|
||||
zcashd_massif_stop
|
||||
@@ -149,13 +284,13 @@ case "$1" in
|
||||
zcash_rpc zcbenchmark parameterloading 1
|
||||
;;
|
||||
createjoinsplit)
|
||||
zcash_rpc zcbenchmark createjoinsplit 1 "${@:3}"
|
||||
zcash_rpc_veryslow zcbenchmark createjoinsplit 1 "${@:3}"
|
||||
;;
|
||||
verifyjoinsplit)
|
||||
zcash_rpc zcbenchmark verifyjoinsplit 1 "\"$RAWJOINSPLIT\""
|
||||
;;
|
||||
solveequihash)
|
||||
zcash_rpc zcbenchmark solveequihash 1 "${@:3}"
|
||||
zcash_rpc_veryslow zcbenchmark solveequihash 1 "${@:3}"
|
||||
;;
|
||||
verifyequihash)
|
||||
zcash_rpc zcbenchmark verifyequihash 1
|
||||
@@ -166,9 +301,13 @@ case "$1" in
|
||||
incnotewitnesses)
|
||||
zcash_rpc zcbenchmark incnotewitnesses 1 "${@:3}"
|
||||
;;
|
||||
connectblockslow)
|
||||
extract_benchmark_data
|
||||
zcash_rpc zcbenchmark connectblockslow 1
|
||||
;;
|
||||
*)
|
||||
zcashd_valgrind_stop
|
||||
echo "Bad arguments."
|
||||
echo "Bad arguments to valgrind."
|
||||
exit 1
|
||||
esac
|
||||
zcashd_valgrind_stop
|
||||
@@ -189,12 +328,12 @@ case "$1" in
|
||||
rm -f valgrind.out
|
||||
;;
|
||||
*)
|
||||
echo "Bad arguments."
|
||||
echo "Bad arguments to valgrind-tests."
|
||||
exit 1
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Bad arguments."
|
||||
echo "Invalid benchmark type."
|
||||
exit 1
|
||||
esac
|
||||
|
||||
|
||||
425
src/Makefile.am
425
src/Makefile.am
@@ -13,8 +13,8 @@ $(LIBLEVELDB): $(LIBMEMENV)
|
||||
|
||||
$(LIBLEVELDB) $(LIBMEMENV):
|
||||
@echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \
|
||||
CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \
|
||||
OPT="$(CXXFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS"
|
||||
CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \
|
||||
OPT="$(CXXFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS"
|
||||
endif
|
||||
|
||||
BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config
|
||||
@@ -50,12 +50,12 @@ $(LIBUNIVALUE): $(wildcard univalue/lib/*)
|
||||
# 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
|
||||
@@ -80,7 +80,10 @@ if BUILD_BITCOIND
|
||||
endif
|
||||
|
||||
if BUILD_BITCOIN_UTILS
|
||||
bin_PROGRAMS += komodo-cli komodo-tx
|
||||
bin_PROGRAMS += komodo-cli komodo-tx
|
||||
if ENABLE_WALLET
|
||||
bin_PROGRAMS += wallet-utility
|
||||
endif
|
||||
endif
|
||||
|
||||
LIBZCASH_H = \
|
||||
@@ -97,6 +100,8 @@ LIBZCASH_H = \
|
||||
.PHONY: FORCE check-symbols check-security
|
||||
# bitcoin core #
|
||||
BITCOIN_CORE_H = \
|
||||
addressindex.h \
|
||||
spentindex.h \
|
||||
addrman.h \
|
||||
alert.h \
|
||||
amount.h \
|
||||
@@ -200,162 +205,162 @@ 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
|
||||
libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
|
||||
libbitcoin_server_a_SOURCES = \
|
||||
sendalert.cpp \
|
||||
addrman.cpp \
|
||||
alert.cpp \
|
||||
alertkeys.h \
|
||||
asyncrpcoperation.cpp \
|
||||
asyncrpcqueue.cpp \
|
||||
bloom.cpp \
|
||||
chain.cpp \
|
||||
checkpoints.cpp \
|
||||
httprpc.cpp \
|
||||
httpserver.cpp \
|
||||
init.cpp \
|
||||
leveldbwrapper.cpp \
|
||||
main.cpp \
|
||||
merkleblock.cpp \
|
||||
metrics.cpp \
|
||||
miner.cpp \
|
||||
net.cpp \
|
||||
noui.cpp \
|
||||
policy/fees.cpp \
|
||||
pow.cpp \
|
||||
rest.cpp \
|
||||
rpcblockchain.cpp \
|
||||
rpcmining.cpp \
|
||||
rpcmisc.cpp \
|
||||
rpcnet.cpp \
|
||||
rpcrawtransaction.cpp \
|
||||
rpcserver.cpp \
|
||||
script/sigcache.cpp \
|
||||
timedata.cpp \
|
||||
torcontrol.cpp \
|
||||
txdb.cpp \
|
||||
txmempool.cpp \
|
||||
validationinterface.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
sendalert.cpp \
|
||||
addrman.cpp \
|
||||
alert.cpp \
|
||||
alertkeys.h \
|
||||
asyncrpcoperation.cpp \
|
||||
asyncrpcqueue.cpp \
|
||||
bloom.cpp \
|
||||
chain.cpp \
|
||||
checkpoints.cpp \
|
||||
httprpc.cpp \
|
||||
httpserver.cpp \
|
||||
init.cpp \
|
||||
leveldbwrapper.cpp \
|
||||
main.cpp \
|
||||
merkleblock.cpp \
|
||||
metrics.cpp \
|
||||
miner.cpp \
|
||||
net.cpp \
|
||||
noui.cpp \
|
||||
policy/fees.cpp \
|
||||
pow.cpp \
|
||||
rest.cpp \
|
||||
rpcblockchain.cpp \
|
||||
rpcmining.cpp \
|
||||
rpcmisc.cpp \
|
||||
rpcnet.cpp \
|
||||
rpcrawtransaction.cpp \
|
||||
rpcserver.cpp \
|
||||
script/sigcache.cpp \
|
||||
timedata.cpp \
|
||||
torcontrol.cpp \
|
||||
txdb.cpp \
|
||||
txmempool.cpp \
|
||||
validationinterface.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
if ENABLE_ZMQ
|
||||
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
|
||||
|
||||
libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
|
||||
libbitcoin_zmq_a_SOURCES = \
|
||||
zmq/zmqabstractnotifier.cpp \
|
||||
zmq/zmqnotificationinterface.cpp \
|
||||
zmq/zmqpublishnotifier.cpp
|
||||
zmq/zmqabstractnotifier.cpp \
|
||||
zmq/zmqnotificationinterface.cpp \
|
||||
zmq/zmqpublishnotifier.cpp
|
||||
endif
|
||||
|
||||
|
||||
# wallet: zcashd, but only linked when wallet enabled
|
||||
libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
libbitcoin_wallet_a_SOURCES = \
|
||||
utiltest.cpp \
|
||||
utiltest.h \
|
||||
zcbenchmarks.cpp \
|
||||
zcbenchmarks.h \
|
||||
wallet/asyncrpcoperation_sendmany.cpp \
|
||||
wallet/crypter.cpp \
|
||||
wallet/db.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
wallet/rpcwallet.cpp \
|
||||
wallet/wallet.cpp \
|
||||
wallet/wallet_ismine.cpp \
|
||||
wallet/walletdb.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
utiltest.cpp \
|
||||
utiltest.h \
|
||||
zcbenchmarks.cpp \
|
||||
zcbenchmarks.h \
|
||||
wallet/asyncrpcoperation_sendmany.cpp \
|
||||
wallet/crypter.cpp \
|
||||
wallet/db.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
wallet/rpcwallet.cpp \
|
||||
wallet/wallet.cpp \
|
||||
wallet/wallet_ismine.cpp \
|
||||
wallet/walletdb.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
# crypto primitives library
|
||||
crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES)
|
||||
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
|
||||
libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
libbitcoin_common_a_SOURCES = \
|
||||
amount.cpp \
|
||||
arith_uint256.cpp \
|
||||
base58.cpp \
|
||||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
compressor.cpp \
|
||||
core_read.cpp \
|
||||
core_write.cpp \
|
||||
eccryptoverify.cpp \
|
||||
ecwrapper.cpp \
|
||||
hash.cpp \
|
||||
key.cpp \
|
||||
keystore.cpp \
|
||||
netbase.cpp \
|
||||
primitives/block.cpp \
|
||||
primitives/transaction.cpp \
|
||||
protocol.cpp \
|
||||
pubkey.cpp \
|
||||
scheduler.cpp \
|
||||
script/interpreter.cpp \
|
||||
script/script.cpp \
|
||||
script/script_error.cpp \
|
||||
script/sign.cpp \
|
||||
script/standard.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
amount.cpp \
|
||||
arith_uint256.cpp \
|
||||
base58.cpp \
|
||||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
compressor.cpp \
|
||||
core_read.cpp \
|
||||
core_write.cpp \
|
||||
eccryptoverify.cpp \
|
||||
ecwrapper.cpp \
|
||||
hash.cpp \
|
||||
key.cpp \
|
||||
keystore.cpp \
|
||||
netbase.cpp \
|
||||
primitives/block.cpp \
|
||||
primitives/transaction.cpp \
|
||||
protocol.cpp \
|
||||
pubkey.cpp \
|
||||
scheduler.cpp \
|
||||
script/interpreter.cpp \
|
||||
script/script.cpp \
|
||||
script/script_error.cpp \
|
||||
script/sign.cpp \
|
||||
script/standard.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
# util: shared between all executables.
|
||||
# This library *must* be included to make sure that the glibc
|
||||
# backward-compatibility objects and their sanity checks are linked.
|
||||
libbitcoin_util_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
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
|
||||
@@ -364,9 +369,9 @@ endif
|
||||
# cli: zcash-cli
|
||||
libbitcoin_cli_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
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
|
||||
#
|
||||
@@ -381,15 +386,15 @@ komodod_SOURCES += bitcoind-res.rc
|
||||
endif
|
||||
|
||||
komodod_LDADD = \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH) \
|
||||
$(LIBLEVELDB) \
|
||||
$(LIBMEMENV) \
|
||||
$(LIBSECP256K1)
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH) \
|
||||
$(LIBLEVELDB) \
|
||||
$(LIBMEMENV) \
|
||||
$(LIBSECP256K1)
|
||||
|
||||
if ENABLE_ZMQ
|
||||
komodod_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
|
||||
@@ -400,38 +405,60 @@ komodod_LDADD += libbitcoin_wallet.a
|
||||
endif
|
||||
|
||||
komodod_LDADD += \
|
||||
$(BOOST_LIBS) \
|
||||
$(BDB_LIBS) \
|
||||
$(SSL_LIBS) \
|
||||
$(CRYPTO_LIBS) \
|
||||
$(MINIUPNPC_LIBS) \
|
||||
$(EVENT_PTHREADS_LIBS) \
|
||||
$(EVENT_LIBS) \
|
||||
$(LIBZCASH) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH_LIBS)
|
||||
$(BOOST_LIBS) \
|
||||
$(BDB_LIBS) \
|
||||
$(SSL_LIBS) \
|
||||
$(CRYPTO_LIBS) \
|
||||
$(MINIUPNPC_LIBS) \
|
||||
$(EVENT_PTHREADS_LIBS) \
|
||||
$(EVENT_LIBS) \
|
||||
$(LIBZCASH) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH_LIBS)
|
||||
|
||||
# bitcoin-cli binary #
|
||||
komodo_cli_SOURCES = bitcoin-cli.cpp
|
||||
komodo_cli_CPPFLAGS = $(BITCOIN_INCLUDES) $(EVENT_CFLAGS)
|
||||
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) \
|
||||
$(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)
|
||||
endif
|
||||
|
||||
# zcash-tx binary #
|
||||
komodo_tx_SOURCES = komodo-tx.cpp
|
||||
@@ -444,33 +471,33 @@ endif
|
||||
|
||||
# FIXME: Is libzcash needed for zcash_tx?
|
||||
komodo_tx_LDADD = \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBSECP256K1) \
|
||||
$(LIBZCASH) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH_LIBS)
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBSECP256K1) \
|
||||
$(LIBZCASH) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBZCASH_LIBS)
|
||||
|
||||
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) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
|
||||
|
||||
@@ -484,25 +511,25 @@ libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT
|
||||
if BUILD_BITCOIN_LIBS
|
||||
include_HEADERS = script/zcashconsensus.h
|
||||
libzcashconsensus_la_SOURCES = \
|
||||
crypto/equihash.cpp \
|
||||
crypto/hmac_sha512.cpp \
|
||||
crypto/ripemd160.cpp \
|
||||
crypto/sha1.cpp \
|
||||
crypto/sha256.cpp \
|
||||
crypto/sha512.cpp \
|
||||
eccryptoverify.cpp \
|
||||
ecwrapper.cpp \
|
||||
hash.cpp \
|
||||
primitives/transaction.cpp \
|
||||
pubkey.cpp \
|
||||
script/zcashconsensus.cpp \
|
||||
script/interpreter.cpp \
|
||||
script/script.cpp \
|
||||
uint256.cpp \
|
||||
utilstrencodings.cpp
|
||||
crypto/equihash.cpp \
|
||||
crypto/hmac_sha512.cpp \
|
||||
crypto/ripemd160.cpp \
|
||||
crypto/sha1.cpp \
|
||||
crypto/sha256.cpp \
|
||||
crypto/sha512.cpp \
|
||||
eccryptoverify.cpp \
|
||||
ecwrapper.cpp \
|
||||
hash.cpp \
|
||||
primitives/transaction.cpp \
|
||||
pubkey.cpp \
|
||||
script/zcashconsensus.cpp \
|
||||
script/interpreter.cpp \
|
||||
script/script.cpp \
|
||||
uint256.cpp \
|
||||
utilstrencodings.cpp
|
||||
|
||||
if GLIBC_BACK_COMPAT
|
||||
libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp
|
||||
libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp
|
||||
endif
|
||||
|
||||
libzcashconsensus_la_LDFLAGS = -no-undefined $(RELDFLAGS)
|
||||
@@ -530,7 +557,7 @@ clean-local:
|
||||
|
||||
.mm.o:
|
||||
$(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
$(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
check-symbols: $(bin_PROGRAMS)
|
||||
if GLIBC_BACK_COMPAT
|
||||
|
||||
@@ -15,93 +15,96 @@ 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)
|
||||
test_test_bitcoin_CPPFLAGS = -fopenmp $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
|
||||
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
|
||||
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
|
||||
if ENABLE_WALLET
|
||||
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
@@ -132,6 +135,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
|
||||
|
||||
|
||||
82
src/addressindex.h
Normal file
82
src/addressindex.h
Normal file
@@ -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
|
||||
@@ -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))
|
||||
|
||||
@@ -141,6 +141,7 @@ public:
|
||||
|
||||
CTxDestination Get() const;
|
||||
bool GetKeyID(CKeyID &keyID) const;
|
||||
bool GetIndexKey(uint160& hashBytes, int& type) const;
|
||||
bool IsScript() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
26
src/init.cpp
26
src/init.cpp
@@ -345,7 +345,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=<ip>", _("Add a node to connect to and attempt to keep the connection open"));
|
||||
strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), 100));
|
||||
@@ -1276,18 +1278,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));
|
||||
@@ -1308,7 +1326,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);
|
||||
|
||||
@@ -27,14 +27,14 @@ void HandleError(const leveldb::Status& status) throw(leveldb_error)
|
||||
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());
|
||||
|
||||
@@ -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 <typename K, typename V>
|
||||
|
||||
269
src/main.cpp
269
src/main.cpp
@@ -62,6 +62,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;
|
||||
@@ -1335,6 +1338,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);
|
||||
@@ -1342,6 +1355,55 @@ 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<std::pair<uint256, unsigned int> > &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<std::pair<CAddressIndexKey, CAmount> > &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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &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)
|
||||
{
|
||||
@@ -2069,11 +2131,46 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
||||
if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
|
||||
return error("DisconnectBlock(): block and undo data inconsistent");
|
||||
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressUnspentIndex;
|
||||
std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> > 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<unsigned char> 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<unsigned char> 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.
|
||||
{
|
||||
@@ -2110,6 +2207,39 @@ 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<unsigned char> 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<unsigned char> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2125,6 +2255,14 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2312,6 +2450,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
vPos.reserve(block.vtx.size());
|
||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressUnspentIndex;
|
||||
std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> > spentIndex;
|
||||
|
||||
// Construct the incremental merkle tree at the current
|
||||
// block position,
|
||||
auto old_tree_root = view.GetBestAnchor();
|
||||
@@ -2333,6 +2475,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
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)
|
||||
@@ -2350,6 +2493,43 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
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 <unsigned char>(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
||||
addressType = 2;
|
||||
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||
hashBytes = uint160(vector <unsigned char>(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.
|
||||
@@ -2365,6 +2545,36 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
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<unsigned char> 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<unsigned char> 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;
|
||||
@@ -2433,6 +2643,41 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
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());
|
||||
|
||||
@@ -3840,6 +4085,18 @@ bool static LoadBlockIndexDB()
|
||||
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)
|
||||
{
|
||||
@@ -4023,6 +4280,18 @@ bool InitBlockIndex() {
|
||||
// Use the provided setting for -txindex in the new database
|
||||
fTxIndex = GetBoolArg("-txindex", true);
|
||||
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||
|
||||
// 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)
|
||||
|
||||
346
src/main.h
346
src/main.h
@@ -21,6 +21,7 @@
|
||||
#include "script/script.h"
|
||||
#include "script/sigcache.h"
|
||||
#include "script/standard.h"
|
||||
#include "spentindex.h"
|
||||
#include "sync.h"
|
||||
#include "tinyformat.h"
|
||||
#include "txmempool.h"
|
||||
@@ -94,6 +95,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);
|
||||
@@ -260,6 +267,338 @@ struct CNodeStateStats {
|
||||
std::vector<int> vHeightInFlight;
|
||||
};
|
||||
|
||||
struct CTimestampIndexIteratorKey {
|
||||
unsigned int timestamp;
|
||||
|
||||
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||
return 4;
|
||||
}
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
ser_writedata32be(s, timestamp);
|
||||
}
|
||||
template<typename Stream>
|
||||
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<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
ser_writedata32be(s, timestamp);
|
||||
blockHash.Serialize(s, nType, nVersion);
|
||||
}
|
||||
template<typename Stream>
|
||||
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<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
blockHash.Serialize(s, nType, nVersion);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
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<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
ser_writedata32be(s, ltimestamp);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
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<typename Stream>
|
||||
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<typename Stream>
|
||||
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 <typename Stream, typename Operation>
|
||||
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<typename Stream>
|
||||
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<typename Stream>
|
||||
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<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
ser_writedata8(s, type);
|
||||
hashBytes.Serialize(s, nType, nVersion);
|
||||
}
|
||||
template<typename Stream>
|
||||
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<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
ser_writedata8(s, type);
|
||||
hashBytes.Serialize(s, nType, nVersion);
|
||||
ser_writedata32be(s, blockHeight);
|
||||
}
|
||||
template<typename Stream>
|
||||
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
|
||||
@@ -394,6 +733,13 @@ public:
|
||||
ScriptError GetScriptError() const { return error; }
|
||||
};
|
||||
|
||||
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &hashes);
|
||||
bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);
|
||||
bool GetAddressIndex(uint160 addressHash, int type,
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
|
||||
int start = 0, int end = 0);
|
||||
bool GetAddressUnspent(uint160 addressHash, int type,
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs);
|
||||
|
||||
/** Functions for disk access for blocks */
|
||||
bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "checkpoints.h"
|
||||
#include "base58.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "main.h"
|
||||
#include "primitives/transaction.h"
|
||||
#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 <stdint.h>
|
||||
|
||||
@@ -101,6 +106,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<unsigned char> 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<unsigned char> 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);
|
||||
@@ -284,6 +395,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<std::pair<uint256, unsigned int> > 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<std::pair<uint256, unsigned int> >::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)
|
||||
|
||||
@@ -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 },
|
||||
|
||||
528
src/rpcmisc.cpp
528
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"
|
||||
@@ -555,3 +556,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<std::pair<uint160, int> > &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<UniValue> values = addressValues.getValues();
|
||||
|
||||
for (std::vector<UniValue>::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<CAddressUnspentKey, CAddressUnspentValue> a,
|
||||
std::pair<CAddressUnspentKey, CAddressUnspentValue> b) {
|
||||
return a.second.blockHeight < b.second.blockHeight;
|
||||
}
|
||||
|
||||
bool timestampSort(std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> a,
|
||||
std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> 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<std::pair<uint160, int> > addresses;
|
||||
|
||||
if (!getAddressesFromParams(params, addresses)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||
}
|
||||
|
||||
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > 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<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> >::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<std::pair<uint160, int> > addresses;
|
||||
|
||||
if (!getAddressesFromParams(params, addresses)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||
}
|
||||
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
|
||||
for (std::vector<std::pair<uint160, int> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<uint160, int> > addresses;
|
||||
|
||||
if (!getAddressesFromParams(params, addresses)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||
}
|
||||
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||
|
||||
for (std::vector<std::pair<uint160, int> >::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<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<uint160, int> > addresses;
|
||||
|
||||
if (!getAddressesFromParams(params, addresses)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||
}
|
||||
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||
|
||||
for (std::vector<std::pair<uint160, int> >::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<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<uint160, int> > 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<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||
|
||||
for (std::vector<std::pair<uint160, int> >::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<std::pair<int, std::string> > txids;
|
||||
UniValue result(UniValue::VARR);
|
||||
|
||||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<int, std::string> >::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;
|
||||
}
|
||||
@@ -64,7 +64,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()));
|
||||
|
||||
@@ -114,9 +116,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("version", tx.nVersion));
|
||||
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
|
||||
UniValue vin(UniValue::VARR);
|
||||
@@ -131,6 +134,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);
|
||||
@@ -156,6 +172,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));
|
||||
@@ -163,18 +189,77 @@ 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,7 +357,6 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp)
|
||||
+ HelpExampleRpc("getrawtransaction", "\"mytxid\", 1")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
uint256 hash = ParseHashV(params[0], "parameter 1");
|
||||
|
||||
@@ -282,8 +366,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);
|
||||
|
||||
@@ -292,7 +397,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;
|
||||
}
|
||||
|
||||
@@ -586,7 +691,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp)
|
||||
" \"vjoinsplit\" : [ (array of json objects, only for version >= 2)\n"
|
||||
" {\n"
|
||||
" \"vpub_old\" : x.xxx, (numeric) public input value in ZEC\n"
|
||||
" \"vpub_new\" : x.xxx, (numeric) public output value in ZEC\n"
|
||||
" \"vpub_new\" : x.xxx, (numeric) public output value in ZECn"
|
||||
" \"anchor\" : \"hex\", (string) the anchor\n"
|
||||
" \"nullifiers\" : [ (json array of string)\n"
|
||||
" \"hex\" (string) input note nullifier\n"
|
||||
|
||||
@@ -280,6 +280,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 },
|
||||
@@ -291,6 +293,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 },
|
||||
@@ -329,6 +332,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 */
|
||||
|
||||
@@ -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);
|
||||
@@ -270,6 +275,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);
|
||||
@@ -279,6 +286,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);
|
||||
|
||||
|
||||
@@ -211,6 +211,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:
|
||||
|
||||
@@ -561,6 +561,8 @@ public:
|
||||
*/
|
||||
unsigned int GetSigOpCount(const CScript& scriptSig) const;
|
||||
|
||||
bool IsPayToPublicKeyHash() const;
|
||||
|
||||
bool IsPayToScriptHash() const;
|
||||
|
||||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
||||
|
||||
@@ -95,6 +95,11 @@ template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
|
||||
obj = htole32(obj);
|
||||
s.write((char*)&obj, 4);
|
||||
}
|
||||
template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
|
||||
{
|
||||
obj = htobe32(obj);
|
||||
s.write((char*)&obj, 4);
|
||||
}
|
||||
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
|
||||
{
|
||||
obj = htole64(obj);
|
||||
@@ -118,6 +123,12 @@ template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
|
||||
s.read((char*)&obj, 4);
|
||||
return le32toh(obj);
|
||||
}
|
||||
template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
|
||||
{
|
||||
uint32_t obj;
|
||||
s.read((char*)&obj, 4);
|
||||
return be32toh(obj);
|
||||
}
|
||||
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
|
||||
{
|
||||
uint64_t obj;
|
||||
|
||||
98
src/spentindex.h
Normal file
98
src/spentindex.h
Normal file
@@ -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 <typename Stream, typename Operation>
|
||||
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 <typename Stream, typename Operation>
|
||||
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
|
||||
59
src/test/script_P2PKH_tests.cpp
Normal file
59
src/test/script_P2PKH_tests.cpp
Normal file
@@ -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 <boost/test/unit_test.hpp>
|
||||
|
||||
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()
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "key.h"
|
||||
#include "main.h"
|
||||
#include "random.h"
|
||||
#include "chainparamsbase.h"
|
||||
#include "txdb.h"
|
||||
#include "ui_interface.h"
|
||||
#include "util.h"
|
||||
|
||||
61
src/test/wallet-utility.py
Normal file
61
src/test/wallet-utility.py
Normal file
@@ -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)
|
||||
210
src/txdb.cpp
210
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';
|
||||
@@ -65,7 +70,7 @@ void static BatchWriteHashBestAnchor(CLevelDBBatch &batch, const uint256 &hash)
|
||||
batch.Write(DB_BEST_ANCHOR, hash);
|
||||
}
|
||||
|
||||
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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +160,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) {
|
||||
@@ -257,6 +262,197 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos>
|
||||
return WriteBatch(batch);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) {
|
||||
return Read(make_pair(DB_SPENTINDEX, key), value);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::UpdateSpentIndex(const std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> >&vect) {
|
||||
CLevelDBBatch batch;
|
||||
for (std::vector<std::pair<CSpentIndexKey,CSpentIndexValue> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue > >&vect) {
|
||||
CLevelDBBatch batch;
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs) {
|
||||
|
||||
boost::scoped_ptr<leveldb::Iterator> 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<std::pair<CAddressIndexKey, CAmount > >&vect) {
|
||||
CLevelDBBatch batch;
|
||||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<CAddressIndexKey, CAmount > >&vect) {
|
||||
CLevelDBBatch batch;
|
||||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
|
||||
int start, int end) {
|
||||
|
||||
boost::scoped_ptr<leveldb::Iterator> 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<std::pair<uint256, unsigned int> > &hashes) {
|
||||
|
||||
boost::scoped_ptr<leveldb::Iterator> 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');
|
||||
}
|
||||
@@ -271,6 +467,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<leveldb::Iterator> pcursor(NewIterator());
|
||||
|
||||
28
src/txdb.h
28
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)
|
||||
@@ -52,7 +63,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&);
|
||||
@@ -64,9 +75,24 @@ public:
|
||||
bool ReadReindexing(bool &fReindex);
|
||||
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
||||
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
||||
bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);
|
||||
bool UpdateSpentIndex(const std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> >&vect);
|
||||
bool UpdateAddressUnspentIndex(const std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue > >&vect);
|
||||
bool ReadAddressUnspentIndex(uint160 addressHash, int type,
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &vect);
|
||||
bool WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
|
||||
bool EraseAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
|
||||
bool ReadAddressIndex(uint160 addressHash, int type,
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > &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<std::pair<uint256, unsigned int> > &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
|
||||
|
||||
@@ -114,6 +114,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<CMempoolAddressDeltaKey> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||
std::pair<addressDeltaMap::iterator,bool> 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<std::pair<uint160, int> > &addresses,
|
||||
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &results)
|
||||
{
|
||||
LOCK(cs);
|
||||
for (std::vector<std::pair<uint160, int> >::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<CMempoolAddressDeltaKey> keys = (*it).second;
|
||||
for (std::vector<CMempoolAddressDeltaKey>::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<CSpentIndexKey> 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<unsigned char> (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
||||
addressType = 2;
|
||||
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||
addressHash = uint160(vector<unsigned char> (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<CSpentIndexKey> keys = (*it).second;
|
||||
for (std::vector<CSpentIndexKey>::iterator mit = keys.begin(); mit != keys.end(); mit++) {
|
||||
mapSpent.erase(*mit);
|
||||
}
|
||||
mapSpentInserted.erase(it);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& removed, bool fRecursive)
|
||||
{
|
||||
@@ -163,6 +301,8 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
|
||||
mapTx.erase(hash);
|
||||
nTransactionsUpdated++;
|
||||
minerPolicyEstimator->removeTx(hash);
|
||||
removeAddressIndex(hash);
|
||||
removeSpentIndex(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "addressindex.h"
|
||||
#include "spentindex.h"
|
||||
#include "amount.h"
|
||||
#include "coins.h"
|
||||
#include "primitives/transaction.h"
|
||||
@@ -101,6 +103,21 @@ private:
|
||||
public:
|
||||
mutable CCriticalSection cs;
|
||||
std::map<uint256, CTxMemPoolEntry> mapTx;
|
||||
|
||||
private:
|
||||
typedef std::map<CMempoolAddressDeltaKey, CMempoolAddressDelta, CMempoolAddressDeltaKeyCompare> addressDeltaMap;
|
||||
addressDeltaMap mapAddress;
|
||||
|
||||
typedef std::map<uint256, std::vector<CMempoolAddressDeltaKey> > addressDeltaMapInserted;
|
||||
addressDeltaMapInserted mapAddressInserted;
|
||||
|
||||
typedef std::map<CSpentIndexKey, CSpentIndexValue, CSpentIndexKeyCompare> mapSpentIndex;
|
||||
mapSpentIndex mapSpent;
|
||||
|
||||
typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted;
|
||||
mapSpentIndexInserted mapSpentInserted;
|
||||
|
||||
public:
|
||||
std::map<COutPoint, CInPoint> mapNextTx;
|
||||
std::map<uint256, const CTransaction*> mapNullifiers;
|
||||
std::map<uint256, std::pair<double, CAmount> > mapDeltas;
|
||||
@@ -118,6 +135,14 @@ public:
|
||||
void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; }
|
||||
|
||||
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
|
||||
void addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view);
|
||||
bool getAddressIndex(std::vector<std::pair<uint160, int> > &addresses,
|
||||
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &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<CTransaction>& removed, bool fRecursive = false);
|
||||
void removeWithAnchor(const uint256 &invalidRoot);
|
||||
void removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight);
|
||||
|
||||
339
src/wallet-utility.cpp
Normal file
339
src/wallet-utility.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// Include local headers
|
||||
#include "wallet/walletdb.h"
|
||||
#include "util.h"
|
||||
#include "base58.h"
|
||||
#include "wallet/crypter.h"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
||||
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=<directory> to tell the program where your wallet is"
|
||||
<< std::endl
|
||||
<< " -wallet=<name> (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=<walletpassphrase> if you have encrypted private keys stored in your wallet"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
||||
class WalletUtilityDB : public CDB
|
||||
{
|
||||
private:
|
||||
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
|
||||
MasterKeyMap mapMasterKeys;
|
||||
unsigned int nMasterKeyMaxID;
|
||||
SecureString mPass;
|
||||
std::vector<CKeyingMaterial> 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<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext);
|
||||
bool Unlock();
|
||||
bool DecryptKey(const std::vector<unsigned char>& 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<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
|
||||
{
|
||||
CCrypter cKeyCrypter;
|
||||
std::vector<unsigned char> 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<unsigned char>& 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<unsigned char> 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;
|
||||
}
|
||||
Reference in New Issue
Block a user