Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/Makefile.am b/src/Makefile.am
index d6ac6e127..bc2b1aff9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,418 +1,421 @@
DIST_SUBDIRS = secp256k1
AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS)
if EMBEDDED_LEVELDB
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv
LIBLEVELDB += $(builddir)/leveldb/libleveldb.a
LIBMEMENV += $(builddir)/leveldb/libmemenv.a
# NOTE: This dependency is not strictly necessary, but without it make may try to build both in parallel, which breaks the LevelDB build system in a race
$(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)"
endif
BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config
BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
LIBBITCOIN_SERVER=libbitcoin_server.a
LIBBITCOIN_WALLET=libbitcoin_wallet.a
LIBBITCOIN_COMMON=libbitcoin_common.a
LIBBITCOIN_CLI=libbitcoin_cli.a
LIBBITCOIN_UTIL=libbitcoin_util.a
LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a
LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a
LIBBITCOINQT=qt/libbitcoinqt.a
LIBSECP256K1=secp256k1/libsecp256k1.la
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
# 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:
noinst_LIBRARIES = \
crypto/libbitcoin_crypto.a \
libbitcoin_util.a \
libbitcoin_common.a \
univalue/libbitcoin_univalue.a \
libbitcoin_server.a \
libbitcoin_cli.a
if ENABLE_WALLET
BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
noinst_LIBRARIES += libbitcoin_wallet.a
endif
if BUILD_BITCOIN_LIBS
lib_LTLIBRARIES = libbitcoinconsensus.la
LIBBITCOIN_CONSENSUS=libbitcoinconsensus.la
else
LIBBITCOIN_CONSENSUS=
endif
bin_PROGRAMS =
TESTS =
if BUILD_BITCOIND
bin_PROGRAMS += bitcoind
endif
if BUILD_BITCOIN_UTILS
bin_PROGRAMS += bitcoin-cli bitcoin-tx
endif
.PHONY: FORCE
# bitcoin core #
BITCOIN_CORE_H = \
addrman.h \
alert.h \
allocators.h \
amount.h \
+ arith_uint256.h \
base58.h \
bloom.h \
chain.h \
- chainparams.h \
chainparamsbase.h \
+ chainparams.h \
chainparamsseeds.h \
checkpoints.h \
checkqueue.h \
clientversion.h \
coincontrol.h \
coins.h \
compat.h \
compressor.h \
- primitives/block.h \
- primitives/transaction.h \
core_io.h \
crypter.h \
db.h \
eccryptoverify.h \
ecwrapper.h \
hash.h \
init.h \
key.h \
keystore.h \
leveldbwrapper.h \
limitedmap.h \
main.h \
merkleblock.h \
miner.h \
mruset.h \
netbase.h \
net.h \
noui.h \
pow.h \
+ primitives/block.h \
+ primitives/transaction.h \
protocol.h \
pubkey.h \
random.h \
rpcclient.h \
rpcprotocol.h \
rpcserver.h \
script/interpreter.h \
+ script/script_error.h \
script/script.h \
script/sigcache.h \
script/sign.h \
script/standard.h \
- script/script_error.h \
serialize.h \
streams.h \
sync.h \
threadsafety.h \
timedata.h \
tinyformat.h \
txdb.h \
txmempool.h \
ui_interface.h \
uint256.h \
undo.h \
util.h \
- utilstrencodings.h \
utilmoneystr.h \
+ utilstrencodings.h \
utiltime.h \
version.h \
+ walletdb.h \
wallet.h \
wallet_ismine.h \
- walletdb.h \
compat/sanity.h
JSON_H = \
json/json_spirit.h \
json/json_spirit_error_position.h \
json/json_spirit_reader.h \
json/json_spirit_reader_template.h \
json/json_spirit_stream_reader.h \
json/json_spirit_utils.h \
json/json_spirit_value.h \
json/json_spirit_writer.h \
json/json_spirit_writer_template.h
obj/build.h: FORCE
@$(MKDIR_P) $(builddir)/obj
@$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \
$(abs_top_srcdir)
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# server: shared between bitcoind and bitcoin-qt
libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS)
libbitcoin_server_a_SOURCES = \
addrman.cpp \
alert.cpp \
bloom.cpp \
chain.cpp \
checkpoints.cpp \
init.cpp \
leveldbwrapper.cpp \
main.cpp \
merkleblock.cpp \
miner.cpp \
net.cpp \
noui.cpp \
pow.cpp \
rest.cpp \
rpcblockchain.cpp \
rpcmining.cpp \
rpcmisc.cpp \
rpcnet.cpp \
rpcrawtransaction.cpp \
rpcserver.cpp \
script/sigcache.cpp \
timedata.cpp \
txdb.cpp \
txmempool.cpp \
$(JSON_H) \
$(BITCOIN_CORE_H)
# wallet: shared between bitcoind and bitcoin-qt, but only linked
# when wallet enabled
libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_SOURCES = \
db.cpp \
crypter.cpp \
rpcdump.cpp \
rpcwallet.cpp \
wallet.cpp \
wallet_ismine.cpp \
walletdb.cpp \
$(BITCOIN_CORE_H)
# crypto primitives library
crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES)
crypto_libbitcoin_crypto_a_SOURCES = \
crypto/sha1.cpp \
crypto/sha256.cpp \
crypto/sha512.cpp \
crypto/hmac_sha256.cpp \
crypto/rfc6979_hmac_sha256.cpp \
crypto/hmac_sha512.cpp \
crypto/ripemd160.cpp \
crypto/common.h \
crypto/sha256.h \
crypto/sha512.h \
crypto/hmac_sha256.h \
crypto/rfc6979_hmac_sha256.h \
crypto/hmac_sha512.h \
crypto/sha1.h \
crypto/ripemd160.h
# univalue JSON library
univalue_libbitcoin_univalue_a_SOURCES = \
univalue/univalue.cpp \
univalue/univalue_read.cpp \
univalue/univalue_write.cpp \
univalue/univalue_escapes.h \
univalue/univalue.h
# common: shared between bitcoind, and bitcoin-qt and non-server tools
libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_common_a_SOURCES = \
allocators.cpp \
amount.cpp \
base58.cpp \
chainparams.cpp \
coins.cpp \
compressor.cpp \
primitives/block.cpp \
primitives/transaction.cpp \
core_read.cpp \
core_write.cpp \
eccryptoverify.cpp \
ecwrapper.cpp \
hash.cpp \
key.cpp \
keystore.cpp \
netbase.cpp \
protocol.cpp \
pubkey.cpp \
script/interpreter.cpp \
script/script.cpp \
script/sign.cpp \
script/standard.cpp \
script/script_error.cpp \
$(BITCOIN_CORE_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 = \
- compat/strnlen.cpp \
- compat/glibc_sanity.cpp \
- compat/glibcxx_sanity.cpp \
+ arith_uint256.cpp \
chainparamsbase.cpp \
clientversion.cpp \
+ compat/glibc_sanity.cpp \
+ compat/glibcxx_sanity.cpp \
+ compat/strnlen.cpp \
random.cpp \
rpcprotocol.cpp \
sync.cpp \
uint256.cpp \
util.cpp \
- utilstrencodings.cpp \
utilmoneystr.cpp \
+ utilstrencodings.cpp \
utiltime.cpp \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp
libbitcoin_util_a_SOURCES += compat/glibcxx_compat.cpp
endif
# cli: shared between bitcoin-cli and bitcoin-qt
libbitcoin_cli_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_cli_a_SOURCES = \
rpcclient.cpp \
$(BITCOIN_CORE_H)
nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
#
# bitcoind binary #
bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBLEVELDB) \
$(LIBMEMENV) \
$(LIBSECP256K1)
if ENABLE_WALLET
bitcoind_LDADD += libbitcoin_wallet.a
endif
bitcoind_SOURCES = bitcoind.cpp
#
if TARGET_WINDOWS
bitcoind_SOURCES += bitcoind-res.rc
endif
bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
bitcoind_CPPFLAGS = $(BITCOIN_INCLUDES)
bitcoind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
# bitcoin-cli binary #
bitcoin_cli_LDADD = \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_UTIL) \
$(BOOST_LIBS) \
$(SSL_LIBS) \
$(CRYPTO_LIBS)
bitcoin_cli_SOURCES = \
bitcoin-cli.cpp
bitcoin_cli_CPPFLAGS = $(BITCOIN_INCLUDES)
#
# bitcoin-tx binary #
bitcoin_tx_LDADD = \
$(LIBBITCOIN_UNIVALUE) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBSECP256K1) \
$(BOOST_LIBS) \
$(CRYPTO_LIBS)
bitcoin_tx_SOURCES = bitcoin-tx.cpp
bitcoin_tx_CPPFLAGS = $(BITCOIN_INCLUDES)
#
bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if TARGET_WINDOWS
bitcoin_cli_SOURCES += bitcoin-cli-res.rc
endif
bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if BUILD_BITCOIN_LIBS
include_HEADERS = script/bitcoinconsensus.h
libbitcoinconsensus_la_SOURCES = \
- primitives/transaction.cpp \
+ arith_uint256.cpp \
crypto/hmac_sha512.cpp \
+ crypto/ripemd160.cpp \
crypto/sha1.cpp \
crypto/sha256.cpp \
crypto/sha512.cpp \
- crypto/ripemd160.cpp \
eccryptoverify.cpp \
ecwrapper.cpp \
hash.cpp \
+ primitives/transaction.cpp \
pubkey.cpp \
- script/script.cpp \
- script/interpreter.cpp \
script/bitcoinconsensus.cpp \
+ script/interpreter.cpp \
+ script/script.cpp \
uint256.cpp \
utilstrencodings.cpp
if GLIBC_BACK_COMPAT
libbitcoinconsensus_la_SOURCES += compat/glibc_compat.cpp
libbitcoinconsensus_la_SOURCES += compat/glibcxx_compat.cpp
endif
libbitcoinconsensus_la_LDFLAGS = -no-undefined $(RELDFLAGS)
libbitcoinconsensus_la_LIBADD = $(CRYPTO_LIBS)
libbitcoinconsensus_la_CPPFLAGS = $(CRYPTO_CFLAGS) -I$(builddir)/obj -DBUILD_BITCOIN_INTERNAL
if USE_LIBSECP256K1
libbitcoinconsensus_la_LIBADD += secp256k1/libsecp256k1.la
endif
endif
CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno
DISTCLEANFILES = obj/build.h
EXTRA_DIST = leveldb
clean-local:
-$(MAKE) -C leveldb clean
-$(MAKE) -C secp256k1 clean
rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno
-rm -f config.h
.rc.o:
@test -f $(WINDRES)
$(AM_V_GEN) $(WINDRES) -DWINDRES_PREPROC -i $< -o $@
.mm.o:
$(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(CXXFLAGS) -c -o $@ $<
%.pb.cc %.pb.h: %.proto
@test -f $(PROTOC)
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $(<D) $<)
if ENABLE_TESTS
include Makefile.test.include
endif
if ENABLE_QT
include Makefile.qt.include
endif
if ENABLE_QT_TESTS
include Makefile.qttest.include
endif
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 5f388e280..2f10702d8 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -1,127 +1,128 @@
TESTS += test/test_bitcoin
bin_PROGRAMS += test/test_bitcoin
TEST_SRCDIR = test
TEST_BINARY=test/test_bitcoin$(EXEEXT)
EXTRA_DIST += \
test/bctest.py \
test/bitcoin-util-test.py \
test/data/bitcoin-util-test.json \
test/data/blanktx.hex \
test/data/tt-delin1-out.hex \
test/data/tt-delout1-out.hex \
test/data/tt-locktime317000-out.hex \
test/data/tx394b54bb.hex \
test/data/txcreate1.hex \
test/data/txcreate2.hex \
test/data/txcreatesign.hex
JSON_TEST_FILES = \
test/data/script_valid.json \
test/data/base58_keys_valid.json \
test/data/sig_canonical.json \
test/data/sig_noncanonical.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
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/alert_tests.cpp \
test/allocator_tests.cpp \
test/base32_tests.cpp \
test/base58_tests.cpp \
test/base64_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/getarg_tests.cpp \
test/hash_tests.cpp \
test/key_tests.cpp \
test/main_tests.cpp \
test/miner_tests.cpp \
test/mruset_tests.cpp \
test/multisig_tests.cpp \
test/netbase_tests.cpp \
test/pmt_tests.cpp \
test/rpc_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/timedata_tests.cpp \
test/transaction_tests.cpp \
test/uint256_tests.cpp \
test/univalue_tests.cpp \
test/util_tests.cpp
if ENABLE_WALLET
BITCOIN_TESTS += \
test/accounting_tests.cpp \
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 = $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS)
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
endif
test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
$(BITCOIN_TESTS): $(GENERATED_TEST_FILES)
CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES)
CLEANFILES += $(CLEAN_BITCOIN_TEST)
bitcoin_test: $(TEST_BINARY)
bitcoin_test_check: $(TEST_BINARY) FORCE
$(MAKE) check-TESTS TESTS=$^
bitcoin_test_clean : FORCE
rm -f $(CLEAN_BITCOIN_TEST) $(test_test_bitcoin_OBJECTS) $(TEST_BINARY)
check-local:
@echo "Running test/bitcoin-util-test.py..."
$(AM_V_at)srcdir=$(srcdir) PYTHONPATH=$(builddir)/test $(srcdir)/test/bitcoin-util-test.py
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
%.json.h: %.json
@$(MKDIR_P) $(@D)
@echo "namespace json_tests{" > $@
@echo "static unsigned const char $(*F)[] = {" >> $@
@$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' >> $@
@echo "};};" >> $@
@echo "Generated $@"
%.raw.h: %.raw
@$(MKDIR_P) $(@D)
@echo "namespace alert_tests{" > $@
@echo "static unsigned const char $(*F)[] = {" >> $@
@$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' >> $@
@echo "};};" >> $@
@echo "Generated $@"
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 1982db52a..1e08ae772 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -1,529 +1,529 @@
// Copyright (c) 2012 Pieter Wuille
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "addrman.h"
#include "hash.h"
#include "serialize.h"
#include "streams.h"
using namespace std;
int CAddrInfo::GetTriedBucket(const std::vector<unsigned char>& nKey) const
{
CDataStream ss1(SER_GETHASH, 0);
std::vector<unsigned char> vchKey = GetKey();
ss1 << nKey << vchKey;
- uint64_t hash1 = Hash(ss1.begin(), ss1.end()).GetLow64();
+ uint64_t hash1 = Hash(ss1.begin(), ss1.end()).GetCheapHash();
CDataStream ss2(SER_GETHASH, 0);
std::vector<unsigned char> vchGroupKey = GetGroup();
ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP);
- uint64_t hash2 = Hash(ss2.begin(), ss2.end()).GetLow64();
+ uint64_t hash2 = Hash(ss2.begin(), ss2.end()).GetCheapHash();
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
}
int CAddrInfo::GetNewBucket(const std::vector<unsigned char>& nKey, const CNetAddr& src) const
{
CDataStream ss1(SER_GETHASH, 0);
std::vector<unsigned char> vchGroupKey = GetGroup();
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
ss1 << nKey << vchGroupKey << vchSourceGroupKey;
- uint64_t hash1 = Hash(ss1.begin(), ss1.end()).GetLow64();
+ uint64_t hash1 = Hash(ss1.begin(), ss1.end()).GetCheapHash();
CDataStream ss2(SER_GETHASH, 0);
ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP);
- uint64_t hash2 = Hash(ss2.begin(), ss2.end()).GetLow64();
+ uint64_t hash2 = Hash(ss2.begin(), ss2.end()).GetCheapHash();
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
}
bool CAddrInfo::IsTerrible(int64_t nNow) const
{
if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
return false;
if (nTime > nNow + 10 * 60) // came in a flying DeLorean
return true;
if (nTime == 0 || nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) // not seen in recent history
return true;
if (nLastSuccess == 0 && nAttempts >= ADDRMAN_RETRIES) // tried N times and never a success
return true;
if (nNow - nLastSuccess > ADDRMAN_MIN_FAIL_DAYS * 24 * 60 * 60 && nAttempts >= ADDRMAN_MAX_FAILURES) // N successive failures in the last week
return true;
return false;
}
double CAddrInfo::GetChance(int64_t nNow) const
{
double fChance = 1.0;
int64_t nSinceLastSeen = nNow - nTime;
int64_t nSinceLastTry = nNow - nLastTry;
if (nSinceLastSeen < 0)
nSinceLastSeen = 0;
if (nSinceLastTry < 0)
nSinceLastTry = 0;
fChance *= 600.0 / (600.0 + nSinceLastSeen);
// deprioritize very recent attempts away
if (nSinceLastTry < 60 * 10)
fChance *= 0.01;
// deprioritize 50% after each failed attempt
for (int n = 0; n < nAttempts; n++)
fChance /= 1.5;
return fChance;
}
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
{
std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
if (it == mapAddr.end())
return NULL;
if (pnId)
*pnId = (*it).second;
std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
if (it2 != mapInfo.end())
return &(*it2).second;
return NULL;
}
CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
int nId = nIdCount++;
mapInfo[nId] = CAddrInfo(addr, addrSource);
mapAddr[addr] = nId;
mapInfo[nId].nRandomPos = vRandom.size();
vRandom.push_back(nId);
if (pnId)
*pnId = nId;
return &mapInfo[nId];
}
void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
{
if (nRndPos1 == nRndPos2)
return;
assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
int nId1 = vRandom[nRndPos1];
int nId2 = vRandom[nRndPos2];
assert(mapInfo.count(nId1) == 1);
assert(mapInfo.count(nId2) == 1);
mapInfo[nId1].nRandomPos = nRndPos2;
mapInfo[nId2].nRandomPos = nRndPos1;
vRandom[nRndPos1] = nId2;
vRandom[nRndPos2] = nId1;
}
int CAddrMan::SelectTried(int nKBucket)
{
std::vector<int>& vTried = vvTried[nKBucket];
// randomly shuffle the first few elements (using the entire list)
// find the least recently tried among them
int64_t nOldest = -1;
int nOldestPos = -1;
for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++) {
int nPos = GetRandInt(vTried.size() - i) + i;
int nTemp = vTried[nPos];
vTried[nPos] = vTried[i];
vTried[i] = nTemp;
assert(nOldest == -1 || mapInfo.count(nTemp) == 1);
if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) {
nOldest = nTemp;
nOldestPos = nPos;
}
}
return nOldestPos;
}
int CAddrMan::ShrinkNew(int nUBucket)
{
assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size());
std::set<int>& vNew = vvNew[nUBucket];
// first look for deletable items
for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++) {
assert(mapInfo.count(*it));
CAddrInfo& info = mapInfo[*it];
if (info.IsTerrible()) {
if (--info.nRefCount == 0) {
SwapRandom(info.nRandomPos, vRandom.size() - 1);
vRandom.pop_back();
mapAddr.erase(info);
mapInfo.erase(*it);
nNew--;
}
vNew.erase(it);
return 0;
}
}
// otherwise, select four randomly, and pick the oldest of those to replace
int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())};
int nI = 0;
int nOldest = -1;
for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++) {
if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3]) {
assert(nOldest == -1 || mapInfo.count(*it) == 1);
if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime)
nOldest = *it;
}
nI++;
}
assert(mapInfo.count(nOldest) == 1);
CAddrInfo& info = mapInfo[nOldest];
if (--info.nRefCount == 0) {
SwapRandom(info.nRandomPos, vRandom.size() - 1);
vRandom.pop_back();
mapAddr.erase(info);
mapInfo.erase(nOldest);
nNew--;
}
vNew.erase(nOldest);
return 1;
}
void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin)
{
assert(vvNew[nOrigin].count(nId) == 1);
// remove the entry from all new buckets
for (std::vector<std::set<int> >::iterator it = vvNew.begin(); it != vvNew.end(); it++) {
if ((*it).erase(nId))
info.nRefCount--;
}
nNew--;
assert(info.nRefCount == 0);
// which tried bucket to move the entry to
int nKBucket = info.GetTriedBucket(nKey);
std::vector<int>& vTried = vvTried[nKBucket];
// first check whether there is place to just add it
if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) {
vTried.push_back(nId);
nTried++;
info.fInTried = true;
return;
}
// otherwise, find an item to evict
int nPos = SelectTried(nKBucket);
// find which new bucket it belongs to
assert(mapInfo.count(vTried[nPos]) == 1);
int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey);
std::set<int>& vNew = vvNew[nUBucket];
// remove the to-be-replaced tried entry from the tried set
CAddrInfo& infoOld = mapInfo[vTried[nPos]];
infoOld.fInTried = false;
infoOld.nRefCount = 1;
// do not update nTried, as we are going to move something else there immediately
// check whether there is place in that one,
if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE) {
// if so, move it back there
vNew.insert(vTried[nPos]);
} else {
// otherwise, move it to the new bucket nId came from (there is certainly place there)
vvNew[nOrigin].insert(vTried[nPos]);
}
nNew++;
vTried[nPos] = nId;
// we just overwrote an entry in vTried; no need to update nTried
info.fInTried = true;
return;
}
void CAddrMan::Good_(const CService& addr, int64_t nTime)
{
int nId;
CAddrInfo* pinfo = Find(addr, &nId);
// if not found, bail out
if (!pinfo)
return;
CAddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
return;
// update info
info.nLastSuccess = nTime;
info.nLastTry = nTime;
info.nTime = nTime;
info.nAttempts = 0;
// if it is already in the tried set, don't do anything else
if (info.fInTried)
return;
// find a bucket it is in now
int nRnd = GetRandInt(vvNew.size());
int nUBucket = -1;
for (unsigned int n = 0; n < vvNew.size(); n++) {
int nB = (n + nRnd) % vvNew.size();
std::set<int>& vNew = vvNew[nB];
if (vNew.count(nId)) {
nUBucket = nB;
break;
}
}
// if no bucket is found, something bad happened;
// TODO: maybe re-add the node, but for now, just bail out
if (nUBucket == -1)
return;
LogPrint("addrman", "Moving %s to tried\n", addr.ToString());
// move nId to the tried tables
MakeTried(info, nId, nUBucket);
}
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
if (!addr.IsRoutable())
return false;
bool fNew = false;
int nId;
CAddrInfo* pinfo = Find(addr, &nId);
if (pinfo) {
// periodically update nTime
bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
pinfo->nTime = max((int64_t)0, addr.nTime - nTimePenalty);
// add services
pinfo->nServices |= addr.nServices;
// do not update if no new information is present
if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
return false;
// do not update if the entry was already in the "tried" table
if (pinfo->fInTried)
return false;
// do not update if the max reference count is reached
if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
return false;
// stochastic test: previous nRefCount == N: 2^N times harder to increase it
int nFactor = 1;
for (int n = 0; n < pinfo->nRefCount; n++)
nFactor *= 2;
if (nFactor > 1 && (GetRandInt(nFactor) != 0))
return false;
} else {
pinfo = Create(addr, source, &nId);
pinfo->nTime = max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
nNew++;
fNew = true;
}
int nUBucket = pinfo->GetNewBucket(nKey, source);
std::set<int>& vNew = vvNew[nUBucket];
if (!vNew.count(nId)) {
pinfo->nRefCount++;
if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE)
ShrinkNew(nUBucket);
vvNew[nUBucket].insert(nId);
}
return fNew;
}
void CAddrMan::Attempt_(const CService& addr, int64_t nTime)
{
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
CAddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
return;
// update info
info.nLastTry = nTime;
info.nAttempts++;
}
CAddress CAddrMan::Select_(int nUnkBias)
{
if (size() == 0)
return CAddress();
double nCorTried = sqrt(nTried) * (100.0 - nUnkBias);
double nCorNew = sqrt(nNew) * nUnkBias;
if ((nCorTried + nCorNew) * GetRandInt(1 << 30) / (1 << 30) < nCorTried) {
// use a tried node
double fChanceFactor = 1.0;
while (1) {
int nKBucket = GetRandInt(vvTried.size());
std::vector<int>& vTried = vvTried[nKBucket];
if (vTried.size() == 0)
continue;
int nPos = GetRandInt(vTried.size());
assert(mapInfo.count(vTried[nPos]) == 1);
CAddrInfo& info = mapInfo[vTried[nPos]];
if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
} else {
// use a new node
double fChanceFactor = 1.0;
while (1) {
int nUBucket = GetRandInt(vvNew.size());
std::set<int>& vNew = vvNew[nUBucket];
if (vNew.size() == 0)
continue;
int nPos = GetRandInt(vNew.size());
std::set<int>::iterator it = vNew.begin();
while (nPos--)
it++;
assert(mapInfo.count(*it) == 1);
CAddrInfo& info = mapInfo[*it];
if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
}
}
#ifdef DEBUG_ADDRMAN
int CAddrMan::Check_()
{
std::set<int> setTried;
std::map<int, int> mapNew;
if (vRandom.size() != nTried + nNew)
return -7;
for (std::map<int, CAddrInfo>::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
int n = (*it).first;
CAddrInfo& info = (*it).second;
if (info.fInTried) {
if (!info.nLastSuccess)
return -1;
if (info.nRefCount)
return -2;
setTried.insert(n);
} else {
if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
return -3;
if (!info.nRefCount)
return -4;
mapNew[n] = info.nRefCount;
}
if (mapAddr[info] != n)
return -5;
if (info.nRandomPos < 0 || info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
return -14;
if (info.nLastTry < 0)
return -6;
if (info.nLastSuccess < 0)
return -8;
}
if (setTried.size() != nTried)
return -9;
if (mapNew.size() != nNew)
return -10;
for (int n = 0; n < vvTried.size(); n++) {
std::vector<int>& vTried = vvTried[n];
for (std::vector<int>::iterator it = vTried.begin(); it != vTried.end(); it++) {
if (!setTried.count(*it))
return -11;
setTried.erase(*it);
}
}
for (int n = 0; n < vvNew.size(); n++) {
std::set<int>& vNew = vvNew[n];
for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++) {
if (!mapNew.count(*it))
return -12;
if (--mapNew[*it] == 0)
mapNew.erase(*it);
}
}
if (setTried.size())
return -13;
if (mapNew.size())
return -15;
return 0;
}
#endif
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
{
unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100;
if (nNodes > ADDRMAN_GETADDR_MAX)
nNodes = ADDRMAN_GETADDR_MAX;
// gather a list of random nodes, skipping those of low quality
for (unsigned int n = 0; n < vRandom.size(); n++) {
if (vAddr.size() >= nNodes)
break;
int nRndPos = GetRandInt(vRandom.size() - n) + n;
SwapRandom(n, nRndPos);
assert(mapInfo.count(vRandom[n]) == 1);
const CAddrInfo& ai = mapInfo[vRandom[n]];
if (!ai.IsTerrible())
vAddr.push_back(ai);
}
}
void CAddrMan::Connected_(const CService& addr, int64_t nTime)
{
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
CAddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
return;
// update info
int64_t nUpdateInterval = 20 * 60;
if (nTime - info.nTime > nUpdateInterval)
info.nTime = nTime;
}
diff --git a/src/uint256.cpp b/src/arith_uint256.cpp
similarity index 64%
copy from src/uint256.cpp
copy to src/arith_uint256.cpp
index 918c1a7cd..1243823da 100644
--- a/src/uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -1,357 +1,259 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin Core developers
+// Copyright (c) 2009-2014 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "uint256.h"
+#include "arith_uint256.h"
+#include "uint256.h"
#include "utilstrencodings.h"
#include <stdio.h>
#include <string.h>
template <unsigned int BITS>
base_uint<BITS>::base_uint(const std::string& str)
{
SetHex(str);
}
-template <unsigned int BITS>
-base_uint<BITS>::base_uint(const std::vector<unsigned char>& vch)
-{
- if (vch.size() != sizeof(pn))
- throw uint_error("Converting vector of wrong size to base_uint");
- memcpy(pn, &vch[0], sizeof(pn));
-}
-
template <unsigned int BITS>
base_uint<BITS>& base_uint<BITS>::operator<<=(unsigned int shift)
{
base_uint<BITS> a(*this);
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
int k = shift / 32;
shift = shift % 32;
for (int i = 0; i < WIDTH; i++) {
if (i + k + 1 < WIDTH && shift != 0)
pn[i + k + 1] |= (a.pn[i] >> (32 - shift));
if (i + k < WIDTH)
pn[i + k] |= (a.pn[i] << shift);
}
return *this;
}
template <unsigned int BITS>
base_uint<BITS>& base_uint<BITS>::operator>>=(unsigned int shift)
{
base_uint<BITS> a(*this);
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
int k = shift / 32;
shift = shift % 32;
for (int i = 0; i < WIDTH; i++) {
if (i - k - 1 >= 0 && shift != 0)
pn[i - k - 1] |= (a.pn[i] << (32 - shift));
if (i - k >= 0)
pn[i - k] |= (a.pn[i] >> shift);
}
return *this;
}
template <unsigned int BITS>
base_uint<BITS>& base_uint<BITS>::operator*=(uint32_t b32)
{
uint64_t carry = 0;
for (int i = 0; i < WIDTH; i++) {
uint64_t n = carry + (uint64_t)b32 * pn[i];
pn[i] = n & 0xffffffff;
carry = n >> 32;
}
return *this;
}
template <unsigned int BITS>
base_uint<BITS>& base_uint<BITS>::operator*=(const base_uint& b)
{
base_uint<BITS> a = *this;
*this = 0;
for (int j = 0; j < WIDTH; j++) {
uint64_t carry = 0;
for (int i = 0; i + j < WIDTH; i++) {
uint64_t n = carry + pn[i + j] + (uint64_t)a.pn[j] * b.pn[i];
pn[i + j] = n & 0xffffffff;
carry = n >> 32;
}
}
return *this;
}
template <unsigned int BITS>
base_uint<BITS>& base_uint<BITS>::operator/=(const base_uint& b)
{
base_uint<BITS> div = b; // make a copy, so we can shift.
base_uint<BITS> num = *this; // make a copy, so we can subtract.
*this = 0; // the quotient.
int num_bits = num.bits();
int div_bits = div.bits();
if (div_bits == 0)
throw uint_error("Division by zero");
if (div_bits > num_bits) // the result is certainly 0.
return *this;
int shift = num_bits - div_bits;
div <<= shift; // shift so that div and num align.
while (shift >= 0) {
if (num >= div) {
num -= div;
pn[shift / 32] |= (1 << (shift & 31)); // set a bit of the result.
}
div >>= 1; // shift back.
shift--;
}
// num now contains the remainder of the division.
return *this;
}
template <unsigned int BITS>
int base_uint<BITS>::CompareTo(const base_uint<BITS>& b) const
{
for (int i = WIDTH - 1; i >= 0; i--) {
if (pn[i] < b.pn[i])
return -1;
if (pn[i] > b.pn[i])
return 1;
}
return 0;
}
template <unsigned int BITS>
bool base_uint<BITS>::EqualTo(uint64_t b) const
{
for (int i = WIDTH - 1; i >= 2; i--) {
if (pn[i])
return false;
}
if (pn[1] != (b >> 32))
return false;
if (pn[0] != (b & 0xfffffffful))
return false;
return true;
}
template <unsigned int BITS>
double base_uint<BITS>::getdouble() const
{
double ret = 0.0;
double fact = 1.0;
for (int i = 0; i < WIDTH; i++) {
ret += fact * pn[i];
fact *= 4294967296.0;
}
return ret;
}
template <unsigned int BITS>
std::string base_uint<BITS>::GetHex() const
{
- char psz[sizeof(pn) * 2 + 1];
- for (unsigned int i = 0; i < sizeof(pn); i++)
- sprintf(psz + i * 2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]);
- return std::string(psz, psz + sizeof(pn) * 2);
+ return ArithToUint256(*this).GetHex();
}
template <unsigned int BITS>
void base_uint<BITS>::SetHex(const char* psz)
{
- memset(pn, 0, sizeof(pn));
-
- // skip leading spaces
- while (isspace(*psz))
- psz++;
-
- // skip 0x
- if (psz[0] == '0' && tolower(psz[1]) == 'x')
- psz += 2;
-
- // hex string to uint
- const char* pbegin = psz;
- while (::HexDigit(*psz) != -1)
- psz++;
- psz--;
- unsigned char* p1 = (unsigned char*)pn;
- unsigned char* pend = p1 + WIDTH * 4;
- while (psz >= pbegin && p1 < pend) {
- *p1 = ::HexDigit(*psz--);
- if (psz >= pbegin) {
- *p1 |= ((unsigned char)::HexDigit(*psz--) << 4);
- p1++;
- }
- }
+ *this = UintToArith256(uint256S(psz));
}
template <unsigned int BITS>
void base_uint<BITS>::SetHex(const std::string& str)
{
SetHex(str.c_str());
}
template <unsigned int BITS>
std::string base_uint<BITS>::ToString() const
{
return (GetHex());
}
template <unsigned int BITS>
unsigned int base_uint<BITS>::bits() const
{
for (int pos = WIDTH - 1; pos >= 0; pos--) {
if (pn[pos]) {
for (int bits = 31; bits > 0; bits--) {
if (pn[pos] & 1 << bits)
return 32 * pos + bits + 1;
}
return 32 * pos + 1;
}
}
return 0;
}
-// Explicit instantiations for base_uint<160>
-template base_uint<160>::base_uint(const std::string&);
-template base_uint<160>::base_uint(const std::vector<unsigned char>&);
-template base_uint<160>& base_uint<160>::operator<<=(unsigned int);
-template base_uint<160>& base_uint<160>::operator>>=(unsigned int);
-template base_uint<160>& base_uint<160>::operator*=(uint32_t b32);
-template base_uint<160>& base_uint<160>::operator*=(const base_uint<160>& b);
-template base_uint<160>& base_uint<160>::operator/=(const base_uint<160>& b);
-template int base_uint<160>::CompareTo(const base_uint<160>&) const;
-template bool base_uint<160>::EqualTo(uint64_t) const;
-template double base_uint<160>::getdouble() const;
-template std::string base_uint<160>::GetHex() const;
-template std::string base_uint<160>::ToString() const;
-template void base_uint<160>::SetHex(const char*);
-template void base_uint<160>::SetHex(const std::string&);
-template unsigned int base_uint<160>::bits() const;
-
// Explicit instantiations for base_uint<256>
template base_uint<256>::base_uint(const std::string&);
-template base_uint<256>::base_uint(const std::vector<unsigned char>&);
template base_uint<256>& base_uint<256>::operator<<=(unsigned int);
template base_uint<256>& base_uint<256>::operator>>=(unsigned int);
template base_uint<256>& base_uint<256>::operator*=(uint32_t b32);
template base_uint<256>& base_uint<256>::operator*=(const base_uint<256>& b);
template base_uint<256>& base_uint<256>::operator/=(const base_uint<256>& b);
template int base_uint<256>::CompareTo(const base_uint<256>&) const;
template bool base_uint<256>::EqualTo(uint64_t) const;
template double base_uint<256>::getdouble() const;
template std::string base_uint<256>::GetHex() const;
template std::string base_uint<256>::ToString() const;
template void base_uint<256>::SetHex(const char*);
template void base_uint<256>::SetHex(const std::string&);
template unsigned int base_uint<256>::bits() const;
// This implementation directly uses shifts instead of going
// through an intermediate MPI representation.
-uint256& uint256::SetCompact(uint32_t nCompact, bool* pfNegative, bool* pfOverflow)
+arith_uint256& arith_uint256::SetCompact(uint32_t nCompact, bool* pfNegative, bool* pfOverflow)
{
int nSize = nCompact >> 24;
uint32_t nWord = nCompact & 0x007fffff;
if (nSize <= 3) {
nWord >>= 8 * (3 - nSize);
*this = nWord;
} else {
*this = nWord;
*this <<= 8 * (nSize - 3);
}
if (pfNegative)
*pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0;
if (pfOverflow)
*pfOverflow = nWord != 0 && ((nSize > 34) ||
(nWord > 0xff && nSize > 33) ||
(nWord > 0xffff && nSize > 32));
return *this;
}
-uint32_t uint256::GetCompact(bool fNegative) const
+uint32_t arith_uint256::GetCompact(bool fNegative) const
{
int nSize = (bits() + 7) / 8;
uint32_t nCompact = 0;
if (nSize <= 3) {
nCompact = GetLow64() << 8 * (3 - nSize);
} else {
- uint256 bn = *this >> 8 * (nSize - 3);
+ arith_uint256 bn = *this >> 8 * (nSize - 3);
nCompact = bn.GetLow64();
}
// The 0x00800000 bit denotes the sign.
// Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
if (nCompact & 0x00800000) {
nCompact >>= 8;
nSize++;
}
assert((nCompact & ~0x007fffff) == 0);
assert(nSize < 256);
nCompact |= nSize << 24;
nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0);
return nCompact;
}
-static void inline HashMix(uint32_t& a, uint32_t& b, uint32_t& c)
+uint256 ArithToUint256(const arith_uint256 &a)
{
- // Taken from lookup3, by Bob Jenkins.
- a -= c;
- a ^= ((c << 4) | (c >> 28));
- c += b;
- b -= a;
- b ^= ((a << 6) | (a >> 26));
- a += c;
- c -= b;
- c ^= ((b << 8) | (b >> 24));
- b += a;
- a -= c;
- a ^= ((c << 16) | (c >> 16));
- c += b;
- b -= a;
- b ^= ((a << 19) | (a >> 13));
- a += c;
- c -= b;
- c ^= ((b << 4) | (b >> 28));
- b += a;
+ uint256 b;
+ // TODO: needs bswap32 on big-endian
+ memcpy(b.begin(), a.pn, a.size());
+ return b;
}
-
-static void inline HashFinal(uint32_t& a, uint32_t& b, uint32_t& c)
+arith_uint256 UintToArith256(const uint256 &a)
{
- // Taken from lookup3, by Bob Jenkins.
- c ^= b;
- c -= ((b << 14) | (b >> 18));
- a ^= c;
- a -= ((c << 11) | (c >> 21));
- b ^= a;
- b -= ((a << 25) | (a >> 7));
- c ^= b;
- c -= ((b << 16) | (b >> 16));
- a ^= c;
- a -= ((c << 4) | (c >> 28));
- b ^= a;
- b -= ((a << 14) | (a >> 18));
- c ^= b;
- c -= ((b << 24) | (b >> 8));
-}
-
-uint64_t uint256::GetHash(const uint256& salt) const
-{
- uint32_t a, b, c;
- a = b = c = 0xdeadbeef + (WIDTH << 2);
-
- a += pn[0] ^ salt.pn[0];
- b += pn[1] ^ salt.pn[1];
- c += pn[2] ^ salt.pn[2];
- HashMix(a, b, c);
- a += pn[3] ^ salt.pn[3];
- b += pn[4] ^ salt.pn[4];
- c += pn[5] ^ salt.pn[5];
- HashMix(a, b, c);
- a += pn[6] ^ salt.pn[6];
- b += pn[7] ^ salt.pn[7];
- HashFinal(a, b, c);
-
- return ((((uint64_t)b) << 32) | c);
+ arith_uint256 b;
+ // TODO: needs bswap32 on big-endian
+ memcpy(b.pn, a.begin(), a.size());
+ return b;
}
diff --git a/src/uint256.h b/src/arith_uint256.h
similarity index 81%
copy from src/uint256.h
copy to src/arith_uint256.h
index 139270ac4..b6ba3a108 100644
--- a/src/uint256.h
+++ b/src/arith_uint256.h
@@ -1,333 +1,290 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin Core developers
+// Copyright (c) 2009-2014 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_UINT256_H
-#define BITCOIN_UINT256_H
+#ifndef BITCOIN_ARITH_UINT256_H
+#define BITCOIN_ARITH_UINT256_H
#include <assert.h>
#include <cstring>
#include <stdexcept>
#include <stdint.h>
#include <string>
#include <vector>
+class uint256;
+
class uint_error : public std::runtime_error {
public:
explicit uint_error(const std::string& str) : std::runtime_error(str) {}
};
/** Template base class for unsigned big integers. */
template<unsigned int BITS>
class base_uint
{
protected:
enum { WIDTH=BITS/32 };
uint32_t pn[WIDTH];
public:
base_uint()
{
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
}
base_uint(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
}
base_uint& operator=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
return *this;
}
base_uint(uint64_t b)
{
pn[0] = (unsigned int)b;
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
}
explicit base_uint(const std::string& str);
- explicit base_uint(const std::vector<unsigned char>& vch);
bool operator!() const
{
for (int i = 0; i < WIDTH; i++)
if (pn[i] != 0)
return false;
return true;
}
const base_uint operator~() const
{
base_uint ret;
for (int i = 0; i < WIDTH; i++)
ret.pn[i] = ~pn[i];
return ret;
}
const base_uint operator-() const
{
base_uint ret;
for (int i = 0; i < WIDTH; i++)
ret.pn[i] = ~pn[i];
ret++;
return ret;
}
double getdouble() const;
base_uint& operator=(uint64_t b)
{
pn[0] = (unsigned int)b;
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
return *this;
}
base_uint& operator^=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] ^= b.pn[i];
return *this;
}
base_uint& operator&=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] &= b.pn[i];
return *this;
}
base_uint& operator|=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] |= b.pn[i];
return *this;
}
base_uint& operator^=(uint64_t b)
{
pn[0] ^= (unsigned int)b;
pn[1] ^= (unsigned int)(b >> 32);
return *this;
}
base_uint& operator|=(uint64_t b)
{
pn[0] |= (unsigned int)b;
pn[1] |= (unsigned int)(b >> 32);
return *this;
}
base_uint& operator<<=(unsigned int shift);
base_uint& operator>>=(unsigned int shift);
base_uint& operator+=(const base_uint& b)
{
uint64_t carry = 0;
for (int i = 0; i < WIDTH; i++)
{
uint64_t n = carry + pn[i] + b.pn[i];
pn[i] = n & 0xffffffff;
carry = n >> 32;
}
return *this;
}
base_uint& operator-=(const base_uint& b)
{
*this += -b;
return *this;
}
base_uint& operator+=(uint64_t b64)
{
base_uint b;
b = b64;
*this += b;
return *this;
}
base_uint& operator-=(uint64_t b64)
{
base_uint b;
b = b64;
*this += -b;
return *this;
}
base_uint& operator*=(uint32_t b32);
base_uint& operator*=(const base_uint& b);
base_uint& operator/=(const base_uint& b);
base_uint& operator++()
{
// prefix operator
int i = 0;
while (++pn[i] == 0 && i < WIDTH-1)
i++;
return *this;
}
const base_uint operator++(int)
{
// postfix operator
const base_uint ret = *this;
++(*this);
return ret;
}
base_uint& operator--()
{
// prefix operator
int i = 0;
while (--pn[i] == (uint32_t)-1 && i < WIDTH-1)
i++;
return *this;
}
const base_uint operator--(int)
{
// postfix operator
const base_uint ret = *this;
--(*this);
return ret;
}
int CompareTo(const base_uint& b) const;
bool EqualTo(uint64_t b) const;
friend inline const base_uint operator+(const base_uint& a, const base_uint& b) { return base_uint(a) += b; }
friend inline const base_uint operator-(const base_uint& a, const base_uint& b) { return base_uint(a) -= b; }
friend inline const base_uint operator*(const base_uint& a, const base_uint& b) { return base_uint(a) *= b; }
friend inline const base_uint operator/(const base_uint& a, const base_uint& b) { return base_uint(a) /= b; }
friend inline const base_uint operator|(const base_uint& a, const base_uint& b) { return base_uint(a) |= b; }
friend inline const base_uint operator&(const base_uint& a, const base_uint& b) { return base_uint(a) &= b; }
friend inline const base_uint operator^(const base_uint& a, const base_uint& b) { return base_uint(a) ^= b; }
friend inline const base_uint operator>>(const base_uint& a, int shift) { return base_uint(a) >>= shift; }
friend inline const base_uint operator<<(const base_uint& a, int shift) { return base_uint(a) <<= shift; }
friend inline const base_uint operator*(const base_uint& a, uint32_t b) { return base_uint(a) *= b; }
friend inline bool operator==(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) == 0; }
friend inline bool operator!=(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) != 0; }
friend inline bool operator>(const base_uint& a, const base_uint& b) { return a.CompareTo(b) > 0; }
friend inline bool operator<(const base_uint& a, const base_uint& b) { return a.CompareTo(b) < 0; }
friend inline bool operator>=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) >= 0; }
friend inline bool operator<=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) <= 0; }
friend inline bool operator==(const base_uint& a, uint64_t b) { return a.EqualTo(b); }
friend inline bool operator!=(const base_uint& a, uint64_t b) { return !a.EqualTo(b); }
std::string GetHex() const;
void SetHex(const char* psz);
void SetHex(const std::string& str);
std::string ToString() const;
- unsigned char* begin()
- {
- return (unsigned char*)&pn[0];
- }
-
- unsigned char* end()
- {
- return (unsigned char*)&pn[WIDTH];
- }
-
- const unsigned char* begin() const
- {
- return (unsigned char*)&pn[0];
- }
-
- const unsigned char* end() const
- {
- return (unsigned char*)&pn[WIDTH];
- }
-
unsigned int size() const
{
return sizeof(pn);
}
/**
* Returns the position of the highest bit set plus one, or zero if the
* value is zero.
*/
unsigned int bits() const;
uint64_t GetLow64() const
{
assert(WIDTH >= 2);
return pn[0] | (uint64_t)pn[1] << 32;
}
-
- unsigned int GetSerializeSize(int nType, int nVersion) const
- {
- return sizeof(pn);
- }
-
- template<typename Stream>
- void Serialize(Stream& s, int nType, int nVersion) const
- {
- s.write((char*)pn, sizeof(pn));
- }
-
- template<typename Stream>
- void Unserialize(Stream& s, int nType, int nVersion)
- {
- s.read((char*)pn, sizeof(pn));
- }
-};
-
-/** 160-bit unsigned big integer. */
-class uint160 : public base_uint<160> {
-public:
- uint160() {}
- uint160(const base_uint<160>& b) : base_uint<160>(b) {}
- uint160(uint64_t b) : base_uint<160>(b) {}
- explicit uint160(const std::string& str) : base_uint<160>(str) {}
- explicit uint160(const std::vector<unsigned char>& vch) : base_uint<160>(vch) {}
};
/** 256-bit unsigned big integer. */
-class uint256 : public base_uint<256> {
+class arith_uint256 : public base_uint<256> {
public:
- uint256() {}
- uint256(const base_uint<256>& b) : base_uint<256>(b) {}
- uint256(uint64_t b) : base_uint<256>(b) {}
- explicit uint256(const std::string& str) : base_uint<256>(str) {}
- explicit uint256(const std::vector<unsigned char>& vch) : base_uint<256>(vch) {}
-
+ arith_uint256() {}
+ arith_uint256(const base_uint<256>& b) : base_uint<256>(b) {}
+ arith_uint256(uint64_t b) : base_uint<256>(b) {}
+ explicit arith_uint256(const std::string& str) : base_uint<256>(str) {}
+
/**
* The "compact" format is a representation of a whole
* number N using an unsigned 32bit number similar to a
* floating point format.
* The most significant 8 bits are the unsigned exponent of base 256.
* This exponent can be thought of as "number of bytes of N".
* The lower 23 bits are the mantissa.
* Bit number 24 (0x800000) represents the sign of N.
* N = (-1^sign) * mantissa * 256^(exponent-3)
- *
+ *
* Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().
* MPI uses the most significant bit of the first byte as sign.
* Thus 0x1234560000 is compact (0x05123456)
* and 0xc0de000000 is compact (0x0600c0de)
- *
+ *
* Bitcoin only uses this "compact" format for encoding difficulty
* targets, which are unsigned 256bit quantities. Thus, all the
* complexities of the sign bit and using base 256 are probably an
* implementation accident.
*/
- uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL);
+ arith_uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL);
uint32_t GetCompact(bool fNegative = false) const;
- uint64_t GetHash(const uint256& salt) const;
+ friend uint256 ArithToUint256(const arith_uint256 &);
+ friend arith_uint256 UintToArith256(const uint256 &);
};
+uint256 ArithToUint256(const arith_uint256 &);
+arith_uint256 UintToArith256(const uint256 &);
+
#endif // BITCOIN_UINT256_H
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 05a85810e..e9dac9ffb 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -1,633 +1,633 @@
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
#include "clientversion.h"
#include "primitives/block.h" // for MAX_BLOCK_SIZE
#include "primitives/transaction.h"
#include "core_io.h"
#include "coins.h"
#include "keystore.h"
#include "script/script.h"
#include "script/sign.h"
#include "ui_interface.h" // for _(...)
#include "univalue/univalue.h"
#include "util.h"
#include "utilstrencodings.h"
#include "utilmoneystr.h"
#include <stdio.h>
#include <boost/algorithm/string.hpp>
#include <boost/assign/list_of.hpp>
using namespace boost::assign;
using namespace std;
static bool fCreateBlank;
static map<string,UniValue> registers;
CClientUIInterface uiInterface;
static bool AppInitRawTx(int argc, char* argv[])
{
//
// Parameters
//
ParseParameters(argc, argv);
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
if (!SelectParamsFromCommandLine()) {
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
return false;
}
fCreateBlank = GetBoolArg("-create", false);
if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help"))
{
// First part of help message is specific to this utility
std::string strUsage = _("Bitcoin Core bitcoin-tx utility version") + " " + FormatFullVersion() + "\n\n" +
_("Usage:") + "\n" +
" bitcoin-tx [options] <hex-tx> [commands] " + _("Update hex-encoded bitcoin transaction") + "\n" +
" bitcoin-tx [options] -create [commands] " + _("Create hex-encoded bitcoin transaction") + "\n" +
"\n";
fprintf(stdout, "%s", strUsage.c_str());
strUsage = _("Options:") + "\n";
strUsage += " -? " + _("This help message") + "\n";
strUsage += " -create " + _("Create new, empty TX.") + "\n";
strUsage += " -json " + _("Select JSON output") + "\n";
strUsage += " -txid " + _("Output only the hex-encoded transaction id of the resultant transaction.") + "\n";
strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be solved instantly.") + "\n";
strUsage += " -testnet " + _("Use the test network") + "\n";
strUsage += "\n";
fprintf(stdout, "%s", strUsage.c_str());
strUsage = _("Commands:") + "\n";
strUsage += " delin=N " + _("Delete input N from TX") + "\n";
strUsage += " delout=N " + _("Delete output N from TX") + "\n";
strUsage += " in=TXID:VOUT " + _("Add input to TX") + "\n";
strUsage += " locktime=N " + _("Set TX lock time to N") + "\n";
strUsage += " nversion=N " + _("Set TX version to N") + "\n";
strUsage += " outaddr=VALUE:ADDRESS " + _("Add address-based output to TX") + "\n";
strUsage += " outscript=VALUE:SCRIPT " + _("Add raw script output to TX") + "\n";
strUsage += " sign=SIGHASH-FLAGS " + _("Add zero or more signatures to transaction") + "\n";
strUsage += " This command requires JSON registers:\n";
strUsage += " prevtxs=JSON object\n";
strUsage += " privatekeys=JSON object\n";
strUsage += " See signrawtransaction docs for format of sighash flags, JSON objects.\n";
strUsage += "\n";
fprintf(stdout, "%s", strUsage.c_str());
strUsage = _("Register Commands:") + "\n";
strUsage += " load=NAME:FILENAME " + _("Load JSON file FILENAME into register NAME") + "\n";
strUsage += " set=NAME:JSON-STRING " + _("Set register NAME to given JSON-STRING") + "\n";
strUsage += "\n";
fprintf(stdout, "%s", strUsage.c_str());
return false;
}
return true;
}
static void RegisterSetJson(const string& key, const string& rawJson)
{
UniValue val;
if (!val.read(rawJson)) {
string strErr = "Cannot parse JSON for key " + key;
throw runtime_error(strErr);
}
registers[key] = val;
}
static void RegisterSet(const string& strInput)
{
// separate NAME:VALUE in string
size_t pos = strInput.find(':');
if ((pos == string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw runtime_error("Register input requires NAME:VALUE");
string key = strInput.substr(0, pos);
string valStr = strInput.substr(pos + 1, string::npos);
RegisterSetJson(key, valStr);
}
static void RegisterLoad(const string& strInput)
{
// separate NAME:FILENAME in string
size_t pos = strInput.find(':');
if ((pos == string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw runtime_error("Register load requires NAME:FILENAME");
string key = strInput.substr(0, pos);
string filename = strInput.substr(pos + 1, string::npos);
FILE *f = fopen(filename.c_str(), "r");
if (!f) {
string strErr = "Cannot open file " + filename;
throw runtime_error(strErr);
}
// load file chunks into one big buffer
string valStr;
while ((!feof(f)) && (!ferror(f))) {
char buf[4096];
int bread = fread(buf, 1, sizeof(buf), f);
if (bread <= 0)
break;
valStr.insert(valStr.size(), buf, bread);
}
if (ferror(f)) {
string strErr = "Error reading file " + filename;
throw runtime_error(strErr);
}
fclose(f);
// evaluate as JSON buffer register
RegisterSetJson(key, valStr);
}
static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
{
int64_t newVersion = atoi64(cmdVal);
if (newVersion < 1 || newVersion > CTransaction::CURRENT_VERSION)
throw runtime_error("Invalid TX version requested");
tx.nVersion = (int) newVersion;
}
static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
{
int64_t newLocktime = atoi64(cmdVal);
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
throw runtime_error("Invalid TX locktime requested");
tx.nLockTime = (unsigned int) newLocktime;
}
static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
{
// separate TXID:VOUT in string
size_t pos = strInput.find(':');
if ((pos == string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw runtime_error("TX input missing separator");
// extract and validate TXID
string strTxid = strInput.substr(0, pos);
if ((strTxid.size() != 64) || !IsHex(strTxid))
throw runtime_error("invalid TX input txid");
- uint256 txid(strTxid);
+ uint256 txid(uint256S(strTxid));
static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz;
// extract and validate vout
string strVout = strInput.substr(pos + 1, string::npos);
int vout = atoi(strVout);
if ((vout < 0) || (vout > (int)maxVout))
throw runtime_error("invalid TX input vout");
// append to transaction input list
CTxIn txin(txid, vout);
tx.vin.push_back(txin);
}
static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
{
// separate VALUE:ADDRESS in string
size_t pos = strInput.find(':');
if ((pos == string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw runtime_error("TX output missing separator");
// extract and validate VALUE
string strValue = strInput.substr(0, pos);
CAmount value;
if (!ParseMoney(strValue, value))
throw runtime_error("invalid TX output value");
// extract and validate ADDRESS
string strAddr = strInput.substr(pos + 1, string::npos);
CBitcoinAddress addr(strAddr);
if (!addr.IsValid())
throw runtime_error("invalid TX output address");
// build standard output script via GetScriptForDestination()
CScript scriptPubKey = GetScriptForDestination(addr.Get());
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
{
// separate VALUE:SCRIPT in string
size_t pos = strInput.find(':');
if ((pos == string::npos) ||
(pos == 0))
throw runtime_error("TX output missing separator");
// extract and validate VALUE
string strValue = strInput.substr(0, pos);
CAmount value;
if (!ParseMoney(strValue, value))
throw runtime_error("invalid TX output value");
// extract and validate script
string strScript = strInput.substr(pos + 1, string::npos);
CScript scriptPubKey = ParseScript(strScript); // throws on err
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
{
// parse requested deletion index
int inIdx = atoi(strInIdx);
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
string strErr = "Invalid TX input index '" + strInIdx + "'";
throw runtime_error(strErr.c_str());
}
// delete input from transaction
tx.vin.erase(tx.vin.begin() + inIdx);
}
static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx)
{
// parse requested deletion index
int outIdx = atoi(strOutIdx);
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
string strErr = "Invalid TX output index '" + strOutIdx + "'";
throw runtime_error(strErr.c_str());
}
// delete output from transaction
tx.vout.erase(tx.vout.begin() + outIdx);
}
static const unsigned int N_SIGHASH_OPTS = 6;
static const struct {
const char *flagStr;
int flags;
} sighashOptions[N_SIGHASH_OPTS] = {
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
{"ALL|ANYONECANPAY", SIGHASH_ALL|SIGHASH_ANYONECANPAY},
{"NONE|ANYONECANPAY", SIGHASH_NONE|SIGHASH_ANYONECANPAY},
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
};
static bool findSighashFlags(int& flags, const string& flagStr)
{
flags = 0;
for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
if (flagStr == sighashOptions[i].flagStr) {
flags = sighashOptions[i].flags;
return true;
}
}
return false;
}
uint256 ParseHashUO(map<string,UniValue>& o, string strKey)
{
if (!o.count(strKey))
- return 0;
+ return uint256();
return ParseHashUV(o[strKey], strKey);
}
vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
{
if (!o.count(strKey)) {
vector<unsigned char> emptyVec;
return emptyVec;
}
return ParseHexUV(o[strKey], strKey);
}
static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
{
int nHashType = SIGHASH_ALL;
if (flagStr.size() > 0)
if (!findSighashFlags(nHashType, flagStr))
throw runtime_error("unknown sighash flag/sign option");
vector<CTransaction> txVariants;
txVariants.push_back(tx);
// mergedTx will end up with all the signatures; it
// starts as a clone of the raw tx:
CMutableTransaction mergedTx(txVariants[0]);
bool fComplete = true;
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
if (!registers.count("privatekeys"))
throw runtime_error("privatekeys register variable must be set.");
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
UniValue keysObj = registers["privatekeys"];
fGivenKeys = true;
for (unsigned int kidx = 0; kidx < keysObj.count(); kidx++) {
if (!keysObj[kidx].isStr())
throw runtime_error("privatekey not a string");
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
if (!fGood)
throw runtime_error("privatekey not valid");
CKey key = vchSecret.GetKey();
tempKeystore.AddKey(key);
}
// Add previous txouts given in the RPC call:
if (!registers.count("prevtxs"))
throw runtime_error("prevtxs register variable must be set.");
UniValue prevtxsObj = registers["prevtxs"];
{
for (unsigned int previdx = 0; previdx < prevtxsObj.count(); previdx++) {
UniValue prevOut = prevtxsObj[previdx];
if (!prevOut.isObject())
throw runtime_error("expected prevtxs internal object");
map<string,UniValue::VType> types = map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
if (!prevOut.checkObject(types))
throw runtime_error("prevtxs internal object typecheck fail");
uint256 txid = ParseHashUV(prevOut["txid"], "txid");
int nOut = atoi(prevOut["vout"].getValStr());
if (nOut < 0)
throw runtime_error("vout must be positive");
vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
CCoinsModifier coins = view.ModifyCoins(txid);
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
string err("Previous output scriptPubKey mismatch:\n");
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
scriptPubKey.ToString();
throw runtime_error(err);
}
if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1);
coins->vout[nOut].scriptPubKey = scriptPubKey;
coins->vout[nOut].nValue = 0; // we don't know the actual output value
}
// if redeemScript given and private keys given,
// add redeemScript to the tempKeystore so it can be signed:
if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
prevOut.exists("redeemScript")) {
UniValue v = prevOut["redeemScript"];
vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
}
const CKeyStore& keystore = tempKeystore;
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (!coins || !coins->IsAvailable(txin.prevout.n)) {
fComplete = false;
continue;
}
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
txin.scriptSig.clear();
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
// ... and merge in other signatures:
BOOST_FOREACH(const CTransaction& txv, txVariants) {
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
}
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(mergedTx, i)))
fComplete = false;
}
if (fComplete) {
// do nothing... for now
// perhaps store this for later optional JSON output
}
tx = mergedTx;
}
static void MutateTx(CMutableTransaction& tx, const string& command,
const string& commandVal)
{
if (command == "nversion")
MutateTxVersion(tx, commandVal);
else if (command == "locktime")
MutateTxLocktime(tx, commandVal);
else if (command == "delin")
MutateTxDelInput(tx, commandVal);
else if (command == "in")
MutateTxAddInput(tx, commandVal);
else if (command == "delout")
MutateTxDelOutput(tx, commandVal);
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
else if (command == "sign")
MutateTxSign(tx, commandVal);
else if (command == "load")
RegisterLoad(commandVal);
else if (command == "set")
RegisterSet(commandVal);
else
throw runtime_error("unknown command");
}
static void OutputTxJSON(const CTransaction& tx)
{
UniValue entry(UniValue::VOBJ);
- TxToUniv(tx, 0, entry);
+ TxToUniv(tx, uint256(), entry);
string jsonOutput = entry.write(4);
fprintf(stdout, "%s\n", jsonOutput.c_str());
}
static void OutputTxHash(const CTransaction& tx)
{
string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
fprintf(stdout, "%s\n", strHexHash.c_str());
}
static void OutputTxHex(const CTransaction& tx)
{
string strHex = EncodeHexTx(tx);
fprintf(stdout, "%s\n", strHex.c_str());
}
static void OutputTx(const CTransaction& tx)
{
if (GetBoolArg("-json", false))
OutputTxJSON(tx);
else if (GetBoolArg("-txid", false))
OutputTxHash(tx);
else
OutputTxHex(tx);
}
static string readStdin()
{
char buf[4096];
string ret;
while (!feof(stdin)) {
size_t bread = fread(buf, 1, sizeof(buf), stdin);
ret.append(buf, bread);
if (bread < sizeof(buf))
break;
}
if (ferror(stdin))
throw runtime_error("error reading stdin");
boost::algorithm::trim_right(ret);
return ret;
}
static int CommandLineRawTx(int argc, char* argv[])
{
string strPrint;
int nRet = 0;
try {
// Skip switches; Permit common stdin convention "-"
while (argc > 1 && IsSwitchChar(argv[1][0]) &&
(argv[1][1] != 0)) {
argc--;
argv++;
}
CTransaction txDecodeTmp;
int startArg;
if (!fCreateBlank) {
// require at least one param
if (argc < 2)
throw runtime_error("too few parameters");
// param: hex-encoded bitcoin transaction
string strHexTx(argv[1]);
if (strHexTx == "-") // "-" implies standard input
strHexTx = readStdin();
if (!DecodeHexTx(txDecodeTmp, strHexTx))
throw runtime_error("invalid transaction encoding");
startArg = 2;
} else
startArg = 1;
CMutableTransaction tx(txDecodeTmp);
for (int i = startArg; i < argc; i++) {
string arg = argv[i];
string key, value;
size_t eqpos = arg.find('=');
if (eqpos == string::npos)
key = arg;
else {
key = arg.substr(0, eqpos);
value = arg.substr(eqpos + 1);
}
MutateTx(tx, key, value);
}
OutputTx(tx);
}
catch (const boost::thread_interrupted&) {
throw;
}
catch (const std::exception& e) {
strPrint = string("error: ") + e.what();
nRet = EXIT_FAILURE;
}
catch (...) {
PrintExceptionContinue(NULL, "CommandLineRawTx()");
throw;
}
if (strPrint != "") {
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
}
return nRet;
}
int main(int argc, char* argv[])
{
SetupEnvironment();
try {
if(!AppInitRawTx(argc, argv))
return EXIT_FAILURE;
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInitRawTx()");
return EXIT_FAILURE;
} catch (...) {
PrintExceptionContinue(NULL, "AppInitRawTx()");
return EXIT_FAILURE;
}
int ret = EXIT_FAILURE;
try {
ret = CommandLineRawTx(argc, argv);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "CommandLineRawTx()");
} catch (...) {
PrintExceptionContinue(NULL, "CommandLineRawTx()");
}
return ret;
}
diff --git a/src/chain.h b/src/chain.h
index b459c975f..004e87ab5 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -1,399 +1,400 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CHAIN_H
#define BITCOIN_CHAIN_H
+#include "arith_uint256.h"
#include "primitives/block.h"
#include "pow.h"
#include "tinyformat.h"
#include "uint256.h"
#include <vector>
#include <boost/foreach.hpp>
struct CDiskBlockPos
{
int nFile;
unsigned int nPos;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(VARINT(nFile));
READWRITE(VARINT(nPos));
}
CDiskBlockPos() {
SetNull();
}
CDiskBlockPos(int nFileIn, unsigned int nPosIn) {
nFile = nFileIn;
nPos = nPosIn;
}
friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return (a.nFile == b.nFile && a.nPos == b.nPos);
}
friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return !(a == b);
}
void SetNull() { nFile = -1; nPos = 0; }
bool IsNull() const { return (nFile == -1); }
};
enum BlockStatus {
//! Unused.
BLOCK_VALID_UNKNOWN = 0,
//! Parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future
BLOCK_VALID_HEADER = 1,
//! All parent headers found, difficulty matches, timestamp >= median previous, checkpoint. Implies all parents
//! are also at least TREE.
BLOCK_VALID_TREE = 2,
/**
* Only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids,
* sigops, size, merkle root. Implies all parents are at least TREE but not necessarily TRANSACTIONS. When all
* parent blocks also have TRANSACTIONS, CBlockIndex::nChainTx will be set.
*/
BLOCK_VALID_TRANSACTIONS = 3,
//! Outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30.
//! Implies all parents are also at least CHAIN.
BLOCK_VALID_CHAIN = 4,
//! Scripts & signatures ok. Implies all parents are also at least SCRIPTS.
BLOCK_VALID_SCRIPTS = 5,
//! All validity bits.
BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS,
BLOCK_HAVE_DATA = 8, //! full block available in blk*.dat
BLOCK_HAVE_UNDO = 16, //! undo data available in rev*.dat
BLOCK_HAVE_MASK = BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO,
BLOCK_FAILED_VALID = 32, //! stage after last reached validness failed
BLOCK_FAILED_CHILD = 64, //! descends from failed block
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
};
/** The block chain is a tree shaped structure starting with the
* genesis block at the root, with each block potentially having multiple
* candidates to be the next block. A blockindex may have multiple pprev pointing
* to it, but at most one of them can be part of the currently active branch.
*/
class CBlockIndex
{
public:
//! pointer to the hash of the block, if any. Memory is owned by this CBlockIndex
const uint256* phashBlock;
//! pointer to the index of the predecessor of this block
CBlockIndex* pprev;
//! pointer to the index of some further predecessor of this block
CBlockIndex* pskip;
//! height of the entry in the chain. The genesis block has height 0
int nHeight;
//! Which # file this block is stored in (blk?????.dat)
int nFile;
//! Byte offset within blk?????.dat where this block's data is stored
unsigned int nDataPos;
//! Byte offset within rev?????.dat where this block's undo data is stored
unsigned int nUndoPos;
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
- uint256 nChainWork;
+ arith_uint256 nChainWork;
//! Number of transactions in this block.
//! Note: in a potential headers-first mode, this number cannot be relied upon
unsigned int nTx;
//! (memory only) Number of transactions in the chain up to and including this block.
//! This value will be non-zero only if and only if transactions for this block and all its parents are available.
//! Change to 64-bit type when necessary; won't happen before 2030
unsigned int nChainTx;
//! Verification status of this block. See enum BlockStatus
unsigned int nStatus;
//! block header
int nVersion;
uint256 hashMerkleRoot;
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
uint32_t nSequenceId;
void SetNull()
{
phashBlock = NULL;
pprev = NULL;
pskip = NULL;
nHeight = 0;
nFile = 0;
nDataPos = 0;
nUndoPos = 0;
- nChainWork = 0;
+ nChainWork = arith_uint256();
nTx = 0;
nChainTx = 0;
nStatus = 0;
nSequenceId = 0;
nVersion = 0;
- hashMerkleRoot = 0;
+ hashMerkleRoot = uint256();
nTime = 0;
nBits = 0;
nNonce = 0;
}
CBlockIndex()
{
SetNull();
}
CBlockIndex(const CBlockHeader& block)
{
SetNull();
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
}
CDiskBlockPos GetBlockPos() const {
CDiskBlockPos ret;
if (nStatus & BLOCK_HAVE_DATA) {
ret.nFile = nFile;
ret.nPos = nDataPos;
}
return ret;
}
CDiskBlockPos GetUndoPos() const {
CDiskBlockPos ret;
if (nStatus & BLOCK_HAVE_UNDO) {
ret.nFile = nFile;
ret.nPos = nUndoPos;
}
return ret;
}
CBlockHeader GetBlockHeader() const
{
CBlockHeader block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
uint256 GetBlockHash() const
{
return *phashBlock;
}
int64_t GetBlockTime() const
{
return (int64_t)nTime;
}
enum { nMedianTimeSpan=11 };
int64_t GetMedianTimePast() const
{
int64_t pmedian[nMedianTimeSpan];
int64_t* pbegin = &pmedian[nMedianTimeSpan];
int64_t* pend = &pmedian[nMedianTimeSpan];
const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
*(--pbegin) = pindex->GetBlockTime();
std::sort(pbegin, pend);
return pbegin[(pend - pbegin)/2];
}
std::string ToString() const
{
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, nHeight,
hashMerkleRoot.ToString(),
GetBlockHash().ToString());
}
//! Check whether this block index entry is valid up to the passed validity level.
bool IsValid(enum BlockStatus nUpTo = BLOCK_VALID_TRANSACTIONS) const
{
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
return false;
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
}
//! Raise the validity level of this block index entry.
//! Returns true if the validity was changed.
bool RaiseValidity(enum BlockStatus nUpTo)
{
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
return false;
if ((nStatus & BLOCK_VALID_MASK) < nUpTo) {
nStatus = (nStatus & ~BLOCK_VALID_MASK) | nUpTo;
return true;
}
return false;
}
//! Build the skiplist pointer for this entry.
void BuildSkip();
//! Efficiently find an ancestor of this block.
CBlockIndex* GetAncestor(int height);
const CBlockIndex* GetAncestor(int height) const;
};
/** Used to marshal pointers into hashes for db storage. */
class CDiskBlockIndex : public CBlockIndex
{
public:
uint256 hashPrev;
CDiskBlockIndex() {
- hashPrev = 0;
+ hashPrev = uint256();
}
explicit CDiskBlockIndex(const CBlockIndex* pindex) : CBlockIndex(*pindex) {
- hashPrev = (pprev ? pprev->GetBlockHash() : 0);
+ hashPrev = (pprev ? pprev->GetBlockHash() : uint256());
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (!(nType & SER_GETHASH))
READWRITE(VARINT(nVersion));
READWRITE(VARINT(nHeight));
READWRITE(VARINT(nStatus));
READWRITE(VARINT(nTx));
if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO))
READWRITE(VARINT(nFile));
if (nStatus & BLOCK_HAVE_DATA)
READWRITE(VARINT(nDataPos));
if (nStatus & BLOCK_HAVE_UNDO)
READWRITE(VARINT(nUndoPos));
// block header
READWRITE(this->nVersion);
READWRITE(hashPrev);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
}
uint256 GetBlockHash() const
{
CBlockHeader block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block.GetHash();
}
std::string ToString() const
{
std::string str = "CDiskBlockIndex(";
str += CBlockIndex::ToString();
str += strprintf("\n hashBlock=%s, hashPrev=%s)",
GetBlockHash().ToString(),
hashPrev.ToString());
return str;
}
};
/** An in-memory indexed chain of blocks. */
class CChain {
private:
std::vector<CBlockIndex*> vChain;
public:
/** Returns the index entry for the genesis block of this chain, or NULL if none. */
CBlockIndex *Genesis() const {
return vChain.size() > 0 ? vChain[0] : NULL;
}
/** Returns the index entry for the tip of this chain, or NULL if none. */
CBlockIndex *Tip() const {
return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL;
}
/** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */
CBlockIndex *operator[](int nHeight) const {
if (nHeight < 0 || nHeight >= (int)vChain.size())
return NULL;
return vChain[nHeight];
}
/** Compare two chains efficiently. */
friend bool operator==(const CChain &a, const CChain &b) {
return a.vChain.size() == b.vChain.size() &&
a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1];
}
/** Efficiently check whether a block is present in this chain. */
bool Contains(const CBlockIndex *pindex) const {
return (*this)[pindex->nHeight] == pindex;
}
/** Find the successor of a block in this chain, or NULL if the given index is not found or is the tip. */
CBlockIndex *Next(const CBlockIndex *pindex) const {
if (Contains(pindex))
return (*this)[pindex->nHeight + 1];
else
return NULL;
}
/** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */
int Height() const {
return vChain.size() - 1;
}
/** Set/initialize a chain with a given tip. */
void SetTip(CBlockIndex *pindex);
/** Return a CBlockLocator that refers to a block in this chain (by default the tip). */
CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const;
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
};
#endif // BITCOIN_CHAIN_H
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index f4bc6e1d7..3abc569dc 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -1,368 +1,368 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
#include "random.h"
#include "util.h"
#include "utilstrencodings.h"
#include <assert.h>
#include <boost/assign/list_of.hpp>
using namespace std;
using namespace boost::assign;
struct SeedSpec6 {
uint8_t addr[16];
uint16_t port;
};
#include "chainparamsseeds.h"
/**
* Main network
*/
//! Convert the pnSeeds6 array into usable address objects.
static void convertSeed6(std::vector<CAddress> &vSeedsOut, const SeedSpec6 *data, unsigned int count)
{
// It'll only connect to one or two seed nodes because once it connects,
// it'll get a pile of addresses with newer timestamps.
// Seed nodes are given a random 'last seen time' of between one and two
// weeks ago.
const int64_t nOneWeek = 7*24*60*60;
for (unsigned int i = 0; i < count; i++)
{
struct in6_addr ip;
memcpy(&ip, data[i].addr, sizeof(ip));
CAddress addr(CService(ip, data[i].port));
addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
vSeedsOut.push_back(addr);
}
}
/**
* What makes a good checkpoint block?
* + Is surrounded by blocks with reasonable timestamps
* (no blocks before with a timestamp after, none after with
* timestamp before)
* + Contains no strange transactions
*/
static Checkpoints::MapCheckpoints mapCheckpoints =
boost::assign::map_list_of
- ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
- ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"))
- ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))
- (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"))
- (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"))
- (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"))
- (193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"))
- (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"))
- (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"))
- (225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"))
- (250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"))
- (279000, uint256("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"))
- (295000, uint256("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983"))
+ ( 11111, uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
+ ( 33333, uint256S("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"))
+ ( 74000, uint256S("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))
+ (105000, uint256S("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"))
+ (134444, uint256S("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"))
+ (168000, uint256S("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"))
+ (193000, uint256S("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"))
+ (210000, uint256S("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"))
+ (216116, uint256S("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"))
+ (225430, uint256S("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"))
+ (250000, uint256S("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"))
+ (279000, uint256S("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"))
+ (295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983"))
;
static const Checkpoints::CCheckpointData data = {
&mapCheckpoints,
1397080064, // * UNIX timestamp of last checkpoint block
36544669, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the SetBestChain debug.log lines)
60000.0 // * estimated number of transactions per day after checkpoint
};
static Checkpoints::MapCheckpoints mapCheckpointsTestnet =
boost::assign::map_list_of
- ( 546, uint256("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70"))
+ ( 546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70"))
;
static const Checkpoints::CCheckpointData dataTestnet = {
&mapCheckpointsTestnet,
1337966069,
1488,
300
};
static Checkpoints::MapCheckpoints mapCheckpointsRegtest =
boost::assign::map_list_of
- ( 0, uint256("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"))
+ ( 0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"))
;
static const Checkpoints::CCheckpointData dataRegtest = {
&mapCheckpointsRegtest,
0,
0,
0
};
class CMainParams : public CChainParams {
public:
CMainParams() {
networkID = CBaseChainParams::MAIN;
strNetworkID = "main";
/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
* a large 4-byte int at any alignment.
*/
pchMessageStart[0] = 0xf9;
pchMessageStart[1] = 0xbe;
pchMessageStart[2] = 0xb4;
pchMessageStart[3] = 0xd9;
vAlertPubKey = ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284");
nDefaultPort = 8333;
- bnProofOfWorkLimit = ~uint256(0) >> 32;
+ bnProofOfWorkLimit = ~arith_uint256(0) >> 32;
nSubsidyHalvingInterval = 210000;
nEnforceBlockUpgradeMajority = 750;
nRejectBlockOutdatedMajority = 950;
nToCheckBlockUpgradeMajority = 1000;
nMinerThreads = 0;
nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
nTargetSpacing = 10 * 60;
/**
* Build the genesis block. Note that the output of the genesis coinbase cannot
* be spent as it did not originally exist in the database.
*
* CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
* CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
* CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
* CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
* vMerkleTree: 4a5e1e
*/
const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
CMutableTransaction txNew;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].nValue = 50 * COIN;
txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
genesis.vtx.push_back(txNew);
- genesis.hashPrevBlock = 0;
+ genesis.hashPrevBlock.SetNull();
genesis.hashMerkleRoot = genesis.BuildMerkleTree();
genesis.nVersion = 1;
genesis.nTime = 1231006505;
genesis.nBits = 0x1d00ffff;
genesis.nNonce = 2083236893;
hashGenesisBlock = genesis.GetHash();
- assert(hashGenesisBlock == uint256("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"));
- assert(genesis.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
+ assert(hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"));
+ assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be"));
vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me"));
vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org"));
vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com"));
vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org"));
base58Prefixes[PUBKEY_ADDRESS] = list_of(0);
base58Prefixes[SCRIPT_ADDRESS] = list_of(5);
base58Prefixes[SECRET_KEY] = list_of(128);
base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E);
base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4);
convertSeed6(vFixedSeeds, pnSeed6_main, ARRAYLEN(pnSeed6_main));
fRequireRPCPassword = true;
fMiningRequiresPeers = true;
fDefaultCheckMemPool = false;
fAllowMinDifficultyBlocks = false;
fRequireStandard = true;
fMineBlocksOnDemand = false;
fSkipProofOfWorkCheck = false;
fTestnetToBeDeprecatedFieldRPC = false;
}
const Checkpoints::CCheckpointData& Checkpoints() const
{
return data;
}
};
static CMainParams mainParams;
/**
* Testnet (v3)
*/
class CTestNetParams : public CMainParams {
public:
CTestNetParams() {
networkID = CBaseChainParams::TESTNET;
strNetworkID = "test";
pchMessageStart[0] = 0x0b;
pchMessageStart[1] = 0x11;
pchMessageStart[2] = 0x09;
pchMessageStart[3] = 0x07;
vAlertPubKey = ParseHex("04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a");
nDefaultPort = 18333;
nEnforceBlockUpgradeMajority = 51;
nRejectBlockOutdatedMajority = 75;
nToCheckBlockUpgradeMajority = 100;
nMinerThreads = 0;
nTargetTimespan = 14 * 24 * 60 * 60; //! two weeks
nTargetSpacing = 10 * 60;
//! Modify the testnet genesis block so the timestamp is valid for a later start.
genesis.nTime = 1296688602;
genesis.nNonce = 414098458;
hashGenesisBlock = genesis.GetHash();
- assert(hashGenesisBlock == uint256("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
+ assert(hashGenesisBlock == uint256S("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
vFixedSeeds.clear();
vSeeds.clear();
vSeeds.push_back(CDNSSeedData("alexykot.me", "testnet-seed.alexykot.me"));
vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org"));
vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me"));
vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de"));
base58Prefixes[PUBKEY_ADDRESS] = list_of(111);
base58Prefixes[SCRIPT_ADDRESS] = list_of(196);
base58Prefixes[SECRET_KEY] = list_of(239);
base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x35)(0x87)(0xCF);
base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x35)(0x83)(0x94);
convertSeed6(vFixedSeeds, pnSeed6_test, ARRAYLEN(pnSeed6_test));
fRequireRPCPassword = true;
fMiningRequiresPeers = true;
fDefaultCheckMemPool = false;
fAllowMinDifficultyBlocks = true;
fRequireStandard = false;
fMineBlocksOnDemand = false;
fTestnetToBeDeprecatedFieldRPC = true;
}
const Checkpoints::CCheckpointData& Checkpoints() const
{
return dataTestnet;
}
};
static CTestNetParams testNetParams;
/**
* Regression test
*/
class CRegTestParams : public CTestNetParams {
public:
CRegTestParams() {
networkID = CBaseChainParams::REGTEST;
strNetworkID = "regtest";
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
pchMessageStart[2] = 0xb5;
pchMessageStart[3] = 0xda;
nSubsidyHalvingInterval = 150;
nEnforceBlockUpgradeMajority = 750;
nRejectBlockOutdatedMajority = 950;
nToCheckBlockUpgradeMajority = 1000;
nMinerThreads = 1;
nTargetTimespan = 14 * 24 * 60 * 60; //! two weeks
nTargetSpacing = 10 * 60;
- bnProofOfWorkLimit = ~uint256(0) >> 1;
+ bnProofOfWorkLimit = ~arith_uint256(0) >> 1;
genesis.nTime = 1296688602;
genesis.nBits = 0x207fffff;
genesis.nNonce = 2;
hashGenesisBlock = genesis.GetHash();
nDefaultPort = 18444;
- assert(hashGenesisBlock == uint256("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"));
+ assert(hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"));
vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds.
vSeeds.clear(); //! Regtest mode doesn't have any DNS seeds.
fRequireRPCPassword = false;
fMiningRequiresPeers = false;
fDefaultCheckMemPool = true;
fAllowMinDifficultyBlocks = true;
fRequireStandard = false;
fMineBlocksOnDemand = true;
fTestnetToBeDeprecatedFieldRPC = false;
}
const Checkpoints::CCheckpointData& Checkpoints() const
{
return dataRegtest;
}
};
static CRegTestParams regTestParams;
/**
* Unit test
*/
class CUnitTestParams : public CMainParams, public CModifiableParams {
public:
CUnitTestParams() {
networkID = CBaseChainParams::UNITTEST;
strNetworkID = "unittest";
nDefaultPort = 18445;
vFixedSeeds.clear(); //! Unit test mode doesn't have any fixed seeds.
vSeeds.clear(); //! Unit test mode doesn't have any DNS seeds.
fRequireRPCPassword = false;
fMiningRequiresPeers = false;
fDefaultCheckMemPool = true;
fAllowMinDifficultyBlocks = false;
fMineBlocksOnDemand = true;
}
const Checkpoints::CCheckpointData& Checkpoints() const
{
// UnitTest share the same checkpoints as MAIN
return data;
}
//! Published setters to allow changing values in unit test cases
virtual void setSubsidyHalvingInterval(int anSubsidyHalvingInterval) { nSubsidyHalvingInterval=anSubsidyHalvingInterval; }
virtual void setEnforceBlockUpgradeMajority(int anEnforceBlockUpgradeMajority) { nEnforceBlockUpgradeMajority=anEnforceBlockUpgradeMajority; }
virtual void setRejectBlockOutdatedMajority(int anRejectBlockOutdatedMajority) { nRejectBlockOutdatedMajority=anRejectBlockOutdatedMajority; }
virtual void setToCheckBlockUpgradeMajority(int anToCheckBlockUpgradeMajority) { nToCheckBlockUpgradeMajority=anToCheckBlockUpgradeMajority; }
virtual void setDefaultCheckMemPool(bool afDefaultCheckMemPool) { fDefaultCheckMemPool=afDefaultCheckMemPool; }
virtual void setAllowMinDifficultyBlocks(bool afAllowMinDifficultyBlocks) { fAllowMinDifficultyBlocks=afAllowMinDifficultyBlocks; }
virtual void setSkipProofOfWorkCheck(bool afSkipProofOfWorkCheck) { fSkipProofOfWorkCheck = afSkipProofOfWorkCheck; }
};
static CUnitTestParams unitTestParams;
static CChainParams *pCurrentParams = 0;
CModifiableParams *ModifiableParams()
{
assert(pCurrentParams);
assert(pCurrentParams==&unitTestParams);
return (CModifiableParams*)&unitTestParams;
}
const CChainParams &Params() {
assert(pCurrentParams);
return *pCurrentParams;
}
CChainParams &Params(CBaseChainParams::Network network) {
switch (network) {
case CBaseChainParams::MAIN:
return mainParams;
case CBaseChainParams::TESTNET:
return testNetParams;
case CBaseChainParams::REGTEST:
return regTestParams;
case CBaseChainParams::UNITTEST:
return unitTestParams;
default:
assert(false && "Unimplemented network");
return mainParams;
}
}
void SelectParams(CBaseChainParams::Network network) {
SelectBaseParams(network);
pCurrentParams = &Params(network);
}
bool SelectParamsFromCommandLine()
{
CBaseChainParams::Network network = NetworkIdFromCommandLine();
if (network == CBaseChainParams::MAX_NETWORK_TYPES)
return false;
SelectParams(network);
return true;
}
diff --git a/src/chainparams.h b/src/chainparams.h
index 60c7b4758..f1ef6a11f 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -1,154 +1,154 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CHAINPARAMS_H
#define BITCOIN_CHAINPARAMS_H
#include "chainparamsbase.h"
#include "checkpoints.h"
#include "primitives/block.h"
#include "protocol.h"
-#include "uint256.h"
+#include "arith_uint256.h"
#include <vector>
typedef unsigned char MessageStartChars[MESSAGE_START_SIZE];
struct CDNSSeedData {
std::string name, host;
CDNSSeedData(const std::string &strName, const std::string &strHost) : name(strName), host(strHost) {}
};
/**
* CChainParams defines various tweakable parameters of a given instance of the
* Bitcoin system. There are three: the main network on which people trade goods
* and services, the public test network which gets reset from time to time and
* a regression test mode which is intended for private networks only. It has
* minimal difficulty to ensure that blocks can be found instantly.
*/
class CChainParams
{
public:
enum Base58Type {
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
MAX_BASE58_TYPES
};
const uint256& HashGenesisBlock() const { return hashGenesisBlock; }
const MessageStartChars& MessageStart() const { return pchMessageStart; }
const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; }
int GetDefaultPort() const { return nDefaultPort; }
- const uint256& ProofOfWorkLimit() const { return bnProofOfWorkLimit; }
+ const arith_uint256& ProofOfWorkLimit() const { return bnProofOfWorkLimit; }
int SubsidyHalvingInterval() const { return nSubsidyHalvingInterval; }
/** Used to check majorities for block version upgrade */
int EnforceBlockUpgradeMajority() const { return nEnforceBlockUpgradeMajority; }
int RejectBlockOutdatedMajority() const { return nRejectBlockOutdatedMajority; }
int ToCheckBlockUpgradeMajority() const { return nToCheckBlockUpgradeMajority; }
/** Used if GenerateBitcoins is called with a negative number of threads */
int DefaultMinerThreads() const { return nMinerThreads; }
const CBlock& GenesisBlock() const { return genesis; }
bool RequireRPCPassword() const { return fRequireRPCPassword; }
/** Make miner wait to have peers to avoid wasting work */
bool MiningRequiresPeers() const { return fMiningRequiresPeers; }
/** Default value for -checkmempool argument */
bool DefaultCheckMemPool() const { return fDefaultCheckMemPool; }
/** Allow mining of a min-difficulty block */
bool AllowMinDifficultyBlocks() const { return fAllowMinDifficultyBlocks; }
/** Skip proof-of-work check: allow mining of any difficulty block */
bool SkipProofOfWorkCheck() const { return fSkipProofOfWorkCheck; }
/** Make standard checks */
bool RequireStandard() const { return fRequireStandard; }
int64_t TargetTimespan() const { return nTargetTimespan; }
int64_t TargetSpacing() const { return nTargetSpacing; }
int64_t Interval() const { return nTargetTimespan / nTargetSpacing; }
/** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */
bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
/** In the future use NetworkIDString() for RPC fields */
bool TestnetToBeDeprecatedFieldRPC() const { return fTestnetToBeDeprecatedFieldRPC; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
const std::vector<CAddress>& FixedSeeds() const { return vFixedSeeds; }
virtual const Checkpoints::CCheckpointData& Checkpoints() const = 0;
protected:
CChainParams() {}
uint256 hashGenesisBlock;
MessageStartChars pchMessageStart;
//! Raw pub key bytes for the broadcast alert signing key.
std::vector<unsigned char> vAlertPubKey;
int nDefaultPort;
- uint256 bnProofOfWorkLimit;
+ arith_uint256 bnProofOfWorkLimit;
int nSubsidyHalvingInterval;
int nEnforceBlockUpgradeMajority;
int nRejectBlockOutdatedMajority;
int nToCheckBlockUpgradeMajority;
int64_t nTargetTimespan;
int64_t nTargetSpacing;
int nMinerThreads;
std::vector<CDNSSeedData> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
CBaseChainParams::Network networkID;
std::string strNetworkID;
CBlock genesis;
std::vector<CAddress> vFixedSeeds;
bool fRequireRPCPassword;
bool fMiningRequiresPeers;
bool fDefaultCheckMemPool;
bool fAllowMinDifficultyBlocks;
bool fRequireStandard;
bool fMineBlocksOnDemand;
bool fSkipProofOfWorkCheck;
bool fTestnetToBeDeprecatedFieldRPC;
};
/**
* Modifiable parameters interface is used by test cases to adapt the parameters in order
* to test specific features more easily. Test cases should always restore the previous
* values after finalization.
*/
class CModifiableParams {
public:
//! Published setters to allow changing values in unit test cases
virtual void setSubsidyHalvingInterval(int anSubsidyHalvingInterval) =0;
virtual void setEnforceBlockUpgradeMajority(int anEnforceBlockUpgradeMajority)=0;
virtual void setRejectBlockOutdatedMajority(int anRejectBlockOutdatedMajority)=0;
virtual void setToCheckBlockUpgradeMajority(int anToCheckBlockUpgradeMajority)=0;
virtual void setDefaultCheckMemPool(bool aDefaultCheckMemPool)=0;
virtual void setAllowMinDifficultyBlocks(bool aAllowMinDifficultyBlocks)=0;
virtual void setSkipProofOfWorkCheck(bool aSkipProofOfWorkCheck)=0;
};
/**
* Return the currently selected parameters. This won't change after app startup
* outside of the unit tests.
*/
const CChainParams &Params();
/** Return parameters for the given network. */
CChainParams &Params(CBaseChainParams::Network network);
/** Get modifiable network parameters (UNITTEST only) */
CModifiableParams *ModifiableParams();
/** Sets the params returned by Params() to those for the given network. */
void SelectParams(CBaseChainParams::Network network);
/**
* Looks for -regtest or -testnet and then calls SelectParams as appropriate.
* Returns false if an invalid combination is given.
*/
bool SelectParamsFromCommandLine();
#endif // BITCOIN_CHAINPARAMS_H
diff --git a/src/coins.cpp b/src/coins.cpp
index f4599ff39..ef4f96fde 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -1,246 +1,246 @@
// Copyright (c) 2012-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "coins.h"
#include "random.h"
#include <assert.h>
/**
* calculate number of bytes for the bitmask, and its number of non-zero bytes
* each bit in the bitmask represents the availability of one output, but the
* availabilities of the first two outputs are encoded separately
*/
void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const {
unsigned int nLastUsedByte = 0;
for (unsigned int b = 0; 2+b*8 < vout.size(); b++) {
bool fZero = true;
for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) {
if (!vout[2+b*8+i].IsNull()) {
fZero = false;
continue;
}
}
if (!fZero) {
nLastUsedByte = b + 1;
nNonzeroBytes++;
}
}
nBytes += nLastUsedByte;
}
bool CCoins::Spend(uint32_t nPos)
{
if (nPos >= vout.size() || vout[nPos].IsNull())
return false;
vout[nPos].SetNull();
Cleanup();
return true;
}
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
-uint256 CCoinsView::GetBestBlock() const { return uint256(0); }
+uint256 CCoinsView::GetBestBlock() const { return uint256(); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
-CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), hashBlock(0) { }
+CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false) { }
CCoinsViewCache::~CCoinsViewCache()
{
assert(!hasModifier);
}
CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const {
CCoinsMap::iterator it = cacheCoins.find(txid);
if (it != cacheCoins.end())
return it;
CCoins tmp;
if (!base->GetCoins(txid, tmp))
return cacheCoins.end();
CCoinsMap::iterator ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())).first;
tmp.swap(ret->second.coins);
if (ret->second.coins.IsPruned()) {
// The parent only has an empty entry for this txid; we can consider our
// version as fresh.
ret->second.flags = CCoinsCacheEntry::FRESH;
}
return ret;
}
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
CCoinsMap::const_iterator it = FetchCoins(txid);
if (it != cacheCoins.end()) {
coins = it->second.coins;
return true;
}
return false;
}
CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
assert(!hasModifier);
hasModifier = true;
std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry()));
if (ret.second) {
if (!base->GetCoins(txid, ret.first->second.coins)) {
// The parent view does not have this entry; mark it as fresh.
ret.first->second.coins.Clear();
ret.first->second.flags = CCoinsCacheEntry::FRESH;
} else if (ret.first->second.coins.IsPruned()) {
// The parent view only has a pruned entry for this; mark it as fresh.
ret.first->second.flags = CCoinsCacheEntry::FRESH;
}
}
// Assume that whenever ModifyCoins is called, the entry will be modified.
ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
return CCoinsModifier(*this, ret.first);
}
const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const {
CCoinsMap::const_iterator it = FetchCoins(txid);
if (it == cacheCoins.end()) {
return NULL;
} else {
return &it->second.coins;
}
}
bool CCoinsViewCache::HaveCoins(const uint256 &txid) const {
CCoinsMap::const_iterator it = FetchCoins(txid);
// We're using vtx.empty() instead of IsPruned here for performance reasons,
// as we only care about the case where a transaction was replaced entirely
// in a reorganization (which wipes vout entirely, as opposed to spending
// which just cleans individual outputs).
return (it != cacheCoins.end() && !it->second.coins.vout.empty());
}
uint256 CCoinsViewCache::GetBestBlock() const {
- if (hashBlock == uint256(0))
+ if (hashBlock.IsNull())
hashBlock = base->GetBestBlock();
return hashBlock;
}
void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
hashBlock = hashBlockIn;
}
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
assert(!hasModifier);
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
if (itUs == cacheCoins.end()) {
if (!it->second.coins.IsPruned()) {
// The parent cache does not have an entry, while the child
// cache does have (a non-pruned) one. Move the data up, and
// mark it as fresh (if the grandparent did have it, we
// would have pulled it in at first GetCoins).
assert(it->second.flags & CCoinsCacheEntry::FRESH);
CCoinsCacheEntry& entry = cacheCoins[it->first];
entry.coins.swap(it->second.coins);
entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
}
} else {
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
// The grandparent does not have an entry, and the child is
// modified and being pruned. This means we can just delete
// it from the parent.
cacheCoins.erase(itUs);
} else {
// A normal modification.
itUs->second.coins.swap(it->second.coins);
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
}
}
}
CCoinsMap::iterator itOld = it++;
mapCoins.erase(itOld);
}
hashBlock = hashBlockIn;
return true;
}
bool CCoinsViewCache::Flush() {
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
cacheCoins.clear();
return fOk;
}
unsigned int CCoinsViewCache::GetCacheSize() const {
return cacheCoins.size();
}
const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const
{
const CCoins* coins = AccessCoins(input.prevout.hash);
assert(coins && coins->IsAvailable(input.prevout.n));
return coins->vout[input.prevout.n];
}
CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
{
if (tx.IsCoinBase())
return 0;
CAmount nResult = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
nResult += GetOutputFor(tx.vin[i]).nValue;
return nResult;
}
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
if (!tx.IsCoinBase()) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins* coins = AccessCoins(prevout.hash);
if (!coins || !coins->IsAvailable(prevout.n)) {
return false;
}
}
}
return true;
}
double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
{
if (tx.IsCoinBase())
return 0.0;
double dResult = 0.0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
const CCoins* coins = AccessCoins(txin.prevout.hash);
assert(coins);
if (!coins->IsAvailable(txin.prevout.n)) continue;
if (coins->nHeight < nHeight) {
dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight);
}
}
return tx.ComputePriority(dResult);
}
CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_) : cache(cache_), it(it_) {}
CCoinsModifier::~CCoinsModifier()
{
assert(cache.hasModifier);
cache.hasModifier = false;
it->second.coins.Cleanup();
if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
cache.cacheCoins.erase(it);
}
}
diff --git a/src/coins.h b/src/coins.h
index 4f1afb00d..fe2eaa08e 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -1,447 +1,447 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_COINS_H
#define BITCOIN_COINS_H
#include "compressor.h"
#include "serialize.h"
#include "uint256.h"
#include <assert.h>
#include <stdint.h>
#include <boost/foreach.hpp>
#include <boost/unordered_map.hpp>
/**
* Pruned version of CTransaction: only retains metadata and unspent transaction outputs
*
* Serialized format:
* - VARINT(nVersion)
* - VARINT(nCode)
* - unspentness bitvector, for vout[2] and further; least significant byte first
* - the non-spent CTxOuts (via CTxOutCompressor)
* - VARINT(nHeight)
*
* The nCode value consists of:
* - bit 1: IsCoinBase()
* - bit 2: vout[0] is not spent
* - bit 4: vout[1] is not spent
* - The higher bits encode N, the number of non-zero bytes in the following bitvector.
* - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at
* least one non-spent output).
*
* Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e
* <><><--------------------------------------------><---->
* | \ | /
* version code vout[1] height
*
* - version = 1
* - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow)
* - unspentness bitvector: as 0 non-zero bytes follow, it has length 0
* - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35
* * 8358: compact amount representation for 60000000000 (600 BTC)
* * 00: special txout type pay-to-pubkey-hash
* * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160
* - height = 203998
*
*
* Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b
* <><><--><--------------------------------------------------><----------------------------------------------><---->
* / \ \ | | /
* version code unspentness vout[4] vout[16] height
*
* - version = 1
* - code = 9 (coinbase, neither vout[0] or vout[1] are unspent,
* 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow)
* - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent
* - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee
* * 86ef97d579: compact amount representation for 234925952 (2.35 BTC)
* * 00: special txout type pay-to-pubkey-hash
* * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160
* - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4
* * bbd123: compact amount representation for 110397 (0.001 BTC)
* * 00: special txout type pay-to-pubkey-hash
* * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160
* - height = 120891
*/
class CCoins
{
public:
//! whether transaction is a coinbase
bool fCoinBase;
//! unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped
std::vector<CTxOut> vout;
//! at which height this transaction was included in the active block chain
int nHeight;
//! version of the CTransaction; accesses to this value should probably check for nHeight as well,
//! as new tx version will probably only be introduced at certain heights
int nVersion;
void FromTx(const CTransaction &tx, int nHeightIn) {
fCoinBase = tx.IsCoinBase();
vout = tx.vout;
nHeight = nHeightIn;
nVersion = tx.nVersion;
ClearUnspendable();
}
//! construct a CCoins from a CTransaction, at a given height
CCoins(const CTransaction &tx, int nHeightIn) {
FromTx(tx, nHeightIn);
}
void Clear() {
fCoinBase = false;
std::vector<CTxOut>().swap(vout);
nHeight = 0;
nVersion = 0;
}
//! empty constructor
CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { }
//!remove spent outputs at the end of vout
void Cleanup() {
while (vout.size() > 0 && vout.back().IsNull())
vout.pop_back();
if (vout.empty())
std::vector<CTxOut>().swap(vout);
}
void ClearUnspendable() {
BOOST_FOREACH(CTxOut &txout, vout) {
if (txout.scriptPubKey.IsUnspendable())
txout.SetNull();
}
Cleanup();
}
void swap(CCoins &to) {
std::swap(to.fCoinBase, fCoinBase);
to.vout.swap(vout);
std::swap(to.nHeight, nHeight);
std::swap(to.nVersion, nVersion);
}
//! equality test
friend bool operator==(const CCoins &a, const CCoins &b) {
// Empty CCoins objects are always equal.
if (a.IsPruned() && b.IsPruned())
return true;
return a.fCoinBase == b.fCoinBase &&
a.nHeight == b.nHeight &&
a.nVersion == b.nVersion &&
a.vout == b.vout;
}
friend bool operator!=(const CCoins &a, const CCoins &b) {
return !(a == b);
}
void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const;
bool IsCoinBase() const {
return fCoinBase;
}
unsigned int GetSerializeSize(int nType, int nVersion) const {
unsigned int nSize = 0;
unsigned int nMaskSize = 0, nMaskCode = 0;
CalcMaskSize(nMaskSize, nMaskCode);
bool fFirst = vout.size() > 0 && !vout[0].IsNull();
bool fSecond = vout.size() > 1 && !vout[1].IsNull();
assert(fFirst || fSecond || nMaskCode);
unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
// version
nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion);
// size of header code
nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion);
// spentness bitmask
nSize += nMaskSize;
// txouts themself
for (unsigned int i = 0; i < vout.size(); i++)
if (!vout[i].IsNull())
nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion);
// height
nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion);
return nSize;
}
template<typename Stream>
void Serialize(Stream &s, int nType, int nVersion) const {
unsigned int nMaskSize = 0, nMaskCode = 0;
CalcMaskSize(nMaskSize, nMaskCode);
bool fFirst = vout.size() > 0 && !vout[0].IsNull();
bool fSecond = vout.size() > 1 && !vout[1].IsNull();
assert(fFirst || fSecond || nMaskCode);
unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
// version
::Serialize(s, VARINT(this->nVersion), nType, nVersion);
// header code
::Serialize(s, VARINT(nCode), nType, nVersion);
// spentness bitmask
for (unsigned int b = 0; b<nMaskSize; b++) {
unsigned char chAvail = 0;
for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++)
if (!vout[2+b*8+i].IsNull())
chAvail |= (1 << i);
::Serialize(s, chAvail, nType, nVersion);
}
// txouts themself
for (unsigned int i = 0; i < vout.size(); i++) {
if (!vout[i].IsNull())
::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion);
}
// coinbase height
::Serialize(s, VARINT(nHeight), nType, nVersion);
}
template<typename Stream>
void Unserialize(Stream &s, int nType, int nVersion) {
unsigned int nCode = 0;
// version
::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
// header code
::Unserialize(s, VARINT(nCode), nType, nVersion);
fCoinBase = nCode & 1;
std::vector<bool> vAvail(2, false);
vAvail[0] = (nCode & 2) != 0;
vAvail[1] = (nCode & 4) != 0;
unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1);
// spentness bitmask
while (nMaskCode > 0) {
unsigned char chAvail = 0;
::Unserialize(s, chAvail, nType, nVersion);
for (unsigned int p = 0; p < 8; p++) {
bool f = (chAvail & (1 << p)) != 0;
vAvail.push_back(f);
}
if (chAvail != 0)
nMaskCode--;
}
// txouts themself
vout.assign(vAvail.size(), CTxOut());
for (unsigned int i = 0; i < vAvail.size(); i++) {
if (vAvail[i])
::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion);
}
// coinbase height
::Unserialize(s, VARINT(nHeight), nType, nVersion);
Cleanup();
}
//! mark a vout spent
bool Spend(uint32_t nPos);
//! check whether a particular output is still available
bool IsAvailable(unsigned int nPos) const {
return (nPos < vout.size() && !vout[nPos].IsNull());
}
//! check whether the entire CCoins is spent
//! note that only !IsPruned() CCoins can be serialized
bool IsPruned() const {
BOOST_FOREACH(const CTxOut &out, vout)
if (!out.IsNull())
return false;
return true;
}
};
class CCoinsKeyHasher
{
private:
uint256 salt;
public:
CCoinsKeyHasher();
/**
* This *must* return size_t. With Boost 1.46 on 32-bit systems the
* unordered_map will behave unpredictably if the custom hasher returns a
* uint64_t, resulting in failures when syncing the chain (#4634).
*/
size_t operator()(const uint256& key) const {
return key.GetHash(salt);
}
};
struct CCoinsCacheEntry
{
CCoins coins; // The actual cached data.
unsigned char flags;
enum Flags {
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
FRESH = (1 << 1), // The parent view does not have this entry (or it is pruned).
};
CCoinsCacheEntry() : coins(), flags(0) {}
};
typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap;
struct CCoinsStats
{
int nHeight;
uint256 hashBlock;
uint64_t nTransactions;
uint64_t nTransactionOutputs;
uint64_t nSerializedSize;
uint256 hashSerialized;
CAmount nTotalAmount;
- CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {}
+ CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), nTotalAmount(0) {}
};
/** Abstract view on the open txout dataset. */
class CCoinsView
{
public:
//! Retrieve the CCoins (unspent transaction outputs) for a given txid
virtual bool GetCoins(const uint256 &txid, CCoins &coins) const;
//! Just check whether we have data for a given txid.
//! This may (but cannot always) return true for fully spent transactions
virtual bool HaveCoins(const uint256 &txid) const;
//! Retrieve the block hash whose state this CCoinsView currently represents
virtual uint256 GetBestBlock() const;
//! Do a bulk modification (multiple CCoins changes + BestBlock change).
//! The passed mapCoins can be modified.
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
//! Calculate statistics about the unspent transaction output set
virtual bool GetStats(CCoinsStats &stats) const;
//! As we use CCoinsViews polymorphically, have a virtual destructor
virtual ~CCoinsView() {}
};
/** CCoinsView backed by another CCoinsView */
class CCoinsViewBacked : public CCoinsView
{
protected:
CCoinsView *base;
public:
CCoinsViewBacked(CCoinsView *viewIn);
bool GetCoins(const uint256 &txid, CCoins &coins) const;
bool HaveCoins(const uint256 &txid) const;
uint256 GetBestBlock() const;
void SetBackend(CCoinsView &viewIn);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
bool GetStats(CCoinsStats &stats) const;
};
class CCoinsViewCache;
/**
* A reference to a mutable cache entry. Encapsulating it allows us to run
* cleanup code after the modification is finished, and keeping track of
* concurrent modifications.
*/
class CCoinsModifier
{
private:
CCoinsViewCache& cache;
CCoinsMap::iterator it;
CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_);
public:
CCoins* operator->() { return &it->second.coins; }
CCoins& operator*() { return it->second.coins; }
~CCoinsModifier();
friend class CCoinsViewCache;
};
/** CCoinsView that adds a memory cache for transactions to another CCoinsView */
class CCoinsViewCache : public CCoinsViewBacked
{
protected:
/* Whether this cache has an active modifier. */
bool hasModifier;
/**
* Make mutable so that we can "fill the cache" even from Get-methods
* declared as "const".
*/
mutable uint256 hashBlock;
mutable CCoinsMap cacheCoins;
public:
CCoinsViewCache(CCoinsView *baseIn);
~CCoinsViewCache();
// Standard CCoinsView methods
bool GetCoins(const uint256 &txid, CCoins &coins) const;
bool HaveCoins(const uint256 &txid) const;
uint256 GetBestBlock() const;
void SetBestBlock(const uint256 &hashBlock);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
/**
* Return a pointer to CCoins in the cache, or NULL if not found. This is
* more efficient than GetCoins. Modifications to other cache entries are
* allowed while accessing the returned pointer.
*/
const CCoins* AccessCoins(const uint256 &txid) const;
/**
* Return a modifiable reference to a CCoins. If no entry with the given
* txid exists, a new one is created. Simultaneous modifications are not
* allowed.
*/
CCoinsModifier ModifyCoins(const uint256 &txid);
/**
* Push the modifications applied to this cache to its base.
* Failure to call this method before destruction will cause the changes to be forgotten.
* If false is returned, the state of this cache (and its backing view) will be undefined.
*/
bool Flush();
//! Calculate the size of the cache (in number of transactions)
unsigned int GetCacheSize() const;
/**
* Amount of bitcoins coming in to a transaction
* Note that lightweight clients may not know anything besides the hash of previous transactions,
* so may not be able to calculate this.
*
* @param[in] tx transaction for which we are checking input total
* @return Sum of value of all inputs (scriptSigs)
*/
CAmount GetValueIn(const CTransaction& tx) const;
//! Check whether all prevouts of the transaction are present in the UTXO set represented by this view
bool HaveInputs(const CTransaction& tx) const;
//! Return priority of tx at height nHeight
double GetPriority(const CTransaction &tx, int nHeight) const;
const CTxOut &GetOutputFor(const CTxIn& input) const;
friend class CCoinsModifier;
private:
CCoinsMap::iterator FetchCoins(const uint256 &txid);
CCoinsMap::const_iterator FetchCoins(const uint256 &txid) const;
/**
* By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache.
*/
CCoinsViewCache(const CCoinsViewCache &);
};
#endif // BITCOIN_COINS_H
diff --git a/src/core_write.cpp b/src/core_write.cpp
index b6fd7fe87..c3babec2f 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -1,134 +1,134 @@
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "core_io.h"
#include "base58.h"
#include "primitives/transaction.h"
#include "script/script.h"
#include "script/standard.h"
#include "serialize.h"
#include "streams.h"
#include "univalue/univalue.h"
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include <boost/foreach.hpp>
using namespace std;
string FormatScript(const CScript& script)
{
string ret;
CScript::const_iterator it = script.begin();
opcodetype op;
while (it != script.end()) {
CScript::const_iterator it2 = it;
vector<unsigned char> vch;
if (script.GetOp2(it, op, &vch)) {
if (op == OP_0) {
ret += "0 ";
continue;
} else if ((op >= OP_1 && op <= OP_16) || op == OP_1NEGATE) {
ret += strprintf("%i ", op - OP_1NEGATE - 1);
continue;
} else if (op >= OP_NOP && op <= OP_CHECKMULTISIGVERIFY) {
string str(GetOpName(op));
if (str.substr(0, 3) == string("OP_")) {
ret += str.substr(3, string::npos) + " ";
continue;
}
}
if (vch.size() > 0) {
ret += strprintf("0x%x 0x%x ", HexStr(it2, it - vch.size()), HexStr(it - vch.size(), it));
} else {
ret += strprintf("0x%x", HexStr(it2, it));
}
continue;
}
ret += strprintf("0x%x ", HexStr(it2, script.end()));
break;
}
return ret.substr(0, ret.size() - 1);
}
string EncodeHexTx(const CTransaction& tx)
{
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << tx;
return HexStr(ssTx.begin(), ssTx.end());
}
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
UniValue& out, bool fIncludeHex)
{
txnouttype type;
vector<CTxDestination> addresses;
int nRequired;
out.pushKV("asm", scriptPubKey.ToString());
if (fIncludeHex)
out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
out.pushKV("type", GetTxnOutputType(type));
return;
}
out.pushKV("reqSigs", nRequired);
out.pushKV("type", GetTxnOutputType(type));
UniValue a(UniValue::VARR);
BOOST_FOREACH(const CTxDestination& addr, addresses)
a.push_back(CBitcoinAddress(addr).ToString());
out.pushKV("addresses", a);
}
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
{
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("version", tx.nVersion);
entry.pushKV("locktime", (int64_t)tx.nLockTime);
UniValue vin(UniValue::VARR);
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase())
in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
else {
in.pushKV("txid", txin.prevout.hash.GetHex());
in.pushKV("vout", (int64_t)txin.prevout.n);
UniValue o(UniValue::VOBJ);
o.pushKV("asm", txin.scriptSig.ToString());
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
in.pushKV("scriptSig", o);
}
in.pushKV("sequence", (int64_t)txin.nSequence);
vin.push_back(in);
}
entry.pushKV("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);
UniValue outValue(UniValue::VNUM, FormatMoney(txout.nValue));
out.pushKV("value", outValue);
out.pushKV("n", (int64_t)i);
UniValue o(UniValue::VOBJ);
ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
out.pushKV("scriptPubKey", o);
vout.push_back(out);
}
entry.pushKV("vout", vout);
- if (hashBlock != 0)
+ if (!hashBlock.IsNull())
entry.pushKV("blockhash", hashBlock.GetHex());
entry.pushKV("hex", EncodeHexTx(tx)); // the hex-encoded transaction. used the name "hex" to be consistent with the verbose output of "getrawtransaction".
}
diff --git a/src/key.cpp b/src/key.cpp
index f09536fbb..2235c271d 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -1,222 +1,223 @@
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "key.h"
+#include "arith_uint256.h"
#include "crypto/hmac_sha512.h"
#include "crypto/rfc6979_hmac_sha256.h"
#include "eccryptoverify.h"
#include "pubkey.h"
#include "random.h"
#include <secp256k1.h>
#include "ecwrapper.h"
//! anonymous namespace
namespace {
class CSecp256k1Init {
public:
CSecp256k1Init() {
secp256k1_start(SECP256K1_START_SIGN);
}
~CSecp256k1Init() {
secp256k1_stop();
}
};
static CSecp256k1Init instance_of_csecp256k1;
} // anon namespace
bool CKey::Check(const unsigned char *vch) {
return eccrypto::Check(vch);
}
void CKey::MakeNewKey(bool fCompressedIn) {
RandAddSeedPerfmon();
do {
GetRandBytes(vch, sizeof(vch));
} while (!Check(vch));
fValid = true;
fCompressed = fCompressedIn;
}
bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) {
if (!secp256k1_ec_privkey_import((unsigned char*)begin(), &privkey[0], privkey.size()))
return false;
fCompressed = fCompressedIn;
fValid = true;
return true;
}
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
CPrivKey privkey;
int privkeylen, ret;
privkey.resize(279);
privkeylen = 279;
ret = secp256k1_ec_privkey_export(begin(), (unsigned char*)&privkey[0], &privkeylen, fCompressed);
assert(ret);
privkey.resize(privkeylen);
return privkey;
}
CPubKey CKey::GetPubKey() const {
assert(fValid);
CPubKey result;
int clen = 65;
int ret = secp256k1_ec_pubkey_create((unsigned char*)result.begin(), &clen, begin(), fCompressed);
assert((int)result.size() == clen);
assert(ret);
assert(result.IsValid());
return result;
}
bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, uint32_t test_case) const {
if (!fValid)
return false;
vchSig.resize(72);
RFC6979_HMAC_SHA256 prng(begin(), 32, (unsigned char*)&hash, 32);
do {
uint256 nonce;
prng.Generate((unsigned char*)&nonce, 32);
- nonce += test_case;
+ nonce = ArithToUint256(UintToArith256(nonce) + test_case);
int nSigLen = 72;
int ret = secp256k1_ecdsa_sign((const unsigned char*)&hash, (unsigned char*)&vchSig[0], &nSigLen, begin(), (unsigned char*)&nonce);
- nonce = 0;
+ nonce = uint256();
if (ret) {
vchSig.resize(nSigLen);
return true;
}
} while(true);
}
bool CKey::VerifyPubKey(const CPubKey& pubkey) const {
if (pubkey.IsCompressed() != fCompressed) {
return false;
}
unsigned char rnd[8];
std::string str = "Bitcoin key verification\n";
GetRandBytes(rnd, sizeof(rnd));
uint256 hash;
CHash256().Write((unsigned char*)str.data(), str.size()).Write(rnd, sizeof(rnd)).Finalize((unsigned char*)&hash);
std::vector<unsigned char> vchSig;
Sign(hash, vchSig);
return pubkey.Verify(hash, vchSig);
}
bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
if (!fValid)
return false;
vchSig.resize(65);
int rec = -1;
RFC6979_HMAC_SHA256 prng(begin(), 32, (unsigned char*)&hash, 32);
do {
uint256 nonce;
prng.Generate((unsigned char*)&nonce, 32);
int ret = secp256k1_ecdsa_sign_compact((const unsigned char*)&hash, &vchSig[1], begin(), (unsigned char*)&nonce, &rec);
- nonce = 0;
+ nonce = uint256();
if (ret)
break;
} while(true);
assert(rec != -1);
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
return true;
}
bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) {
if (!secp256k1_ec_privkey_import((unsigned char*)begin(), &privkey[0], privkey.size()))
return false;
fCompressed = vchPubKey.IsCompressed();
fValid = true;
if (fSkipCheck)
return true;
return VerifyPubKey(vchPubKey);
}
bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const {
assert(IsValid());
assert(IsCompressed());
unsigned char out[64];
LockObject(out);
if ((nChild >> 31) == 0) {
CPubKey pubkey = GetPubKey();
assert(pubkey.begin() + 33 == pubkey.end());
BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out);
} else {
assert(begin() + 32 == end());
BIP32Hash(cc, nChild, 0, begin(), out);
}
memcpy(ccChild, out+32, 32);
memcpy((unsigned char*)keyChild.begin(), begin(), 32);
bool ret = secp256k1_ec_privkey_tweak_add((unsigned char*)keyChild.begin(), out);
UnlockObject(out);
keyChild.fCompressed = true;
keyChild.fValid = ret;
return ret;
}
bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = key.GetPubKey().GetID();
memcpy(&out.vchFingerprint[0], &id, 4);
out.nChild = nChild;
return key.Derive(out.key, out.vchChainCode, nChild, vchChainCode);
}
void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
unsigned char out[64];
LockObject(out);
CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(out);
key.Set(&out[0], &out[32], true);
memcpy(vchChainCode, &out[32], 32);
UnlockObject(out);
nDepth = 0;
nChild = 0;
memset(vchFingerprint, 0, sizeof(vchFingerprint));
}
CExtPubKey CExtKey::Neuter() const {
CExtPubKey ret;
ret.nDepth = nDepth;
memcpy(&ret.vchFingerprint[0], &vchFingerprint[0], 4);
ret.nChild = nChild;
ret.pubkey = key.GetPubKey();
memcpy(&ret.vchChainCode[0], &vchChainCode[0], 32);
return ret;
}
void CExtKey::Encode(unsigned char code[74]) const {
code[0] = nDepth;
memcpy(code+1, vchFingerprint, 4);
code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
memcpy(code+9, vchChainCode, 32);
code[41] = 0;
assert(key.size() == 32);
memcpy(code+42, key.begin(), 32);
}
void CExtKey::Decode(const unsigned char code[74]) {
nDepth = code[0];
memcpy(vchFingerprint, code+1, 4);
nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
memcpy(vchChainCode, code+9, 32);
key.Set(code+42, code+74, true);
}
bool ECC_InitSanityCheck() {
#if !defined(USE_SECP256K1)
if (!CECKey::SanityCheck()) {
return false;
}
#endif
CKey key;
key.MakeNewKey(true);
CPubKey pubkey = key.GetPubKey();
return key.VerifyPubKey(pubkey);
}
diff --git a/src/main.cpp b/src/main.cpp
index 4fcb4f093..d115f763f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4594 +1,4595 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "main.h"
+#include "arith_uint256.h"
#include "addrman.h"
#include "alert.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
#include "init.h"
#include "merkleblock.h"
#include "net.h"
#include "pow.h"
#include "txdb.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "undo.h"
#include "util.h"
#include "utilmoneystr.h"
#include <sstream>
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/thread.hpp>
using namespace boost;
using namespace std;
#if defined(NDEBUG)
# error "Bitcoin cannot be compiled without assertions."
#endif
/**
* Global state
*/
CCriticalSection cs_main;
BlockMap mapBlockIndex;
CChain chainActive;
CBlockIndex *pindexBestHeader = NULL;
int64_t nTimeBestReceived = 0;
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
bool fTxIndex = false;
bool fIsBareMultisigStd = true;
unsigned int nCoinCacheSize = 5000;
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
CFeeRate minRelayTxFee = CFeeRate(1000);
CTxMemPool mempool(::minRelayTxFee);
struct COrphanTx {
CTransaction tx;
NodeId fromPeer;
};
map<uint256, COrphanTx> mapOrphanTransactions;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
void EraseOrphansFor(NodeId peer);
/**
* Returns true if there are nRequired or more blocks of minVersion or above
* in the last Params().ToCheckBlockUpgradeMajority() blocks, starting at pstart
* and going backwards.
*/
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired);
/** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS;
const string strMessageMagic = "Bitcoin Signed Message:\n";
// Internal stuff
namespace {
struct CBlockIndexWorkComparator
{
bool operator()(CBlockIndex *pa, CBlockIndex *pb) {
// First sort by most total work, ...
if (pa->nChainWork > pb->nChainWork) return false;
if (pa->nChainWork < pb->nChainWork) return true;
// ... then by earliest time received, ...
if (pa->nSequenceId < pb->nSequenceId) return false;
if (pa->nSequenceId > pb->nSequenceId) return true;
// Use pointer address as tie breaker (should only happen with blocks
// loaded from disk, as those all have id 0).
if (pa < pb) return false;
if (pa > pb) return true;
// Identical blocks.
return false;
}
};
CBlockIndex *pindexBestInvalid;
/**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS or better that are at least
* as good as our current tip. Entries may be failed, though.
*/
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/** Number of nodes with fSyncStarted. */
int nSyncStarted = 0;
/** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. */
multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
int nLastBlockFile = 0;
/**
* Every received block is assigned a unique and increasing identifier, so we
* know which one to give priority in case of a fork.
*/
CCriticalSection cs_nBlockSequenceId;
/** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
uint32_t nBlockSequenceId = 1;
/**
* Sources of received blocks, to be able to send them reject messages or ban
* them, if processing happens afterwards. Protected by cs_main.
*/
map<uint256, NodeId> mapBlockSource;
/** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
struct QueuedBlock {
uint256 hash;
CBlockIndex *pindex; //! Optional.
int64_t nTime; //! Time of "getdata" request in microseconds.
};
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
/** Number of preferable block download peers. */
int nPreferredDownload = 0;
/** Dirty block index entries. */
set<CBlockIndex*> setDirtyBlockIndex;
/** Dirty block file entries. */
set<int> setDirtyFileInfo;
} // anon namespace
//////////////////////////////////////////////////////////////////////////////
//
// dispatching functions
//
// These functions dispatch to one or all registered wallets
namespace {
struct CMainSignals {
/** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */
boost::signals2::signal<void (const CTransaction &, const CBlock *)> SyncTransaction;
/** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */
boost::signals2::signal<void (const uint256 &)> EraseTransaction;
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
/** Notifies listeners of a new active block chain. */
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
/** Notifies listeners about an inventory item being seen on the network. */
boost::signals2::signal<void (const uint256 &)> Inventory;
/** Tells listeners to broadcast their data. */
boost::signals2::signal<void ()> Broadcast;
/** Notifies listeners of a block validation result */
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
} g_signals;
} // anon namespace
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2));
g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn));
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn));
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2));
}
void UnregisterAllValidationInterfaces() {
g_signals.BlockChecked.disconnect_all_slots();
g_signals.Broadcast.disconnect_all_slots();
g_signals.Inventory.disconnect_all_slots();
g_signals.SetBestChain.disconnect_all_slots();
g_signals.UpdatedTransaction.disconnect_all_slots();
g_signals.EraseTransaction.disconnect_all_slots();
g_signals.SyncTransaction.disconnect_all_slots();
}
void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) {
g_signals.SyncTransaction(tx, pblock);
}
//////////////////////////////////////////////////////////////////////////////
//
// Registration of network node signals.
//
namespace {
struct CBlockReject {
unsigned char chRejectCode;
string strRejectReason;
uint256 hashBlock;
};
/**
* Maintain validation-specific state about nodes, protected by cs_main, instead
* by CNode's own locks. This simplifies asynchronous operation, where
* processing of incoming data is done after the ProcessMessage call returns,
* and we're no longer holding the node's locks.
*/
struct CNodeState {
//! Accumulated misbehaviour score for this peer.
int nMisbehavior;
//! Whether this peer should be disconnected and banned (unless whitelisted).
bool fShouldBan;
//! String name of this peer (debugging/logging purposes).
std::string name;
//! List of asynchronously-determined block rejections to notify this peer about.
std::vector<CBlockReject> rejects;
//! The best known block we know this peer has announced.
CBlockIndex *pindexBestKnownBlock;
//! The hash of the last unknown block this peer has announced.
uint256 hashLastUnknownBlock;
//! The last full block we both have.
CBlockIndex *pindexLastCommonBlock;
//! Whether we've started headers synchronization with this peer.
bool fSyncStarted;
//! Since when we're stalling block download progress (in microseconds), or 0.
int64_t nStallingSince;
list<QueuedBlock> vBlocksInFlight;
int nBlocksInFlight;
//! Whether we consider this a preferred download peer.
bool fPreferredDownload;
CNodeState() {
nMisbehavior = 0;
fShouldBan = false;
pindexBestKnownBlock = NULL;
- hashLastUnknownBlock = uint256(0);
+ hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = NULL;
fSyncStarted = false;
nStallingSince = 0;
nBlocksInFlight = 0;
fPreferredDownload = false;
}
};
/** Map maintaining per-node state. Requires cs_main. */
map<NodeId, CNodeState> mapNodeState;
// Requires cs_main.
CNodeState *State(NodeId pnode) {
map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
if (it == mapNodeState.end())
return NULL;
return &it->second;
}
int GetHeight()
{
LOCK(cs_main);
return chainActive.Height();
}
void UpdatePreferredDownload(CNode* node, CNodeState* state)
{
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
nPreferredDownload += state->fPreferredDownload;
}
void InitializeNode(NodeId nodeid, const CNode *pnode) {
LOCK(cs_main);
CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second;
state.name = pnode->addrName;
}
void FinalizeNode(NodeId nodeid) {
LOCK(cs_main);
CNodeState *state = State(nodeid);
if (state->fSyncStarted)
nSyncStarted--;
BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight)
mapBlocksInFlight.erase(entry.hash);
EraseOrphansFor(nodeid);
nPreferredDownload -= state->fPreferredDownload;
mapNodeState.erase(nodeid);
}
// Requires cs_main.
void MarkBlockAsReceived(const uint256& hash) {
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
state->vBlocksInFlight.erase(itInFlight->second.second);
state->nBlocksInFlight--;
state->nStallingSince = 0;
mapBlocksInFlight.erase(itInFlight);
}
}
// Requires cs_main.
void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex = NULL) {
CNodeState *state = State(nodeid);
assert(state != NULL);
// Make sure it's not listed somewhere already.
MarkBlockAsReceived(hash);
QueuedBlock newentry = {hash, pindex, GetTimeMicros()};
list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry);
state->nBlocksInFlight++;
mapBlocksInFlight[hash] = std::make_pair(nodeid, it);
}
/** Check whether the last unknown block a peer advertized is not yet known. */
void ProcessBlockAvailability(NodeId nodeid) {
CNodeState *state = State(nodeid);
assert(state != NULL);
- if (state->hashLastUnknownBlock != 0) {
+ if (!state->hashLastUnknownBlock.IsNull()) {
BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) {
if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
state->pindexBestKnownBlock = itOld->second;
- state->hashLastUnknownBlock = uint256(0);
+ state->hashLastUnknownBlock.SetNull();
}
}
}
/** Update tracking information about which blocks a peer is assumed to have. */
void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
CNodeState *state = State(nodeid);
assert(state != NULL);
ProcessBlockAvailability(nodeid);
BlockMap::iterator it = mapBlockIndex.find(hash);
if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
// An actually better block was announced.
if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
state->pindexBestKnownBlock = it->second;
} else {
// An unknown block was announced; just assume that the latest one is the best one.
state->hashLastUnknownBlock = hash;
}
}
/** Find the last common ancestor two blocks have.
* Both pa and pb must be non-NULL. */
CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
if (pa->nHeight > pb->nHeight) {
pa = pa->GetAncestor(pb->nHeight);
} else if (pb->nHeight > pa->nHeight) {
pb = pb->GetAncestor(pa->nHeight);
}
while (pa != pb && pa && pb) {
pa = pa->pprev;
pb = pb->pprev;
}
// Eventually all chain branches meet at the genesis block.
assert(pa == pb);
return pa;
}
/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
* at most count entries. */
void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller) {
if (count == 0)
return;
vBlocks.reserve(vBlocks.size() + count);
CNodeState *state = State(nodeid);
assert(state != NULL);
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);
if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
// This peer has nothing interesting.
return;
}
if (state->pindexLastCommonBlock == NULL) {
// Bootstrap quickly by guessing a parent of our best tip is the forking point.
// Guessing wrong in either direction is not a problem.
state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
}
// If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
// of their current tip anymore. Go back enough to fix that.
state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock);
if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
return;
std::vector<CBlockIndex*> vToFetch;
CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
// Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
// linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to
// download that next block if the window were 1 larger.
int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW;
int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1);
NodeId waitingfor = -1;
while (pindexWalk->nHeight < nMaxHeight) {
// Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards
// pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive
// as iterating over ~100 CBlockIndex* entries anyway.
int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128));
vToFetch.resize(nToFetch);
pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch);
vToFetch[nToFetch - 1] = pindexWalk;
for (unsigned int i = nToFetch - 1; i > 0; i--) {
vToFetch[i - 1] = vToFetch[i]->pprev;
}
// Iterate over those blocks in vToFetch (in forward direction), adding the ones that
// are not yet downloaded and not in flight to vBlocks. In the mean time, update
// pindexLastCommonBlock as long as all ancestors are already downloaded.
BOOST_FOREACH(CBlockIndex* pindex, vToFetch) {
if (!pindex->IsValid(BLOCK_VALID_TREE)) {
// We consider the chain that this peer is on invalid.
return;
}
if (pindex->nStatus & BLOCK_HAVE_DATA) {
if (pindex->nChainTx)
state->pindexLastCommonBlock = pindex;
} else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
// The block is not already downloaded, and not yet in flight.
if (pindex->nHeight > nWindowEnd) {
// We reached the end of the window.
if (vBlocks.size() == 0 && waitingfor != nodeid) {
// We aren't able to fetch anything, but we would be if the download window was one larger.
nodeStaller = waitingfor;
}
return;
}
vBlocks.push_back(pindex);
if (vBlocks.size() == count) {
return;
}
} else if (waitingfor == -1) {
// This is the first already-in-flight block.
waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
}
}
}
}
} // anon namespace
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
LOCK(cs_main);
CNodeState *state = State(nodeid);
if (state == NULL)
return false;
stats.nMisbehavior = state->nMisbehavior;
stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
BOOST_FOREACH(const QueuedBlock& queue, state->vBlocksInFlight) {
if (queue.pindex)
stats.vHeightInFlight.push_back(queue.pindex->nHeight);
}
return true;
}
void RegisterNodeSignals(CNodeSignals& nodeSignals)
{
nodeSignals.GetHeight.connect(&GetHeight);
nodeSignals.ProcessMessages.connect(&ProcessMessages);
nodeSignals.SendMessages.connect(&SendMessages);
nodeSignals.InitializeNode.connect(&InitializeNode);
nodeSignals.FinalizeNode.connect(&FinalizeNode);
}
void UnregisterNodeSignals(CNodeSignals& nodeSignals)
{
nodeSignals.GetHeight.disconnect(&GetHeight);
nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
nodeSignals.SendMessages.disconnect(&SendMessages);
nodeSignals.InitializeNode.disconnect(&InitializeNode);
nodeSignals.FinalizeNode.disconnect(&FinalizeNode);
}
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{
// Find the first block the caller has in the main chain
BOOST_FOREACH(const uint256& hash, locator.vHave) {
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
{
CBlockIndex* pindex = (*mi).second;
if (chain.Contains(pindex))
return pindex;
}
}
return chain.Genesis();
}
CCoinsViewCache *pcoinsTip = NULL;
CBlockTreeDB *pblocktree = NULL;
//////////////////////////////////////////////////////////////////////////////
//
// mapOrphanTransactions
//
bool AddOrphanTx(const CTransaction& tx, NodeId peer)
{
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
// it will rebroadcast it later, after the parent transaction(s)
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz > 5000)
{
LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
return false;
}
mapOrphanTransactions[hash].tx = tx;
mapOrphanTransactions[hash].fromPeer = peer;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(),
mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
return true;
}
void static EraseOrphanTx(uint256 hash)
{
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
if (it == mapOrphanTransactions.end())
return;
BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin)
{
map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash);
if (itPrev == mapOrphanTransactionsByPrev.end())
continue;
itPrev->second.erase(hash);
if (itPrev->second.empty())
mapOrphanTransactionsByPrev.erase(itPrev);
}
mapOrphanTransactions.erase(it);
}
void EraseOrphansFor(NodeId peer)
{
int nErased = 0;
map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
while (iter != mapOrphanTransactions.end())
{
map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
if (maybeErase->second.fromPeer == peer)
{
EraseOrphanTx(maybeErase->second.tx.GetHash());
++nErased;
}
}
if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer);
}
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
{
unsigned int nEvicted = 0;
while (mapOrphanTransactions.size() > nMaxOrphans)
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
++nEvicted;
}
return nEvicted;
}
bool IsStandardTx(const CTransaction& tx, string& reason)
{
AssertLockHeld(cs_main);
if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) {
reason = "version";
return false;
}
// Treat non-final transactions as non-standard to prevent a specific type
// of double-spend attack, as well as DoS attacks. (if the transaction
// can't be mined, the attacker isn't expending resources broadcasting it)
// Basically we don't want to propagate transactions that can't be included in
// the next block.
//
// However, IsFinalTx() is confusing... Without arguments, it uses
// chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height()
// is set to the value of nHeight in the block. However, when IsFinalTx()
// is called within CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a transaction can
// be part of the *next* block, we need to call IsFinalTx() with one more
// than chainActive.Height().
//
// Timestamps on the other hand don't get any special treatment, because we
// can't know what timestamp the next block will have, and there aren't
// timestamp applications where it matters.
if (!IsFinalTx(tx, chainActive.Height() + 1)) {
reason = "non-final";
return false;
}
// Extremely large transactions with lots of inputs can cost the network
// almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions
// to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks.
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz >= MAX_STANDARD_TX_SIZE) {
reason = "tx-size";
return false;
}
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
// keys. (remember the 520 byte limit on redeemScript size) That works
// out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
// bytes of scriptSig, which we round off to 1650 bytes for some minor
// future-proofing. That's also enough to spend a 20-of-20
// CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
// considered standard)
if (txin.scriptSig.size() > 1650) {
reason = "scriptsig-size";
return false;
}
if (!txin.scriptSig.IsPushOnly()) {
reason = "scriptsig-not-pushonly";
return false;
}
}
unsigned int nDataOut = 0;
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
if (!::IsStandard(txout.scriptPubKey, whichType)) {
reason = "scriptpubkey";
return false;
}
if (whichType == TX_NULL_DATA)
nDataOut++;
else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
reason = "bare-multisig";
return false;
} else if (txout.IsDust(::minRelayTxFee)) {
reason = "dust";
return false;
}
}
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
reason = "multi-op-return";
return false;
}
return true;
}
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
AssertLockHeld(cs_main);
// Time based nLockTime implemented in 0.1.6
if (tx.nLockTime == 0)
return true;
if (nBlockHeight == 0)
nBlockHeight = chainActive.Height();
if (nBlockTime == 0)
nBlockTime = GetAdjustedTime();
if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
return true;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
if (!txin.IsFinal())
return false;
return true;
}
/**
* Check transaction inputs to mitigate two
* potential denial-of-service attacks:
*
* 1. scriptSigs with extra data stuffed into them,
* not consumed by scriptPubKey (or P2SH script)
* 2. P2SH scripts with a crazy number of expensive
* CHECKSIG/CHECKMULTISIG operations
*/
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
{
if (tx.IsCoinBase())
return true; // Coinbases don't use vin normally
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
vector<vector<unsigned char> > vSolutions;
txnouttype whichType;
// get the scriptPubKey corresponding to this input:
const CScript& prevScript = prev.scriptPubKey;
if (!Solver(prevScript, whichType, vSolutions))
return false;
int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
if (nArgsExpected < 0)
return false;
// Transactions with extra stuff in their scriptSigs are
// non-standard. Note that this EvalScript() call will
// be quick, because if there are any operations
// beside "push data" in the scriptSig
// IsStandard() will have already returned false
// and this method isn't called.
vector<vector<unsigned char> > stack;
if (!EvalScript(stack, tx.vin[i].scriptSig, false, BaseSignatureChecker()))
return false;
if (whichType == TX_SCRIPTHASH)
{
if (stack.empty())
return false;
CScript subscript(stack.back().begin(), stack.back().end());
vector<vector<unsigned char> > vSolutions2;
txnouttype whichType2;
if (Solver(subscript, whichType2, vSolutions2))
{
int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2);
if (tmpExpected < 0)
return false;
nArgsExpected += tmpExpected;
}
else
{
// Any other Script with less than 15 sigops OK:
unsigned int sigops = subscript.GetSigOpCount(true);
// ... extra data left on the stack after execution is OK, too:
return (sigops <= MAX_P2SH_SIGOPS);
}
}
if (stack.size() != (unsigned int)nArgsExpected)
return false;
}
return true;
}
unsigned int GetLegacySigOpCount(const CTransaction& tx)
{
unsigned int nSigOps = 0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
nSigOps += txin.scriptSig.GetSigOpCount(false);
}
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
}
return nSigOps;
}
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs)
{
if (tx.IsCoinBase())
return 0;
unsigned int nSigOps = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]);
if (prevout.scriptPubKey.IsPayToScriptHash())
nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig);
}
return nSigOps;
}
bool CheckTransaction(const CTransaction& tx, CValidationState &state)
{
// Basic checks that don't depend on any context
if (tx.vin.empty())
return state.DoS(10, error("CheckTransaction() : vin empty"),
REJECT_INVALID, "bad-txns-vin-empty");
if (tx.vout.empty())
return state.DoS(10, error("CheckTransaction() : vout empty"),
REJECT_INVALID, "bad-txns-vout-empty");
// Size limits
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return state.DoS(100, error("CheckTransaction() : size limits failed"),
REJECT_INVALID, "bad-txns-oversize");
// Check for negative or overflow output values
CAmount nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
if (txout.nValue < 0)
return state.DoS(100, error("CheckTransaction() : txout.nValue negative"),
REJECT_INVALID, "bad-txns-vout-negative");
if (txout.nValue > MAX_MONEY)
return state.DoS(100, error("CheckTransaction() : txout.nValue too high"),
REJECT_INVALID, "bad-txns-vout-toolarge");
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.DoS(100, error("CheckTransaction() : txout total out of range"),
REJECT_INVALID, "bad-txns-txouttotal-toolarge");
}
// Check for duplicate inputs
set<COutPoint> vInOutPoints;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
if (vInOutPoints.count(txin.prevout))
return state.DoS(100, error("CheckTransaction() : duplicate inputs"),
REJECT_INVALID, "bad-txns-inputs-duplicate");
vInOutPoints.insert(txin.prevout);
}
if (tx.IsCoinBase())
{
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
return state.DoS(100, error("CheckTransaction() : coinbase script size"),
REJECT_INVALID, "bad-cb-length");
}
else
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
if (txin.prevout.IsNull())
return state.DoS(10, error("CheckTransaction() : prevout is null"),
REJECT_INVALID, "bad-txns-prevout-null");
}
return true;
}
CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
{
{
LOCK(mempool.cs);
uint256 hash = tx.GetHash();
double dPriorityDelta = 0;
CAmount nFeeDelta = 0;
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
if (dPriorityDelta > 0 || nFeeDelta > 0)
return 0;
}
CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes);
if (fAllowFree)
{
// There is a free transaction area in blocks created by most miners,
// * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
// to be considered to fall into this category. We don't want to encourage sending
// multiple transactions instead of one big transaction to avoid fees.
if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))
nMinFee = 0;
}
if (!MoneyRange(nMinFee))
nMinFee = MAX_MONEY;
return nMinFee;
}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectInsaneFee)
{
AssertLockHeld(cs_main);
if (pfMissingInputs)
*pfMissingInputs = false;
if (!CheckTransaction(tx, state))
return error("AcceptToMemoryPool: CheckTransaction failed");
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),
REJECT_INVALID, "coinbase");
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
if (Params().RequireStandard() && !IsStandardTx(tx, reason))
return state.DoS(0,
error("AcceptToMemoryPool : nonstandard transaction: %s", reason),
REJECT_NONSTANDARD, reason);
// is it already in the memory pool?
uint256 hash = tx.GetHash();
if (pool.exists(hash))
return false;
// Check for conflicts with in-memory transactions
{
LOCK(pool.cs); // protect pool.mapNextTx
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
COutPoint outpoint = tx.vin[i].prevout;
if (pool.mapNextTx.count(outpoint))
{
// Disable replacement feature for now
return false;
}
}
}
{
CCoinsView dummy;
CCoinsViewCache view(&dummy);
CAmount nValueIn = 0;
{
LOCK(pool.cs);
CCoinsViewMemPool viewMemPool(pcoinsTip, pool);
view.SetBackend(viewMemPool);
// do we already have it?
if (view.HaveCoins(hash))
return false;
// do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that),
// only helps filling in pfMissingInputs (to determine missing vs spent).
BOOST_FOREACH(const CTxIn txin, tx.vin) {
if (!view.HaveCoins(txin.prevout.hash)) {
if (pfMissingInputs)
*pfMissingInputs = true;
return false;
}
}
// are the actual inputs available?
if (!view.HaveInputs(tx))
return state.Invalid(error("AcceptToMemoryPool : inputs already spent"),
REJECT_DUPLICATE, "bad-txns-inputs-spent");
// Bring the best block into scope
view.GetBestBlock();
nValueIn = view.GetValueIn(tx);
// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
view.SetBackend(dummy);
}
// Check for non-standard pay-to-script-hash in inputs
if (Params().RequireStandard() && !AreInputsStandard(tx, view))
return error("AcceptToMemoryPool: nonstandard transaction input");
// Check that the transaction doesn't have an excessive number of
// sigops, making it impossible to mine. Since the coinbase transaction
// itself can contain sigops MAX_TX_SIGOPS is less than
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
// merely non-standard transaction.
unsigned int nSigOps = GetLegacySigOpCount(tx);
nSigOps += GetP2SHSigOpCount(tx, view);
if (nSigOps > MAX_TX_SIGOPS)
return state.DoS(0,
error("AcceptToMemoryPool : too many sigops %s, %d > %d",
hash.ToString(), nSigOps, MAX_TX_SIGOPS),
REJECT_NONSTANDARD, "bad-txns-too-many-sigops");
CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut;
double dPriority = view.GetPriority(tx, chainActive.Height());
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height());
unsigned int nSize = entry.GetTxSize();
// Don't accept it if it can't get into a block
CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
if (fLimitFree && nFees < txMinFee)
return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d",
hash.ToString(), nFees, txMinFee),
REJECT_INSUFFICIENTFEE, "insufficient fee");
// Continuously rate-limit free (really, very-low-fee) transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
{
static CCriticalSection csFreeLimiter;
static double dFreeCount;
static int64_t nLastTime;
int64_t nNow = GetTime();
LOCK(csFreeLimiter);
// Use an exponentially decaying ~10-minute window:
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"),
REJECT_INSUFFICIENTFEE, "insufficient priority");
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
}
if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000)
return error("AcceptToMemoryPool: insane fees %s, %d > %d",
hash.ToString(),
nFees, ::minRelayTxFee.GetFee(nSize) * 10000);
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true))
{
return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString());
}
// Check again against just the consensus-critical mandatory script
// verification flags, in case of bugs in the standard flags that cause
// transactions to pass as valid when they're actually invalid. For
// instance the STRICTENC flag was incorrectly allowing certain
// CHECKSIG NOT scripts to pass, even though they were invalid.
//
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks, however allowing such transactions into the mempool
// can be exploited as a DoS attack.
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
{
return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString());
}
// Store transaction in memory
pool.addUnchecked(hash, entry);
}
SyncWithWallets(tx, NULL);
return true;
}
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
{
CBlockIndex *pindexSlow = NULL;
{
LOCK(cs_main);
{
if (mempool.lookup(hash, txOut))
{
return true;
}
}
if (fTxIndex) {
CDiskTxPos postx;
if (pblocktree->ReadTxIndex(hash, postx)) {
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
if (file.IsNull())
return error("%s: OpenBlockFile failed", __func__);
CBlockHeader header;
try {
file >> header;
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
file >> txOut;
} catch (const std::exception& e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
hashBlock = header.GetHash();
if (txOut.GetHash() != hash)
return error("%s : txid mismatch", __func__);
return true;
}
}
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
int nHeight = -1;
{
CCoinsViewCache &view = *pcoinsTip;
const CCoins* coins = view.AccessCoins(hash);
if (coins)
nHeight = coins->nHeight;
}
if (nHeight > 0)
pindexSlow = chainActive[nHeight];
}
}
if (pindexSlow) {
CBlock block;
if (ReadBlockFromDisk(block, pindexSlow)) {
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
if (tx.GetHash() == hash) {
txOut = tx;
hashBlock = pindexSlow->GetBlockHash();
return true;
}
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////////
//
// CBlock and CBlockIndex
//
bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos)
{
// Open history file to append
CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("WriteBlockToDisk : OpenBlockFile failed");
// Write index header
unsigned int nSize = fileout.GetSerializeSize(block);
fileout << FLATDATA(Params().MessageStart()) << nSize;
// Write block
long fileOutPos = ftell(fileout.Get());
if (fileOutPos < 0)
return error("WriteBlockToDisk : ftell failed");
pos.nPos = (unsigned int)fileOutPos;
fileout << block;
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
{
block.SetNull();
// Open history file to read
CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("ReadBlockFromDisk : OpenBlockFile failed");
// Read block
try {
filein >> block;
}
catch (const std::exception& e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
// Check the header
if (!CheckProofOfWork(block.GetHash(), block.nBits))
return error("ReadBlockFromDisk : Errors in block header");
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
{
if (!ReadBlockFromDisk(block, pindex->GetBlockPos()))
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index");
return true;
}
CAmount GetBlockValue(int nHeight, const CAmount& nFees)
{
CAmount nSubsidy = 50 * COIN;
int halvings = nHeight / Params().SubsidyHalvingInterval();
// Force block reward to zero when right shift is undefined.
if (halvings >= 64)
return nFees;
// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
nSubsidy >>= halvings;
return nSubsidy + nFees;
}
bool IsInitialBlockDownload()
{
LOCK(cs_main);
if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate())
return true;
static bool lockIBDState = false;
if (lockIBDState)
return false;
bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 ||
pindexBestHeader->GetBlockTime() < GetTime() - 24 * 60 * 60);
if (!state)
lockIBDState = true;
return state;
}
bool fLargeWorkForkFound = false;
bool fLargeWorkInvalidChainFound = false;
CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL;
void CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before the last checkpoint)
if (IsInitialBlockDownload())
return;
// If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it)
// of our head, drop it
if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72)
pindexBestForkTip = NULL;
if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6)))
{
if (!fLargeWorkForkFound && pindexBestForkBase)
{
std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
pindexBestForkBase->phashBlock->ToString() + std::string("'");
CAlert::Notify(warning, true);
}
if (pindexBestForkTip && pindexBestForkBase)
{
LogPrintf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n",
pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(),
pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString());
fLargeWorkForkFound = true;
}
else
{
LogPrintf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n");
fLargeWorkInvalidChainFound = true;
}
}
else
{
fLargeWorkForkFound = false;
fLargeWorkInvalidChainFound = false;
}
}
void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
{
AssertLockHeld(cs_main);
// If we are on a fork that is sufficiently large, set a warning flag
CBlockIndex* pfork = pindexNewForkTip;
CBlockIndex* plonger = chainActive.Tip();
while (pfork && pfork != plonger)
{
while (plonger && plonger->nHeight > pfork->nHeight)
plonger = plonger->pprev;
if (pfork == plonger)
break;
pfork = pfork->pprev;
}
// We define a condition which we should warn the user about as a fork of at least 7 blocks
// who's tip is within 72 blocks (+/- 12 hours if no one mines it) of ours
// We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network
// hash rate operating on the fork.
// or a chain that is entirely longer than ours and invalid (note that this should be detected by both)
// We define it this way because it allows us to only store the highest fork tip (+ base) which meets
// the 7-block condition and from this always have the most-likely-to-cause-warning fork
if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) &&
pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) &&
chainActive.Height() - pindexNewForkTip->nHeight < 72)
{
pindexBestForkTip = pindexNewForkTip;
pindexBestForkBase = pfork;
}
CheckForkWarningConditions();
}
// Requires cs_main.
void Misbehaving(NodeId pnode, int howmuch)
{
if (howmuch == 0)
return;
CNodeState *state = State(pnode);
if (state == NULL)
return;
state->nMisbehavior += howmuch;
int banscore = GetArg("-banscore", 100);
if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
{
LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
state->fShouldBan = true;
} else
LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
}
void static InvalidChainFound(CBlockIndex* pindexNew)
{
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n",
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S",
pindexNew->GetBlockTime()));
LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()));
CheckForkWarningConditions();
}
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
int nDoS = 0;
if (state.IsInvalid(nDoS)) {
std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash());
if (it != mapBlockSource.end() && State(it->second)) {
CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()};
State(it->second)->rejects.push_back(reject);
if (nDoS > 0)
Misbehaving(it->second, nDoS);
}
}
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
}
}
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight)
{
// mark inputs spent
if (!tx.IsCoinBase()) {
txundo.vprevout.reserve(tx.vin.size());
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash);
unsigned nPos = txin.prevout.n;
if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull())
assert(false);
// mark an outpoint spent, and construct undo information
txundo.vprevout.push_back(CTxInUndo(coins->vout[nPos]));
coins->Spend(nPos);
if (coins->vout.size() == 0) {
CTxInUndo& undo = txundo.vprevout.back();
undo.nHeight = coins->nHeight;
undo.fCoinBase = coins->fCoinBase;
undo.nVersion = coins->nVersion;
}
}
}
// add outputs
inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight);
}
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight)
{
CTxUndo txundo;
UpdateCoins(tx, state, inputs, txundo, nHeight);
}
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore), &error)) {
return ::error("CScriptCheck() : %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
}
return true;
}
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks)
{
if (!tx.IsCoinBase())
{
if (pvChecks)
pvChecks->reserve(tx.vin.size());
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network.
if (!inputs.HaveInputs(tx))
return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString()));
// While checking, GetBestBlock() refers to the parent block.
// This is also true for mempool checks.
CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second;
int nSpendHeight = pindexPrev->nHeight + 1;
CAmount nValueIn = 0;
CAmount nFees = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins *coins = inputs.AccessCoins(prevout.hash);
assert(coins);
// If prev is coinbase, check that it's matured
if (coins->IsCoinBase()) {
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY)
return state.Invalid(
error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
}
// Check for negative or overflow input values
nValueIn += coins->vout[prevout.n].nValue;
if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return state.DoS(100, error("CheckInputs() : txin values out of range"),
REJECT_INVALID, "bad-txns-inputvalues-outofrange");
}
if (nValueIn < tx.GetValueOut())
return state.DoS(100, error("CheckInputs() : %s value in (%s) < value out (%s)",
tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())),
REJECT_INVALID, "bad-txns-in-belowout");
// Tally transaction fees
CAmount nTxFee = nValueIn - tx.GetValueOut();
if (nTxFee < 0)
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString()),
REJECT_INVALID, "bad-txns-fee-negative");
nFees += nTxFee;
if (!MoneyRange(nFees))
return state.DoS(100, error("CheckInputs() : nFees out of range"),
REJECT_INVALID, "bad-txns-fee-outofrange");
// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
// Helps prevent CPU exhaustion attacks.
// Skip ECDSA signature verification when connecting blocks
// before the last block chain checkpoint. This is safe because block merkle hashes are
// still computed and checked, and any change will be caught at the next checkpoint.
if (fScriptChecks) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins* coins = inputs.AccessCoins(prevout.hash);
assert(coins);
// Verify signature
CScriptCheck check(*coins, tx, i, flags, cacheStore);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
} else if (!check()) {
if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
// Check whether the failure was caused by a
// non-mandatory script verification check, such as
// non-standard DER encodings or non-null dummy
// arguments; if so, don't trigger DoS protection to
// avoid splitting the network between upgraded and
// non-upgraded nodes.
CScriptCheck check(*coins, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore);
if (check())
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
}
// Failures of other flags indicate a transaction that is
// invalid in new blocks, e.g. a invalid P2SH. We DoS ban
// such nodes as they are not following the protocol. That
// said during an upgrade careful thought should be taken
// as to the correct behavior - we may want to continue
// peering with non-upgraded nodes even after a soft-fork
// super-majority vote has passed.
return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
}
}
}
}
return true;
}
namespace {
bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock)
{
// Open history file to append
CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s : OpenUndoFile failed", __func__);
// Write index header
unsigned int nSize = fileout.GetSerializeSize(blockundo);
fileout << FLATDATA(Params().MessageStart()) << nSize;
// Write undo data
long fileOutPos = ftell(fileout.Get());
if (fileOutPos < 0)
return error("%s : ftell failed", __func__);
pos.nPos = (unsigned int)fileOutPos;
fileout << blockundo;
// calculate & write checksum
CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION);
hasher << hashBlock;
hasher << blockundo;
fileout << hasher.GetHash();
return true;
}
bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock)
{
// Open history file to read
CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("%s : OpenBlockFile failed", __func__);
// Read block
uint256 hashChecksum;
try {
filein >> blockundo;
filein >> hashChecksum;
}
catch (const std::exception& e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
// Verify checksum
CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION);
hasher << hashBlock;
hasher << blockundo;
if (hashChecksum != hasher.GetHash())
return error("%s : Checksum mismatch", __func__);
return true;
}
} // anon namespace
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
{
assert(pindex->GetBlockHash() == view.GetBestBlock());
if (pfClean)
*pfClean = false;
bool fClean = true;
CBlockUndo blockUndo;
CDiskBlockPos pos = pindex->GetUndoPos();
if (pos.IsNull())
return error("DisconnectBlock() : no undo data available");
if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash()))
return error("DisconnectBlock() : failure reading undo data");
if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
return error("DisconnectBlock() : block and undo data inconsistent");
// 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();
// Check that all outputs are available and match the outputs in the block itself
// exactly. Note that transactions with only provably unspendable outputs won't
// have outputs available even in the block itself, so we handle that case
// specially with outsEmpty.
{
CCoins outsEmpty;
CCoinsModifier outs = view.ModifyCoins(hash);
outs->ClearUnspendable();
CCoins outsBlock(tx, pindex->nHeight);
// The CCoins serialization does not serialize negative numbers.
// No network rules currently depend on the version here, so an inconsistency is harmless
// but it must be corrected before txout nversion ever influences a network rule.
if (outsBlock.nVersion < 0)
outs->nVersion = outsBlock.nVersion;
if (*outs != outsBlock)
fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted");
// remove outputs
outs->Clear();
}
// restore inputs
if (i > 0) { // not coinbases
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
if (txundo.vprevout.size() != tx.vin.size())
return error("DisconnectBlock() : transaction and undo data inconsistent");
for (unsigned int j = tx.vin.size(); j-- > 0;) {
const COutPoint &out = tx.vin[j].prevout;
const CTxInUndo &undo = txundo.vprevout[j];
CCoinsModifier coins = view.ModifyCoins(out.hash);
if (undo.nHeight != 0) {
// undo data contains height: this is the last output of the prevout tx being spent
if (!coins->IsPruned())
fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction");
coins->Clear();
coins->fCoinBase = undo.fCoinBase;
coins->nHeight = undo.nHeight;
coins->nVersion = undo.nVersion;
} else {
if (coins->IsPruned())
fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction");
}
if (coins->IsAvailable(out.n))
fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output");
if (coins->vout.size() < out.n+1)
coins->vout.resize(out.n+1);
coins->vout[out.n] = undo.txout;
}
}
}
// move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash());
if (pfClean) {
*pfClean = fClean;
return true;
} else {
return fClean;
}
}
void static FlushBlockFile(bool fFinalize = false)
{
LOCK(cs_LastBlockFile);
CDiskBlockPos posOld(nLastBlockFile, 0);
FILE *fileOld = OpenBlockFile(posOld);
if (fileOld) {
if (fFinalize)
TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
FileCommit(fileOld);
fclose(fileOld);
}
fileOld = OpenUndoFile(posOld);
if (fileOld) {
if (fFinalize)
TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
FileCommit(fileOld);
fclose(fileOld);
}
}
bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void ThreadScriptCheck() {
RenameThread("bitcoin-scriptch");
scriptcheckqueue.Thread();
}
static int64_t nTimeVerify = 0;
static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
{
AssertLockHeld(cs_main);
// Check it again in case a previous version let a bad block in
if (!CheckBlock(block, state, !fJustCheck, !fJustCheck))
return false;
// verify that the view's current state corresponds to the previous block
- uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash();
+ uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash();
assert(hashPrevBlock == view.GetBestBlock());
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
if (block.GetHash() == Params().HashGenesisBlock()) {
if (!fJustCheck)
view.SetBestBlock(pindex->GetBlockHash());
return true;
}
bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate();
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
// can be duplicated to remove the ability to spend the first instance -- even after
// being sent to another address.
// See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information.
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
// already refuses previously-known transaction ids entirely.
// This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC.
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
// two in the chain that violate it. This prevents exploiting the issue against nodes in their
// initial block download.
bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash.
- !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
- (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
+ !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
+ (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
if (fEnforceBIP30) {
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
const CCoins* coins = view.AccessCoins(tx.GetHash());
if (coins && !coins->IsPruned())
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"),
REJECT_INVALID, "bad-txns-BIP30");
}
}
// BIP16 didn't become active until Apr 1 2012
int64_t nBIP16SwitchTime = 1333238400;
bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime);
unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
CBlockUndo blockundo;
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
int64_t nTimeStart = GetTimeMicros();
CAmount nFees = 0;
int nInputs = 0;
unsigned int nSigOps = 0;
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
vPos.reserve(block.vtx.size());
blockundo.vtxundo.reserve(block.vtx.size() - 1);
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const CTransaction &tx = block.vtx[i];
nInputs += tx.vin.size();
nSigOps += GetLegacySigOpCount(tx);
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("ConnectBlock() : too many sigops"),
REJECT_INVALID, "bad-blk-sigops");
if (!tx.IsCoinBase())
{
if (!view.HaveInputs(tx))
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"),
REJECT_INVALID, "bad-txns-inputs-missingorspent");
if (fStrictPayToScriptHash)
{
// 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.
nSigOps += GetP2SHSigOpCount(tx, view);
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("ConnectBlock() : too many sigops"),
REJECT_INVALID, "bad-blk-sigops");
}
nFees += view.GetValueIn(tx)-tx.GetValueOut();
std::vector<CScriptCheck> vChecks;
if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL))
return false;
control.Add(vChecks);
}
CTxUndo undoDummy;
if (i > 0) {
blockundo.vtxundo.push_back(CTxUndo());
}
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
vPos.push_back(std::make_pair(tx.GetHash(), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
return state.DoS(100,
error("ConnectBlock() : coinbase pays too much (actual=%d vs limit=%d)",
block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)),
REJECT_INVALID, "bad-cb-amount");
if (!control.Wait())
return state.DoS(100, false);
int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart;
LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001);
if (fJustCheck)
return true;
// Write undo information to disk
if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS))
{
if (pindex->GetUndoPos().IsNull()) {
CDiskBlockPos pos;
if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
return error("ConnectBlock() : FindUndoPos failed");
if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash()))
return state.Abort("Failed to write undo data");
// update nUndoPos in block index
pindex->nUndoPos = pos.nPos;
pindex->nStatus |= BLOCK_HAVE_UNDO;
}
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
setDirtyBlockIndex.insert(pindex);
}
if (fTxIndex)
if (!pblocktree->WriteTxIndex(vPos))
return state.Abort("Failed to write transaction index");
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2;
LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001);
// Watch for changes to the previous coinbase transaction.
static uint256 hashPrevBestCoinBase;
g_signals.UpdatedTransaction(hashPrevBestCoinBase);
hashPrevBestCoinBase = block.vtx[0].GetHash();
int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3;
LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001);
return true;
}
enum FlushStateMode {
FLUSH_STATE_IF_NEEDED,
FLUSH_STATE_PERIODIC,
FLUSH_STATE_ALWAYS
};
/**
* Update the on-disk chain state.
* The caches and indexes are flushed if either they're too large, forceWrite is set, or
* fast is not set and it's been a while since the last write.
*/
bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
LOCK(cs_main);
static int64_t nLastWrite = 0;
if ((mode == FLUSH_STATE_ALWAYS) ||
((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) ||
(mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000)) {
// Typical CCoins structures on disk are around 100 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error("out of disk space");
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
// Then update all block file information (which may refer to block and undo files).
{
std::vector<std::pair<int, const CBlockFileInfo*> > vFiles;
vFiles.reserve(setDirtyFileInfo.size());
for (set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it]));
setDirtyFileInfo.erase(it++);
}
std::vector<const CBlockIndex*> vBlocks;
vBlocks.reserve(setDirtyBlockIndex.size());
for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) {
vBlocks.push_back(*it);
setDirtyBlockIndex.erase(it++);
}
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
return state.Abort("Files to write to block index database");
}
}
// Finally flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
return state.Abort("Failed to write to coin database");
// Update best block in wallet (so we can detect restored wallets).
if (mode != FLUSH_STATE_IF_NEEDED) {
g_signals.SetBestChain(chainActive.GetLocator());
}
nLastWrite = GetTimeMicros();
}
return true;
}
void FlushStateToDisk() {
CValidationState state;
FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
}
/** Update chainActive and related internal data structures. */
void static UpdateTip(CBlockIndex *pindexNew) {
chainActive.SetTip(pindexNew);
// New best block
nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize());
cvBlockChange.notify_all();
// Check the version of the last 100 blocks to see if we need to upgrade:
static bool fWarned = false;
if (!IsInitialBlockDownload() && !fWarned)
{
int nUpgraded = 0;
const CBlockIndex* pindex = chainActive.Tip();
for (int i = 0; i < 100 && pindex != NULL; i++)
{
if (pindex->nVersion > CBlock::CURRENT_VERSION)
++nUpgraded;
pindex = pindex->pprev;
}
if (nUpgraded > 0)
LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)CBlock::CURRENT_VERSION);
if (nUpgraded > 100/2)
{
// strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
strMiscWarning = _("Warning: This version is obsolete, upgrade required!");
CAlert::Notify(strMiscWarning, true);
fWarned = true;
}
}
}
/** Disconnect chainActive's tip. */
bool static DisconnectTip(CValidationState &state) {
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
mempool.check(pcoinsTip);
// Read block from disk.
CBlock block;
if (!ReadBlockFromDisk(block, pindexDelete))
return state.Abort("Failed to read block");
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
CCoinsViewCache view(pcoinsTip);
if (!DisconnectBlock(block, state, pindexDelete, view))
return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
assert(view.Flush());
}
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
return false;
// Resurrect mempool transactions from the disconnected block.
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
// ignore validation errors in resurrected transactions
list<CTransaction> removed;
CValidationState stateDummy;
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL))
mempool.remove(tx, removed, true);
}
mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight);
mempool.check(pcoinsTip);
// Update chainActive and related variables.
UpdateTip(pindexDelete->pprev);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
SyncWithWallets(tx, NULL);
}
return true;
}
static int64_t nTimeReadFromDisk = 0;
static int64_t nTimeConnectTotal = 0;
static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) {
assert(pindexNew->pprev == chainActive.Tip());
mempool.check(pcoinsTip);
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
CBlock block;
if (!pblock) {
if (!ReadBlockFromDisk(block, pindexNew))
return state.Abort("Failed to read block");
pblock = &block;
}
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
{
CCoinsViewCache view(pcoinsTip);
CInv inv(MSG_BLOCK, pindexNew->GetBlockHash());
bool rv = ConnectBlock(*pblock, state, pindexNew, view);
g_signals.BlockChecked(*pblock, state);
if (!rv) {
if (state.IsInvalid())
InvalidBlockFound(pindexNew, state);
return error("ConnectTip() : ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}
mapBlockSource.erase(inv.hash);
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
assert(view.Flush());
}
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
return false;
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool.
list<CTransaction> txConflicted;
mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted);
mempool.check(pcoinsTip);
// Update chainActive & related variables.
UpdateTip(pindexNew);
// Tell wallet about transactions that went from mempool
// to conflicted:
BOOST_FOREACH(const CTransaction &tx, txConflicted) {
SyncWithWallets(tx, NULL);
}
// ... and about transactions that got confirmed:
BOOST_FOREACH(const CTransaction &tx, pblock->vtx) {
SyncWithWallets(tx, pblock);
}
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
return true;
}
/**
* Return the tip of the chain with the most work in it, that isn't
* known to be invalid (it's however far from certain to be valid).
*/
static CBlockIndex* FindMostWorkChain() {
do {
CBlockIndex *pindexNew = NULL;
// Find the best candidate header.
{
std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexCandidates.rbegin();
if (it == setBlockIndexCandidates.rend())
return NULL;
pindexNew = *it;
}
// Check whether all blocks on the path between the currently active chain and the candidate are valid.
// Just going until the active chain is an optimization, as we know all blocks in it are valid already.
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !chainActive.Contains(pindexTest)) {
assert(pindexTest->nStatus & BLOCK_HAVE_DATA);
assert(pindexTest->nChainTx || pindexTest->nHeight == 0);
if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
// Candidate has an invalid ancestor, remove entire chain from the set.
if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
CBlockIndex *pindexFailed = pindexNew;
while (pindexTest != pindexFailed) {
pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
setBlockIndexCandidates.erase(pindexFailed);
pindexFailed = pindexFailed->pprev;
}
setBlockIndexCandidates.erase(pindexTest);
fInvalidAncestor = true;
break;
}
pindexTest = pindexTest->pprev;
}
if (!fInvalidAncestor)
return pindexNew;
} while(true);
}
/** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */
static void PruneBlockIndexCandidates() {
// Note that we can't delete the current block itself, as we may need to return to it later in case a
// reorganization to a better block fails.
std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin();
while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) {
setBlockIndexCandidates.erase(it++);
}
// Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates.
assert(!setBlockIndexCandidates.empty());
}
/**
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/
static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) {
AssertLockHeld(cs_main);
bool fInvalidFound = false;
const CBlockIndex *pindexOldTip = chainActive.Tip();
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
// Disconnect active blocks which are no longer in the best chain.
while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
if (!DisconnectTip(state))
return false;
}
// Build list of new blocks to connect.
std::vector<CBlockIndex*> vpindexToConnect;
bool fContinue = true;
int nHeight = pindexFork ? pindexFork->nHeight : -1;
while (fContinue && nHeight != pindexMostWork->nHeight) {
// Don't iterate the entire list of potential improvements toward the best tip, as we likely only need
// a few blocks along the way.
int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight);
vpindexToConnect.clear();
vpindexToConnect.reserve(nTargetHeight - nHeight);
CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight);
while (pindexIter && pindexIter->nHeight != nHeight) {
vpindexToConnect.push_back(pindexIter);
pindexIter = pindexIter->pprev;
}
nHeight = nTargetHeight;
// Connect new blocks.
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
if (!state.CorruptionPossible())
InvalidChainFound(vpindexToConnect.back());
state = CValidationState();
fInvalidFound = true;
fContinue = false;
break;
} else {
// A system error occurred (disk space, database error, ...).
return false;
}
} else {
PruneBlockIndexCandidates();
if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
// We're in a better position than we were. Return temporarily to release the lock.
fContinue = false;
break;
}
}
}
}
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
CheckForkWarningConditionsOnNewFork(vpindexToConnect.back());
else
CheckForkWarningConditions();
return true;
}
/**
* Make the best chain active, in multiple steps. The result is either failure
* or an activated best chain. pblock is either NULL or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
CBlockIndex *pindexNewTip = NULL;
CBlockIndex *pindexMostWork = NULL;
do {
boost::this_thread::interruption_point();
bool fInitialDownload;
{
LOCK(cs_main);
pindexMostWork = FindMostWorkChain();
// Whether we have anything to do at all.
if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip())
return true;
if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL))
return false;
pindexNewTip = chainActive.Tip();
fInitialDownload = IsInitialBlockDownload();
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
// Notifications/callbacks that can run without cs_main
if (!fInitialDownload) {
uint256 hashNewTip = pindexNewTip->GetBlockHash();
// Relay inventory, but don't relay old inventory during initial block download.
int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
}
// Notify external listeners about the new tip.
uiInterface.NotifyBlockTip(hashNewTip);
}
} while(pindexMostWork != chainActive.Tip());
// Write changes periodically to disk, after relay.
if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
return false;
}
return true;
}
bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
AssertLockHeld(cs_main);
// Mark the block itself as invalid.
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
while (chainActive.Contains(pindex)) {
CBlockIndex *pindexWalk = chainActive.Tip();
pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindexWalk);
setBlockIndexCandidates.erase(pindexWalk);
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
if (!DisconnectTip(state)) {
return false;
}
}
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add them again.
BlockMap::iterator it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
setBlockIndexCandidates.insert(pindex);
}
it++;
}
InvalidChainFound(pindex);
return true;
}
bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) {
AssertLockHeld(cs_main);
int nHeight = pindex->nHeight;
// Remove the invalidity flag from this block and all its descendants.
BlockMap::iterator it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second);
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
if (it->second == pindexBestInvalid) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = NULL;
}
}
it++;
}
// Remove the invalidity flag from all ancestors too.
while (pindex != NULL) {
if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(pindex);
}
pindex = pindex->pprev;
}
return true;
}
CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
{
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator it = mapBlockIndex.find(hash);
if (it != mapBlockIndex.end())
return it->second;
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(block);
assert(pindexNew);
// We assign the sequence id to blocks only when the full data is available,
// to avoid miners withholding blocks but broadcasting headers, to get a
// competitive advantage.
pindexNew->nSequenceId = 0;
BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
pindexNew->BuildSkip();
}
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
pindexNew->RaiseValidity(BLOCK_VALID_TREE);
if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork)
pindexBestHeader = pindexNew;
setDirtyBlockIndex.insert(pindexNew);
return pindexNew;
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
pindexNew->nFile = pos.nFile;
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus |= BLOCK_HAVE_DATA;
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
{
LOCK(cs_nBlockSequenceId);
pindexNew->nSequenceId = nBlockSequenceId++;
}
setDirtyBlockIndex.insert(pindexNew);
if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
deque<CBlockIndex*> queue;
queue.push_back(pindexNew);
// Recursively process any descendant blocks that now may be eligible to be connected.
while (!queue.empty()) {
CBlockIndex *pindex = queue.front();
queue.pop_front();
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
setBlockIndexCandidates.insert(pindex);
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex);
while (range.first != range.second) {
std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first;
queue.push_back(it->second);
range.first++;
mapBlocksUnlinked.erase(it);
}
}
} else {
if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) {
mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
}
}
return true;
}
bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
}
if (!fKnown) {
while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString());
FlushBlockFile(true);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
}
}
pos.nFile = nFile;
pos.nPos = vinfoBlockFile[nFile].nSize;
}
nLastBlockFile = nFile;
vinfoBlockFile[nFile].nSize += nAddSize;
vinfoBlockFile[nFile].AddBlock(nHeight, nTime);
if (!fKnown) {
unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
if (nNewChunks > nOldChunks) {
if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) {
FILE *file = OpenBlockFile(pos);
if (file) {
LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile);
AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos);
fclose(file);
}
}
else
return state.Error("out of disk space");
}
}
setDirtyFileInfo.insert(nFile);
return true;
}
bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
LOCK(cs_LastBlockFile);
unsigned int nNewSize;
pos.nPos = vinfoBlockFile[nFile].nUndoSize;
nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize;
setDirtyFileInfo.insert(nFile);
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
if (nNewChunks > nOldChunks) {
if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) {
FILE *file = OpenUndoFile(pos);
if (file) {
LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile);
AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos);
fclose(file);
}
}
else
return state.Error("out of disk space");
}
return true;
}
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
return state.DoS(50, error("CheckBlockHeader() : proof of work failed"),
REJECT_INVALID, "high-hash");
// Check timestamp
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return state.Invalid(error("CheckBlockHeader() : block timestamp too far in the future"),
REJECT_INVALID, "time-too-new");
return true;
}
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context.
// Check that the header is valid (particularly PoW). This is mostly
// redundant with the call in AcceptBlockHeader.
if (!CheckBlockHeader(block, state, fCheckPOW))
return false;
// Check the merkle root.
if (fCheckMerkleRoot) {
bool mutated;
uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated);
if (block.hashMerkleRoot != hashMerkleRoot2)
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"),
REJECT_INVALID, "bad-txnmrklroot", true);
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
// of transactions in a block without affecting the merkle root of a block,
// while still invalidating it.
if (mutated)
return state.DoS(100, error("CheckBlock() : duplicate transaction"),
REJECT_INVALID, "bad-txns-duplicate", true);
}
// All potential-corruption validation must be done before we do any
// transaction validation, as otherwise we may mark the header as invalid
// because we receive the wrong transactions for it.
// Size limits
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return state.DoS(100, error("CheckBlock() : size limits failed"),
REJECT_INVALID, "bad-blk-length");
// First transaction must be coinbase, the rest must not be
if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
return state.DoS(100, error("CheckBlock() : first tx is not coinbase"),
REJECT_INVALID, "bad-cb-missing");
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i].IsCoinBase())
return state.DoS(100, error("CheckBlock() : more than one coinbase"),
REJECT_INVALID, "bad-cb-multiple");
// Check transactions
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!CheckTransaction(tx, state))
return error("CheckBlock() : CheckTransaction failed");
unsigned int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, block.vtx)
{
nSigOps += GetLegacySigOpCount(tx);
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"),
REJECT_INVALID, "bad-blk-sigops", true);
return true;
}
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev)
{
uint256 hash = block.GetHash();
if (hash == Params().HashGenesisBlock())
return true;
assert(pindexPrev);
int nHeight = pindexPrev->nHeight+1;
// Check proof of work
if ((!Params().SkipProofOfWorkCheck()) &&
(block.nBits != GetNextWorkRequired(pindexPrev, &block)))
return state.DoS(100, error("%s : incorrect proof of work", __func__),
REJECT_INVALID, "bad-diffbits");
// Check timestamp against prev
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
return state.Invalid(error("%s : block's timestamp is too early", __func__),
REJECT_INVALID, "time-too-old");
// Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckBlock(nHeight, hash))
return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight),
REJECT_CHECKPOINT, "checkpoint mismatch");
// Don't accept any forks from the main chain prior to last checkpoint
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint();
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight));
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority()))
{
return state.Invalid(error("%s : rejected nVersion=1 block", __func__),
REJECT_OBSOLETE, "bad-version");
}
return true;
}
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) {
return state.DoS(10, error("%s : contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal");
}
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority()))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) {
return state.DoS(100, error("%s : block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height");
}
}
return true;
}
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator miSelf = mapBlockIndex.find(hash);
CBlockIndex *pindex = NULL;
if (miSelf != mapBlockIndex.end()) {
// Block header is already known.
pindex = miSelf->second;
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK)
return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate");
return true;
}
if (!CheckBlockHeader(block, state))
return false;
// Get prev block index
CBlockIndex* pindexPrev = NULL;
if (hash != Params().HashGenesisBlock()) {
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk");
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
return state.DoS(100, error("%s : prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
}
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
return false;
if (pindex == NULL)
pindex = AddToBlockIndex(block);
if (ppindex)
*ppindex = pindex;
return true;
}
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp)
{
AssertLockHeld(cs_main);
CBlockIndex *&pindex = *ppindex;
if (!AcceptBlockHeader(block, state, &pindex))
return false;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
// TODO: deal better with duplicate blocks.
// return state.DoS(20, error("AcceptBlock() : already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate");
return true;
}
if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
}
return false;
}
int nHeight = pindex->nHeight;
// Write block to history file
try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
if (dbp != NULL)
blockPos = *dbp;
if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL))
return error("AcceptBlock() : FindBlockPos failed");
if (dbp == NULL)
if (!WriteBlockToDisk(block, blockPos))
return state.Abort("Failed to write block");
if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("AcceptBlock() : ReceivedBlockTransactions failed");
} catch (const std::runtime_error& e) {
return state.Abort(std::string("System error: ") + e.what());
}
return true;
}
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired)
{
unsigned int nToCheck = Params().ToCheckBlockUpgradeMajority();
unsigned int nFound = 0;
for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++)
{
if (pstart->nVersion >= minVersion)
++nFound;
pstart = pstart->pprev;
}
return (nFound >= nRequired);
}
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
{
// Preliminary checks
bool checked = CheckBlock(*pblock, state);
{
LOCK(cs_main);
MarkBlockAsReceived(pblock->GetHash());
if (!checked) {
return error("%s : CheckBlock FAILED", __func__);
}
// Store to disk
CBlockIndex *pindex = NULL;
bool ret = AcceptBlock(*pblock, state, &pindex, dbp);
if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
}
if (!ret)
return error("%s : AcceptBlock FAILED", __func__);
}
if (!ActivateBestChain(state, pblock))
return error("%s : ActivateBestChain failed", __func__);
return true;
}
bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
assert(pindexPrev == chainActive.Tip());
CCoinsViewCache viewNew(pcoinsTip);
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
return false;
if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot))
return false;
if (!ContextualCheckBlock(block, state, pindexPrev))
return false;
if (!ConnectBlock(block, state, &indexDummy, viewNew, true))
return false;
assert(state.IsValid());
return true;
}
bool AbortNode(const std::string &strMessage, const std::string &userMessage) {
strMiscWarning = strMessage;
LogPrintf("*** %s\n", strMessage);
uiInterface.ThreadSafeMessageBox(
userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage,
"", CClientUIInterface::MSG_ERROR);
StartShutdown();
return false;
}
bool CheckDiskSpace(uint64_t nAdditionalBytes)
{
uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available;
// Check for nMinDiskSpace bytes (currently 50MB)
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
return AbortNode("Disk space is low!", _("Error: Disk space is low!"));
return true;
}
FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
if (pos.IsNull())
return NULL;
boost::filesystem::path path = GetBlockPosFilename(pos, prefix);
boost::filesystem::create_directories(path.parent_path());
FILE* file = fopen(path.string().c_str(), "rb+");
if (!file && !fReadOnly)
file = fopen(path.string().c_str(), "wb+");
if (!file) {
LogPrintf("Unable to open file %s\n", path.string());
return NULL;
}
if (pos.nPos) {
if (fseek(file, pos.nPos, SEEK_SET)) {
LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string());
fclose(file);
return NULL;
}
}
return file;
}
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "blk", fReadOnly);
}
FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "rev", fReadOnly);
}
boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
{
return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
}
CBlockIndex * InsertBlockIndex(uint256 hash)
{
- if (hash == 0)
+ if (hash.IsNull())
return NULL;
// Return existing
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
return (*mi).second;
// Create new
CBlockIndex* pindexNew = new CBlockIndex();
if (!pindexNew)
throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
return pindexNew;
}
bool static LoadBlockIndexDB()
{
if (!pblocktree->LoadBlockIndexGuts())
return false;
boost::this_thread::interruption_point();
// Calculate nChainWork
vector<pair<int, CBlockIndex*> > vSortedByHeight;
vSortedByHeight.reserve(mapBlockIndex.size());
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
{
CBlockIndex* pindex = item.second;
vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());
BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
{
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
if (pindex->nStatus & BLOCK_HAVE_DATA) {
if (pindex->pprev) {
if (pindex->pprev->nChainTx) {
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
} else {
pindex->nChainTx = 0;
mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex));
}
} else {
pindex->nChainTx = pindex->nTx;
}
}
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL))
setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
pindexBestInvalid = pindex;
if (pindex->pprev)
pindex->BuildSkip();
if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
pindexBestHeader = pindex;
}
// Load block file info
pblocktree->ReadLastBlockFile(nLastBlockFile);
vinfoBlockFile.resize(nLastBlockFile + 1);
LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile);
for (int nFile = 0; nFile <= nLastBlockFile; nFile++) {
pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
}
LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString());
for (int nFile = nLastBlockFile + 1; true; nFile++) {
CBlockFileInfo info;
if (pblocktree->ReadBlockFileInfo(nFile, info)) {
vinfoBlockFile.push_back(info);
} else {
break;
}
}
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
set<int> setBlkDataFiles;
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
{
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
}
}
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++)
{
CDiskBlockPos pos(*it, 0);
if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) {
return false;
}
}
// Check whether we need to continue reindexing
bool fReindexing = false;
pblocktree->ReadReindexing(fReindexing);
fReindex |= fReindexing;
// Check whether we have a transaction index
pblocktree->ReadFlag("txindex", fTxIndex);
LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled");
// Load pointer to end of best chain
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
if (it == mapBlockIndex.end())
return true;
chainActive.SetTip(it->second);
PruneBlockIndexCandidates();
LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
Checkpoints::GuessVerificationProgress(chainActive.Tip()));
return true;
}
CVerifyDB::CVerifyDB()
{
uiInterface.ShowProgress(_("Verifying blocks..."), 0);
}
CVerifyDB::~CVerifyDB()
{
uiInterface.ShowProgress("", 100);
}
bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
LOCK(cs_main);
if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL)
return true;
// Verify blocks in the best chain
if (nCheckDepth <= 0)
nCheckDepth = 1000000000; // suffices until the year 19000
if (nCheckDepth > chainActive.Height())
nCheckDepth = chainActive.Height();
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
CBlockIndex* pindexState = chainActive.Tip();
CBlockIndex* pindexFailure = NULL;
int nGoodTransactions = 0;
CValidationState state;
for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
{
boost::this_thread::interruption_point();
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))));
if (pindex->nHeight < chainActive.Height()-nCheckDepth)
break;
CBlock block;
// check level 0: read from disk
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state))
return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
CDiskBlockPos pos = pindex->GetUndoPos();
if (!pos.IsNull()) {
if (!UndoReadFromDisk(undo, pos, pindex->pprev->GetBlockHash()))
return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) {
bool fClean = true;
if (!DisconnectBlock(block, state, pindex, coins, &fClean))
return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
pindexState = pindex->pprev;
if (!fClean) {
nGoodTransactions = 0;
pindexFailure = pindex;
} else
nGoodTransactions += block.vtx.size();
}
if (ShutdownRequested())
return true;
}
if (pindexFailure)
return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
CBlockIndex *pindex = pindexState;
while (pindex != chainActive.Tip()) {
boost::this_thread::interruption_point();
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))));
pindex = chainActive.Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!ConnectBlock(block, state, pindex, coins))
return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
}
LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions);
return true;
}
void UnloadBlockIndex()
{
mapBlockIndex.clear();
setBlockIndexCandidates.clear();
chainActive.SetTip(NULL);
pindexBestInvalid = NULL;
}
bool LoadBlockIndex()
{
// Load block index from databases
if (!fReindex && !LoadBlockIndexDB())
return false;
return true;
}
bool InitBlockIndex() {
LOCK(cs_main);
// Check whether we're already initialized
if (chainActive.Genesis() != NULL)
return true;
// Use the provided setting for -txindex in the new database
fTxIndex = GetBoolArg("-txindex", false);
pblocktree->WriteFlag("txindex", fTxIndex);
LogPrintf("Initializing databases...\n");
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
if (!fReindex) {
try {
CBlock &block = const_cast<CBlock&>(Params().GenesisBlock());
// Start new block file
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
CValidationState state;
if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime()))
return error("LoadBlockIndex() : FindBlockPos failed");
if (!WriteBlockToDisk(block, blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
CBlockIndex *pindex = AddToBlockIndex(block);
if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("LoadBlockIndex() : genesis block not accepted");
if (!ActivateBestChain(state, &block))
return error("LoadBlockIndex() : genesis block cannot be activated");
// Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
} catch (const std::runtime_error& e) {
return error("LoadBlockIndex() : failed to initialize block database: %s", e.what());
}
}
return true;
}
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent;
int64_t nStart = GetTimeMillis();
int nLoaded = 0;
try {
// This takes over fileIn and calls fclose() on it in the CBufferedFile destructor
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION);
uint64_t nRewind = blkdat.GetPos();
while (!blkdat.eof()) {
boost::this_thread::interruption_point();
blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure
blkdat.SetLimit(); // remove former limit
unsigned int nSize = 0;
try {
// locate a header
unsigned char buf[MESSAGE_START_SIZE];
blkdat.FindByte(Params().MessageStart()[0]);
nRewind = blkdat.GetPos()+1;
blkdat >> FLATDATA(buf);
if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE))
continue;
// read size
blkdat >> nSize;
if (nSize < 80 || nSize > MAX_BLOCK_SIZE)
continue;
} catch (const std::exception&) {
// no valid block header found; don't complain
break;
}
try {
// read block
uint64_t nBlockPos = blkdat.GetPos();
if (dbp)
dbp->nPos = nBlockPos;
blkdat.SetLimit(nBlockPos + nSize);
blkdat.SetPos(nBlockPos);
CBlock block;
blkdat >> block;
nRewind = blkdat.GetPos();
// detect out of order blocks, and store them for later
uint256 hash = block.GetHash();
if (hash != Params().HashGenesisBlock() && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) {
LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
block.hashPrevBlock.ToString());
if (dbp)
mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp));
continue;
}
// process in case the block isn't known yet
if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) {
CValidationState state;
if (ProcessNewBlock(state, NULL, &block, dbp))
nLoaded++;
if (state.IsError())
break;
} else if (hash != Params().HashGenesisBlock() && mapBlockIndex[hash]->nHeight % 1000 == 0) {
LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight);
}
// Recursively process earlier encountered successors of this block
deque<uint256> queue;
queue.push_back(hash);
while (!queue.empty()) {
uint256 head = queue.front();
queue.pop_front();
std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
while (range.first != range.second) {
std::multimap<uint256, CDiskBlockPos>::iterator it = range.first;
if (ReadBlockFromDisk(block, it->second))
{
LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(),
head.ToString());
CValidationState dummy;
if (ProcessNewBlock(dummy, NULL, &block, &it->second))
{
nLoaded++;
queue.push_back(block.GetHash());
}
}
range.first++;
mapBlocksUnknownParent.erase(it);
}
}
} catch (const std::exception& e) {
LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what());
}
}
} catch (const std::runtime_error& e) {
AbortNode(std::string("System error: ") + e.what());
}
if (nLoaded > 0)
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart);
return nLoaded > 0;
}
//////////////////////////////////////////////////////////////////////////////
//
// CAlert
//
string GetWarnings(string strFor)
{
int nPriority = 0;
string strStatusBar;
string strRPC;
if (!CLIENT_VERSION_IS_RELEASE)
strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
if (GetBoolArg("-testsafemode", false))
strStatusBar = strRPC = "testsafemode enabled";
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
nPriority = 1000;
strStatusBar = strMiscWarning;
}
if (fLargeWorkForkFound)
{
nPriority = 2000;
strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
}
else if (fLargeWorkInvalidChainFound)
{
nPriority = 2000;
strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
// Alerts
{
LOCK(cs_mapAlerts);
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
{
const CAlert& alert = item.second;
if (alert.AppliesToMe() && alert.nPriority > nPriority)
{
nPriority = alert.nPriority;
strStatusBar = alert.strStatusBar;
}
}
}
if (strFor == "statusbar")
return strStatusBar;
else if (strFor == "rpc")
return strRPC;
assert(!"GetWarnings() : invalid parameter");
return "error";
}
//////////////////////////////////////////////////////////////////////////////
//
// Messages
//
bool static AlreadyHave(const CInv& inv)
{
switch (inv.type)
{
case MSG_TX:
{
bool txInMap = false;
txInMap = mempool.exists(inv.hash);
return txInMap || mapOrphanTransactions.count(inv.hash) ||
pcoinsTip->HaveCoins(inv.hash);
}
case MSG_BLOCK:
return mapBlockIndex.count(inv.hash);
}
// Don't know what it is, just say we already got one
return true;
}
void static ProcessGetData(CNode* pfrom)
{
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
vector<CInv> vNotFound;
LOCK(cs_main);
while (it != pfrom->vRecvGetData.end()) {
// Don't bother if send buffer is too full to respond anyway
if (pfrom->nSendSize >= SendBufferSize())
break;
const CInv &inv = *it;
{
boost::this_thread::interruption_point();
it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
{
bool send = false;
BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
if (mi != mapBlockIndex.end())
{
// If the requested block is at a height below our last
// checkpoint, only serve it if it's in the checkpointed chain
int nHeight = mi->second->nHeight;
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint();
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
if (!chainActive.Contains(mi->second))
{
LogPrintf("ProcessGetData(): ignoring request for old block that isn't in the main chain\n");
} else {
send = true;
}
} else {
send = true;
}
}
if (send)
{
// Send block from disk
CBlock block;
if (!ReadBlockFromDisk(block, (*mi).second))
assert(!"cannot load block from disk");
if (inv.type == MSG_BLOCK)
pfrom->PushMessage("block", block);
else // MSG_FILTERED_BLOCK)
{
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
{
CMerkleBlock merkleBlock(block, *pfrom->pfilter);
pfrom->PushMessage("merkleblock", merkleBlock);
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
// Note that there is currently no way for a node to request any single transactions we didnt send here -
// they must either disconnect and retry or request the full block.
// Thus, the protocol spec specified allows for us to provide duplicate txn here,
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second)))
pfrom->PushMessage("tx", block.vtx[pair.first]);
}
// else
// no response
}
// Trigger them to send a getblocks request for the next batch of inventory
if (inv.hash == pfrom->hashContinue)
{
// Bypass PushInventory, this must send even if redundant,
// and we want it right after the last block so they don't
// wait for other stuff first.
vector<CInv> vInv;
vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
pfrom->PushMessage("inv", vInv);
- pfrom->hashContinue = 0;
+ pfrom->hashContinue.SetNull();
}
}
}
else if (inv.IsKnownType())
{
// Send stream from relay memory
bool pushed = false;
{
LOCK(cs_mapRelay);
map<CInv, CDataStream>::iterator mi = mapRelay.find(inv);
if (mi != mapRelay.end()) {
pfrom->PushMessage(inv.GetCommand(), (*mi).second);
pushed = true;
}
}
if (!pushed && inv.type == MSG_TX) {
CTransaction tx;
if (mempool.lookup(inv.hash, tx)) {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << tx;
pfrom->PushMessage("tx", ss);
pushed = true;
}
}
if (!pushed) {
vNotFound.push_back(inv);
}
}
// Track requests for our stuff.
g_signals.Inventory(inv.hash);
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
break;
}
}
pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
// have to wait around forever. Currently only SPV clients actually care
// about this message: it's needed when they are recursively walking the
// dependencies of relevant unconfirmed transactions. SPV clients want to
// do that because they want to know about (and store and rebroadcast and
// risk analyze) the dependencies of transactions relevant to them, without
// having to download the entire memory pool.
pfrom->PushMessage("notfound", vNotFound);
}
}
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived)
{
RandAddSeedPerfmon();
LogPrint("net", "received: %s (%u bytes) peer=%d\n", strCommand, vRecv.size(), pfrom->id);
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
return true;
}
if (strCommand == "version")
{
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
Misbehaving(pfrom->GetId(), 1);
return false;
}
int64_t nTime;
CAddress addrMe;
CAddress addrFrom;
uint64_t nNonce = 1;
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
return false;
}
if (pfrom->nVersion == 10300)
pfrom->nVersion = 300;
if (!vRecv.empty())
vRecv >> addrFrom >> nNonce;
if (!vRecv.empty()) {
vRecv >> LIMITED_STRING(pfrom->strSubVer, 256);
pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer);
}
if (!vRecv.empty())
vRecv >> pfrom->nStartingHeight;
if (!vRecv.empty())
vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
else
pfrom->fRelayTxes = true;
// Disconnect if we connected to ourself
if (nNonce == nLocalHostNonce && nNonce > 1)
{
LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
pfrom->fDisconnect = true;
return true;
}
pfrom->addrLocal = addrMe;
if (pfrom->fInbound && addrMe.IsRoutable())
{
SeenLocal(addrMe);
}
// Be shy and don't send version until we hear
if (pfrom->fInbound)
pfrom->PushVersion();
pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
// Potentially mark this peer as a preferred download peer.
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
// Change version
pfrom->PushMessage("verack");
pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
if (!pfrom->fInbound)
{
// Advertise our address
if (fListen && !IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom->addr);
if (addr.IsRoutable())
{
pfrom->PushAddress(addr);
} else if (IsPeerAddrLocalGood(pfrom)) {
addr.SetIP(pfrom->addrLocal);
pfrom->PushAddress(addr);
}
}
// Get recent addresses
if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000)
{
pfrom->PushMessage("getaddr");
pfrom->fGetAddr = true;
}
addrman.Good(pfrom->addr);
} else {
if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom)
{
addrman.Add(addrFrom, addrFrom);
addrman.Good(addrFrom);
}
}
// Relay alerts
{
LOCK(cs_mapAlerts);
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
item.second.RelayTo(pfrom);
}
pfrom->fSuccessfullyConnected = true;
string remoteAddr;
if (fLogIPs)
remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
pfrom->cleanSubVer, pfrom->nVersion,
pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
remoteAddr);
int64_t nTimeOffset = nTime - GetTime();
pfrom->nTimeOffset = nTimeOffset;
AddTimeData(pfrom->addr, nTimeOffset);
}
else if (pfrom->nVersion == 0)
{
// Must have a version message before anything else
Misbehaving(pfrom->GetId(), 1);
return false;
}
else if (strCommand == "verack")
{
pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
}
else if (strCommand == "addr")
{
vector<CAddress> vAddr;
vRecv >> vAddr;
// Don't want addr from older versions unless seeding
if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000)
return true;
if (vAddr.size() > 1000)
{
Misbehaving(pfrom->GetId(), 20);
return error("message addr size() = %u", vAddr.size());
}
// Store the new addresses
vector<CAddress> vAddrOk;
int64_t nNow = GetAdjustedTime();
int64_t nSince = nNow - 10 * 60;
BOOST_FOREACH(CAddress& addr, vAddr)
{
boost::this_thread::interruption_point();
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr);
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
// Relay to a limited number of other nodes
{
LOCK(cs_vNodes);
// Use deterministic randomness to send to the same nodes for 24 hours
// at a time so the setAddrKnowns of the chosen nodes prevent repeats
static uint256 hashSalt;
- if (hashSalt == 0)
+ if (hashSalt.IsNull())
hashSalt = GetRandHash();
uint64_t hashAddr = addr.GetHash();
- uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60));
+ uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)));
hashRand = Hash(BEGIN(hashRand), END(hashRand));
multimap<uint256, CNode*> mapMix;
BOOST_FOREACH(CNode* pnode, vNodes)
{
if (pnode->nVersion < CADDR_TIME_VERSION)
continue;
unsigned int nPointer;
memcpy(&nPointer, &pnode, sizeof(nPointer));
- uint256 hashKey = hashRand ^ nPointer;
+ uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer);
hashKey = Hash(BEGIN(hashKey), END(hashKey));
mapMix.insert(make_pair(hashKey, pnode));
}
int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
((*mi).second)->PushAddress(addr);
}
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
pfrom->fGetAddr = false;
if (pfrom->fOneShot)
pfrom->fDisconnect = true;
}
else if (strCommand == "inv")
{
vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
Misbehaving(pfrom->GetId(), 20);
return error("message inv size() = %u", vInv.size());
}
LOCK(cs_main);
std::vector<CInv> vToFetch;
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
{
const CInv &inv = vInv[nInv];
boost::this_thread::interruption_point();
pfrom->AddInventoryKnown(inv);
bool fAlreadyHave = AlreadyHave(inv);
LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK)
pfrom->AskFor(inv);
if (inv.type == MSG_BLOCK) {
UpdateBlockAvailability(pfrom->GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
// First request the headers preceeding the announced block. In the normal fully-synced
// case where a new block is announced that succeeds the current tip (no reorganization),
// there are no such headers.
// Secondly, and only when we are close to being synced, we request the announced block directly,
// to avoid an extra round-trip. Note that we must *first* ask for the headers, so by the
// time the block arrives, the header chain leading up to it is already validated. Not
// doing this will result in the received block being rejected as an orphan in case it is
// not a direct successor.
pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash);
CNodeState *nodestate = State(pfrom->GetId());
if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - Params().TargetSpacing() * 20 &&
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
vToFetch.push_back(inv);
// Mark block as in flight already, even though the actual "getdata" message only goes out
// later (within the same cs_main lock, though).
MarkBlockAsInFlight(pfrom->GetId(), inv.hash);
}
LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
}
}
// Track requests for our stuff
g_signals.Inventory(inv.hash);
if (pfrom->nSendSize > (SendBufferSize() * 2)) {
Misbehaving(pfrom->GetId(), 50);
return error("send buffer size() = %u", pfrom->nSendSize);
}
}
if (!vToFetch.empty())
pfrom->PushMessage("getdata", vToFetch);
}
else if (strCommand == "getdata")
{
vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
Misbehaving(pfrom->GetId(), 20);
return error("message getdata size() = %u", vInv.size());
}
if (fDebug || (vInv.size() != 1))
LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom);
}
else if (strCommand == "getblocks")
{
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
LOCK(cs_main);
// Find the last block the caller has in the main chain
CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator);
// Send the rest of the chain
if (pindex)
pindex = chainActive.Next(pindex);
int nLimit = 500;
- LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id);
+ LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id);
for (; pindex; pindex = chainActive.Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
{
LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
}
pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
if (--nLimit <= 0)
{
// When this block is requested, we'll send an inv that'll make them
// getblocks the next batch of inventory.
LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
pfrom->hashContinue = pindex->GetBlockHash();
break;
}
}
}
else if (strCommand == "getheaders")
{
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
LOCK(cs_main);
CBlockIndex* pindex = NULL;
if (locator.IsNull())
{
// If locator is null, return the hashStop block
BlockMap::iterator mi = mapBlockIndex.find(hashStop);
if (mi == mapBlockIndex.end())
return true;
pindex = (*mi).second;
}
else
{
// Find the last block the caller has in the main chain
pindex = FindForkInGlobalIndex(chainActive, locator);
if (pindex)
pindex = chainActive.Next(pindex);
}
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id);
for (; pindex; pindex = chainActive.Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
pfrom->PushMessage("headers", vHeaders);
}
else if (strCommand == "tx")
{
vector<uint256> vWorkQueue;
vector<uint256> vEraseQueue;
CTransaction tx;
vRecv >> tx;
CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);
LOCK(cs_main);
bool fMissingInputs = false;
CValidationState state;
mapAlreadyAskedFor.erase(inv);
if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
{
mempool.check(pcoinsTip);
RelayTransaction(tx);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n",
pfrom->id, pfrom->cleanSubVer,
tx.GetHash().ToString(),
mempool.mapTx.size());
// Recursively process any orphan transactions that depended on this one
set<NodeId> setMisbehaving;
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]);
if (itByPrev == mapOrphanTransactionsByPrev.end())
continue;
for (set<uint256>::iterator mi = itByPrev->second.begin();
mi != itByPrev->second.end();
++mi)
{
const uint256& orphanHash = *mi;
const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx;
NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer;
bool fMissingInputs2 = false;
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
// anyone relaying LegitTxX banned)
CValidationState stateDummy;
vEraseQueue.push_back(orphanHash);
if (setMisbehaving.count(fromPeer))
continue;
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2))
{
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
RelayTransaction(orphanTx);
vWorkQueue.push_back(orphanHash);
}
else if (!fMissingInputs2)
{
int nDos = 0;
if (stateDummy.IsInvalid(nDos) && nDos > 0)
{
// Punish peer that gave us an invalid orphan tx
Misbehaving(fromPeer, nDos);
setMisbehaving.insert(fromPeer);
LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString());
}
// too-little-fee orphan
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
}
mempool.check(pcoinsTip);
}
}
BOOST_FOREACH(uint256 hash, vEraseQueue)
EraseOrphanTx(hash);
}
else if (fMissingInputs)
{
AddOrphanTx(tx, pfrom->GetId());
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
} else if (pfrom->fWhitelisted) {
// Always relay transactions received from whitelisted peers, even
// if they are already in the mempool (allowing the node to function
// as a gateway for nodes hidden behind it).
RelayTransaction(tx);
}
int nDoS = 0;
if (state.IsInvalid(nDoS))
{
LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(),
pfrom->id, pfrom->cleanSubVer,
state.GetRejectReason());
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
}
}
else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing
{
std::vector<CBlockHeader> headers;
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
Misbehaving(pfrom->GetId(), 20);
return error("headers message size = %u", nCount);
}
headers.resize(nCount);
for (unsigned int n = 0; n < nCount; n++) {
vRecv >> headers[n];
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
LOCK(cs_main);
if (nCount == 0) {
// Nothing interesting. Stop asking this peers for more headers.
return true;
}
CBlockIndex *pindexLast = NULL;
BOOST_FOREACH(const CBlockHeader& header, headers) {
CValidationState state;
if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) {
Misbehaving(pfrom->GetId(), 20);
return error("non-continuous headers sequence");
}
if (!AcceptBlockHeader(header, state, &pindexLast)) {
int nDoS;
if (state.IsInvalid(nDoS)) {
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
return error("invalid header received");
}
}
}
if (pindexLast)
UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
if (nCount == MAX_HEADERS_RESULTS && pindexLast) {
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
// from there instead.
LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
- pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256(0));
+ pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256());
}
}
else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing
{
CBlock block;
vRecv >> block;
CInv inv(MSG_BLOCK, block.GetHash());
LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id);
pfrom->AddInventoryKnown(inv);
CValidationState state;
ProcessNewBlock(state, pfrom, &block);
int nDoS;
if (state.IsInvalid(nDoS)) {
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), nDoS);
}
}
}
else if (strCommand == "getaddr")
{
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
BOOST_FOREACH(const CAddress &addr, vAddr)
pfrom->PushAddress(addr);
}
else if (strCommand == "mempool")
{
LOCK2(cs_main, pfrom->cs_filter);
std::vector<uint256> vtxid;
mempool.queryHashes(vtxid);
vector<CInv> vInv;
BOOST_FOREACH(uint256& hash, vtxid) {
CInv inv(MSG_TX, hash);
CTransaction tx;
bool fInMemPool = mempool.lookup(hash, tx);
if (!fInMemPool) continue; // another thread removed since queryHashes, maybe...
if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) ||
(!pfrom->pfilter))
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
pfrom->PushMessage("inv", vInv);
vInv.clear();
}
}
if (vInv.size() > 0)
pfrom->PushMessage("inv", vInv);
}
else if (strCommand == "ping")
{
if (pfrom->nVersion > BIP0031_VERSION)
{
uint64_t nonce = 0;
vRecv >> nonce;
// Echo the message back with the nonce. This allows for two useful features:
//
// 1) A remote node can quickly check if the connection is operational
// 2) Remote nodes can measure the latency of the network thread. If this node
// is overloaded it won't respond to pings quickly and the remote node can
// avoid sending us more work, like chain download requests.
//
// The nonce stops the remote getting confused between different pings: without
// it, if the remote node sends a ping once per second and this node takes 5
// seconds to respond to each, the 5th ping the remote sends would appear to
// return very quickly.
pfrom->PushMessage("pong", nonce);
}
}
else if (strCommand == "pong")
{
int64_t pingUsecEnd = nTimeReceived;
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
bool bPingFinished = false;
std::string sProblem;
if (nAvail >= sizeof(nonce)) {
vRecv >> nonce;
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
if (pfrom->nPingNonceSent != 0) {
if (nonce == pfrom->nPingNonceSent) {
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
if (pingUsecTime > 0) {
// Successful ping time measurement, replace previous
pfrom->nPingUsecTime = pingUsecTime;
} else {
// This should never happen
sProblem = "Timing mishap";
}
} else {
// Nonce mismatches are normal when pings are overlapping
sProblem = "Nonce mismatch";
if (nonce == 0) {
// This is most likely a bug in another implementation somewhere, cancel this ping
bPingFinished = true;
sProblem = "Nonce zero";
}
}
} else {
sProblem = "Unsolicited pong without ping";
}
} else {
// This is most likely a bug in another implementation somewhere, cancel this ping
bPingFinished = true;
sProblem = "Short payload";
}
if (!(sProblem.empty())) {
LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n",
pfrom->id,
pfrom->cleanSubVer,
sProblem,
pfrom->nPingNonceSent,
nonce,
nAvail);
}
if (bPingFinished) {
pfrom->nPingNonceSent = 0;
}
}
else if (strCommand == "alert")
{
CAlert alert;
vRecv >> alert;
uint256 alertHash = alert.GetHash();
if (pfrom->setKnown.count(alertHash) == 0)
{
if (alert.ProcessAlert())
{
// Relay
pfrom->setKnown.insert(alertHash);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
alert.RelayTo(pnode);
}
}
else {
// Small DoS penalty so peers that send us lots of
// duplicate/expired/invalid-signature/whatever alerts
// eventually get banned.
// This isn't a Misbehaving(100) (immediate ban) because the
// peer might be an older or different implementation with
// a different signature key, etc.
Misbehaving(pfrom->GetId(), 10);
}
}
}
else if (strCommand == "filterload")
{
CBloomFilter filter;
vRecv >> filter;
if (!filter.IsWithinSizeConstraints())
// There is no excuse for sending a too-large filter
Misbehaving(pfrom->GetId(), 100);
else
{
LOCK(pfrom->cs_filter);
delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter(filter);
pfrom->pfilter->UpdateEmptyFull();
}
pfrom->fRelayTxes = true;
}
else if (strCommand == "filteradd")
{
vector<unsigned char> vData;
vRecv >> vData;
// Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
// and thus, the maximum size any matched object can have) in a filteradd message
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE)
{
Misbehaving(pfrom->GetId(), 100);
} else {
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
pfrom->pfilter->insert(vData);
else
Misbehaving(pfrom->GetId(), 100);
}
}
else if (strCommand == "filterclear")
{
LOCK(pfrom->cs_filter);
delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter();
pfrom->fRelayTxes = true;
}
else if (strCommand == "reject")
{
if (fDebug) {
try {
string strMsg; unsigned char ccode; string strReason;
vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH);
ostringstream ss;
ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
if (strMsg == "block" || strMsg == "tx")
{
uint256 hash;
vRecv >> hash;
ss << ": hash " << hash.ToString();
}
LogPrint("net", "Reject %s\n", SanitizeString(ss.str()));
} catch (const std::ios_base::failure&) {
// Avoid feedback loops by preventing reject messages from triggering a new reject message.
LogPrint("net", "Unparseable reject message received\n");
}
}
}
else
{
// Ignore unknown commands for extensibility
LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
}
// Update the last seen time for this node's address
if (pfrom->fNetworkNode)
if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping")
AddressCurrentlyConnected(pfrom->addr);
return true;
}
// requires LOCK(cs_vRecvMsg)
bool ProcessMessages(CNode* pfrom)
{
//if (fDebug)
// LogPrintf("ProcessMessages(%u messages)\n", pfrom->vRecvMsg.size());
//
// Message format
// (4) message start
// (12) command
// (4) size
// (4) checksum
// (x) data
//
bool fOk = true;
if (!pfrom->vRecvGetData.empty())
ProcessGetData(pfrom);
// this maintains the order of responses
if (!pfrom->vRecvGetData.empty()) return fOk;
std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin();
while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) {
// Don't bother if send buffer is too full to respond anyway
if (pfrom->nSendSize >= SendBufferSize())
break;
// get next message
CNetMessage& msg = *it;
//if (fDebug)
// LogPrintf("ProcessMessages(message %u msgsz, %u bytes, complete:%s)\n",
// msg.hdr.nMessageSize, msg.vRecv.size(),
// msg.complete() ? "Y" : "N");
// end, if an incomplete message is found
if (!msg.complete())
break;
// at this point, any failure means we can delete the current message
it++;
// Scan for message start
if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) {
LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", msg.hdr.GetCommand(), pfrom->id);
fOk = false;
break;
}
// Read header
CMessageHeader& hdr = msg.hdr;
if (!hdr.IsValid())
{
LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", hdr.GetCommand(), pfrom->id);
continue;
}
string strCommand = hdr.GetCommand();
// Message size
unsigned int nMessageSize = hdr.nMessageSize;
// Checksum
CDataStream& vRecv = msg.vRecv;
uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
unsigned int nChecksum = 0;
memcpy(&nChecksum, &hash, sizeof(nChecksum));
if (nChecksum != hdr.nChecksum)
{
LogPrintf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n",
strCommand, nMessageSize, nChecksum, hdr.nChecksum);
continue;
}
// Process message
bool fRet = false;
try
{
fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime);
boost::this_thread::interruption_point();
}
catch (const std::ios_base::failure& e)
{
pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message"));
if (strstr(e.what(), "end of data"))
{
// Allow exceptions from under-length message on vRecv
LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand, nMessageSize, e.what());
}
else if (strstr(e.what(), "size too large"))
{
// Allow exceptions from over-long size
LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand, nMessageSize, e.what());
}
else
{
PrintExceptionContinue(&e, "ProcessMessages()");
}
}
catch (const boost::thread_interrupted&) {
throw;
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "ProcessMessages()");
} catch (...) {
PrintExceptionContinue(NULL, "ProcessMessages()");
}
if (!fRet)
LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", strCommand, nMessageSize, pfrom->id);
break;
}
// In case the connection got shut down, its receive buffer was wiped
if (!pfrom->fDisconnect)
pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it);
return fOk;
}
bool SendMessages(CNode* pto, bool fSendTrickle)
{
{
// Don't send anything until we get their version message
if (pto->nVersion == 0)
return true;
//
// Message: ping
//
bool pingSend = false;
if (pto->fPingQueued) {
// RPC ping request by user
pingSend = true;
}
if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
// Ping automatically sent as a latency probe & keepalive.
pingSend = true;
}
if (pingSend) {
uint64_t nonce = 0;
while (nonce == 0) {
GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
}
pto->fPingQueued = false;
pto->nPingUsecStart = GetTimeMicros();
if (pto->nVersion > BIP0031_VERSION) {
pto->nPingNonceSent = nonce;
pto->PushMessage("ping", nonce);
} else {
// Peer is too old to support ping command with nonce, pong will never arrive.
pto->nPingNonceSent = 0;
pto->PushMessage("ping");
}
}
TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
if (!lockMain)
return true;
// Address refresh broadcast
static int64_t nLastRebroadcast;
if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
{
// Periodically clear setAddrKnown to allow refresh broadcasts
if (nLastRebroadcast)
pnode->setAddrKnown.clear();
// Rebroadcast our address
AdvertizeLocal(pnode);
}
if (!vNodes.empty())
nLastRebroadcast = GetTime();
}
//
// Message: addr
//
if (fSendTrickle)
{
vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
{
// returns true if wasn't already contained in the set
if (pto->setAddrKnown.insert(addr).second)
{
vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000)
{
pto->PushMessage("addr", vAddr);
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
pto->PushMessage("addr", vAddr);
}
CNodeState &state = *State(pto->GetId());
if (state.fShouldBan) {
if (pto->fWhitelisted)
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
else {
pto->fDisconnect = true;
if (pto->addr.IsLocal())
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
else
{
CNode::Ban(pto->addr);
}
}
state.fShouldBan = false;
}
BOOST_FOREACH(const CBlockReject& reject, state.rejects)
pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock);
state.rejects.clear();
// Start block sync
if (pindexBestHeader == NULL)
pindexBestHeader = chainActive.Tip();
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && fFetch && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
if (nSyncStarted == 0 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
nSyncStarted++;
CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader;
LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
- pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256(0));
+ pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256());
}
}
// Resend wallet transactions that haven't gotten in a block yet
// Except during reindex, importing and IBD, when old wallet
// transactions become unconfirmed and spams other nodes.
if (!fReindex && !fImporting && !IsInitialBlockDownload())
{
g_signals.Broadcast();
}
//
// Message: inventory
//
vector<CInv> vInv;
vector<CInv> vInvWait;
{
LOCK(pto->cs_inventory);
vInv.reserve(pto->vInventoryToSend.size());
vInvWait.reserve(pto->vInventoryToSend.size());
BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend)
{
if (pto->setInventoryKnown.count(inv))
continue;
// trickle out tx inv to protect privacy
if (inv.type == MSG_TX && !fSendTrickle)
{
// 1/4 of tx invs blast to all immediately
static uint256 hashSalt;
- if (hashSalt == 0)
+ if (hashSalt.IsNull())
hashSalt = GetRandHash();
- uint256 hashRand = inv.hash ^ hashSalt;
+ uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt));
hashRand = Hash(BEGIN(hashRand), END(hashRand));
- bool fTrickleWait = ((hashRand & 3) != 0);
+ bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0);
if (fTrickleWait)
{
vInvWait.push_back(inv);
continue;
}
}
// returns true if wasn't already contained in the set
if (pto->setInventoryKnown.insert(inv).second)
{
vInv.push_back(inv);
if (vInv.size() >= 1000)
{
pto->PushMessage("inv", vInv);
vInv.clear();
}
}
}
pto->vInventoryToSend = vInvWait;
}
if (!vInv.empty())
pto->PushMessage("inv", vInv);
// Detect whether we're stalling
int64_t nNow = GetTimeMicros();
if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
// should only happen during initial block download.
LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id);
pto->fDisconnect = true;
}
//
// Message: getdata (blocks)
//
vector<CInv> vGetData;
if (!pto->fDisconnect && !pto->fClient && fFetch && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
vector<CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash()));
MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex);
LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->id);
}
if (state.nBlocksInFlight == 0 && staller != -1) {
if (State(staller)->nStallingSince == 0) {
State(staller)->nStallingSince = nNow;
LogPrint("net", "Stall started peer=%d\n", staller);
}
}
}
//
// Message: getdata (non-blocks)
//
while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
if (!AlreadyHave(inv))
{
if (fDebug)
LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id);
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
pto->PushMessage("getdata", vGetData);
vGetData.clear();
}
}
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vGetData.empty())
pto->PushMessage("getdata", vGetData);
}
return true;
}
std::string CBlockFileInfo::ToString() const {
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
}
class CMainCleanup
{
public:
CMainCleanup() {}
~CMainCleanup() {
// block headers
BlockMap::iterator it1 = mapBlockIndex.begin();
for (; it1 != mapBlockIndex.end(); it1++)
delete (*it1).second;
mapBlockIndex.clear();
// orphan transactions
mapOrphanTransactions.clear();
mapOrphanTransactionsByPrev.clear();
}
} instance_of_cmaincleanup;
diff --git a/src/main.h b/src/main.h
index 9049f5bb7..1e0011476 100644
--- a/src/main.h
+++ b/src/main.h
@@ -1,542 +1,542 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_MAIN_H
#define BITCOIN_MAIN_H
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "amount.h"
#include "chain.h"
#include "chainparams.h"
#include "coins.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "net.h"
#include "pow.h"
#include "script/script.h"
#include "script/sigcache.h"
#include "script/standard.h"
#include "sync.h"
#include "tinyformat.h"
#include "txmempool.h"
#include "uint256.h"
#include <algorithm>
#include <exception>
#include <map>
#include <set>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include <boost/unordered_map.hpp>
class CBlockIndex;
class CBlockTreeDB;
class CBloomFilter;
class CInv;
class CScriptCheck;
class CValidationInterface;
class CValidationState;
struct CBlockTemplate;
struct CNodeStateStats;
/** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0;
/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/
static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000;
/** The maximum size for transactions we're willing to relay/mine */
static const unsigned int MAX_STANDARD_TX_SIZE = 100000;
/** The maximum allowed number of signature check operations in a block (network rule) */
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
/** Maximum number of signature check operations in an IsStandard() P2SH script */
static const unsigned int MAX_P2SH_SIGOPS = 15;
/** The maximum number of sigops we're willing to relay/mine in a single tx */
static const unsigned int MAX_TX_SIGOPS = MAX_BLOCK_SIGOPS/5;
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
/** The maximum size of a blk?????.dat file (since 0.8) */
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
static const int COINBASE_MATURITY = 100;
/** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
/** Maximum number of script-checking threads allowed */
static const int MAX_SCRIPTCHECK_THREADS = 16;
/** -par default (number of script-checking threads, 0 = auto) */
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
* less than this number, we reached their tip. Changing this value is a protocol upgrade. */
static const unsigned int MAX_HEADERS_RESULTS = 2000;
/** Size of the "block download window": how far ahead of our current height do we fetch?
* Larger windows tolerate larger download speed differences between peer, but increase the potential
* degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
* harder). We'll probably want to make this a per-peer adaptive value at some point. */
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
/** Time to wait (in seconds) between writing blockchain state to disk. */
static const unsigned int DATABASE_WRITE_INTERVAL = 3600;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
/** "reject" message codes */
static const unsigned char REJECT_MALFORMED = 0x01;
static const unsigned char REJECT_INVALID = 0x10;
static const unsigned char REJECT_OBSOLETE = 0x11;
static const unsigned char REJECT_DUPLICATE = 0x12;
static const unsigned char REJECT_NONSTANDARD = 0x40;
static const unsigned char REJECT_DUST = 0x41;
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
static const unsigned char REJECT_CHECKPOINT = 0x43;
struct BlockHasher
{
- size_t operator()(const uint256& hash) const { return hash.GetLow64(); }
+ size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
};
extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern CTxMemPool mempool;
typedef boost::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap mapBlockIndex;
extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
extern const std::string strMessageMagic;
extern int64_t nTimeBestReceived;
extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange;
extern bool fImporting;
extern bool fReindex;
extern int nScriptCheckThreads;
extern bool fTxIndex;
extern bool fIsBareMultisigStd;
extern unsigned int nCoinCacheSize;
extern CFeeRate minRelayTxFee;
/** Best header we've seen so far (used for getheaders queries' starting points). */
extern CBlockIndex *pindexBestHeader;
/** Minimum disk space required - used in CheckDiskSpace() */
static const uint64_t nMinDiskSpace = 52428800;
/** Register a wallet to receive updates from core */
void RegisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister a wallet from core */
void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */
void UnregisterAllValidationInterfaces();
/** Push an updated transaction to all registered wallets */
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL);
/** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals);
/** Unregister a network node */
void UnregisterNodeSignals(CNodeSignals& nodeSignals);
/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
* specific block passed to it has been checked for validity!
*
* @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface - this will have its BlockChecked method called whenever *any* block completes validation.
* @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid.
* @param[in] pblock The block we want to process.
* @param[out] dbp If pblock is stored to disk (or already there), this will be set to its location.
* @return True if state.IsValid()
*/
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL);
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Open an undo file (rev?????.dat) */
FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
/** Import blocks from an external file */
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL);
/** Initialize a new block tree database + block data on disk */
bool InitBlockIndex();
/** Load the block tree and coins database from disk */
bool LoadBlockIndex();
/** Unload database information */
void UnloadBlockIndex();
/** Process protocol messages received from a given node */
bool ProcessMessages(CNode* pfrom);
/** Send queued protocol messages to be sent to a give node */
bool SendMessages(CNode* pto, bool fSendTrickle);
/** Run an instance of the script checking thread */
void ThreadScriptCheck();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload();
/** Format a string that describes several potential problems detected by the core */
std::string GetWarnings(std::string strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL);
CAmount GetBlockValue(int nHeight, const CAmount& nFees);
/** Create a new block index entry for a given block hash */
CBlockIndex * InsertBlockIndex(uint256 hash);
/** Abort with a message */
bool AbortNode(const std::string &msg, const std::string &userMessage="");
/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
/** Increase a node's misbehavior score. */
void Misbehaving(NodeId nodeid, int howmuch);
/** Flush all state, indexes and buffers to disk. */
void FlushStateToDisk();
/** (try to) add transaction to memory pool **/
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectInsaneFee=false);
struct CNodeStateStats {
int nMisbehavior;
int nSyncHeight;
int nCommonHeight;
std::vector<int> vHeightInFlight;
};
struct CDiskTxPos : public CDiskBlockPos
{
unsigned int nTxOffset; // after header
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*(CDiskBlockPos*)this);
READWRITE(VARINT(nTxOffset));
}
CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
}
CDiskTxPos() {
SetNull();
}
void SetNull() {
CDiskBlockPos::SetNull();
nTxOffset = 0;
}
};
CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
/**
* Check transaction inputs, and make sure any
* pay-to-script-hash transactions are evaluating IsStandard scripts
*
* Why bother? To avoid denial-of-service attacks; an attacker
* can submit a standard HASH... OP_EQUAL transaction,
* which will get accepted into blocks. The redemption
* script can be anything; an attacker could use a very
* expensive-to-check-upon-redemption script like:
* DUP CHECKSIG DROP ... repeated 100 times... OP_1
*/
/**
* Check for standard transaction types
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
* @return True if all inputs (scriptSigs) use only standard transaction forms
*/
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
/**
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
* @return number of sigops this transaction's outputs will produce when spent
* @see CTransaction::FetchInputs
*/
unsigned int GetLegacySigOpCount(const CTransaction& tx);
/**
* Count ECDSA signature operations in pay-to-script-hash inputs.
*
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
* @return maximum number of sigops required to validate this transaction's inputs
* @see CTransaction::FetchInputs
*/
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& mapInputs);
/**
* Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
* This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
* instead of being performed inline.
*/
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks = NULL);
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight);
/** Context-independent validity checks */
bool CheckTransaction(const CTransaction& tx, CValidationState& state);
/** Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
bool IsStandardTx(const CTransaction& tx, std::string& reason);
bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0);
/**
* Closure representing one script verification
* Note that this stores references to the spending transaction
*/
class CScriptCheck
{
private:
CScript scriptPubKey;
const CTransaction *ptxTo;
unsigned int nIn;
unsigned int nFlags;
bool cacheStore;
ScriptError error;
public:
CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) :
scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey),
ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { }
bool operator()();
void swap(CScriptCheck &check) {
scriptPubKey.swap(check.scriptPubKey);
std::swap(ptxTo, check.ptxTo);
std::swap(nIn, check.nIn);
std::swap(nFlags, check.nFlags);
std::swap(cacheStore, check.cacheStore);
std::swap(error, check.error);
}
ScriptError GetScriptError() const { return error; }
};
/** Functions for disk access for blocks */
bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
/** Functions for validating blocks and updating the block tree */
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
* In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
* will be true if no problems were found. Otherwise, the return value will be false in case
* of problems. Note that in any case, coins may be modified. */
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
/** Apply the effects of this block (with given index) on the UTXO set represented by coins */
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
/** Context-independent validity checks */
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
/** Context-dependent validity checks */
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev);
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev);
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
/** Store block on disk. If dbp is provided, the file is known to already reside on disk */
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL);
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL);
class CBlockFileInfo
{
public:
unsigned int nBlocks; //! number of blocks stored in file
unsigned int nSize; //! number of used bytes of block file
unsigned int nUndoSize; //! number of used bytes in the undo file
unsigned int nHeightFirst; //! lowest height of block in file
unsigned int nHeightLast; //! highest height of block in file
uint64_t nTimeFirst; //! earliest time of block in file
uint64_t nTimeLast; //! latest time of block in file
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(VARINT(nBlocks));
READWRITE(VARINT(nSize));
READWRITE(VARINT(nUndoSize));
READWRITE(VARINT(nHeightFirst));
READWRITE(VARINT(nHeightLast));
READWRITE(VARINT(nTimeFirst));
READWRITE(VARINT(nTimeLast));
}
void SetNull() {
nBlocks = 0;
nSize = 0;
nUndoSize = 0;
nHeightFirst = 0;
nHeightLast = 0;
nTimeFirst = 0;
nTimeLast = 0;
}
CBlockFileInfo() {
SetNull();
}
std::string ToString() const;
/** update statistics (does not update nSize) */
void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn) {
if (nBlocks==0 || nHeightFirst > nHeightIn)
nHeightFirst = nHeightIn;
if (nBlocks==0 || nTimeFirst > nTimeIn)
nTimeFirst = nTimeIn;
nBlocks++;
if (nHeightIn > nHeightLast)
nHeightLast = nHeightIn;
if (nTimeIn > nTimeLast)
nTimeLast = nTimeIn;
}
};
/** Capture information about block/transaction validation */
class CValidationState {
private:
enum mode_state {
MODE_VALID, //! everything ok
MODE_INVALID, //! network rule violation (DoS value may be set)
MODE_ERROR, //! run-time error
} mode;
int nDoS;
std::string strRejectReason;
unsigned char chRejectCode;
bool corruptionPossible;
public:
CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
bool DoS(int level, bool ret = false,
unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="",
bool corruptionIn=false) {
chRejectCode = chRejectCodeIn;
strRejectReason = strRejectReasonIn;
corruptionPossible = corruptionIn;
if (mode == MODE_ERROR)
return ret;
nDoS += level;
mode = MODE_INVALID;
return ret;
}
bool Invalid(bool ret = false,
unsigned char _chRejectCode=0, std::string _strRejectReason="") {
return DoS(0, ret, _chRejectCode, _strRejectReason);
}
bool Error(std::string strRejectReasonIn="") {
if (mode == MODE_VALID)
strRejectReason = strRejectReasonIn;
mode = MODE_ERROR;
return false;
}
bool Abort(const std::string &msg) {
AbortNode(msg);
return Error(msg);
}
bool IsValid() const {
return mode == MODE_VALID;
}
bool IsInvalid() const {
return mode == MODE_INVALID;
}
bool IsError() const {
return mode == MODE_ERROR;
}
bool IsInvalid(int &nDoSOut) const {
if (IsInvalid()) {
nDoSOut = nDoS;
return true;
}
return false;
}
bool CorruptionPossible() const {
return corruptionPossible;
}
unsigned char GetRejectCode() const { return chRejectCode; }
std::string GetRejectReason() const { return strRejectReason; }
};
/** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
class CVerifyDB {
public:
CVerifyDB();
~CVerifyDB();
bool VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth);
};
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
/** Mark a block as invalid. */
bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex);
/** Remove invalidity status from a block and its descendants. */
bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex);
/** The currently-connected chain of blocks. */
extern CChain chainActive;
/** Global variable that points to the active CCoinsView (protected by cs_main) */
extern CCoinsViewCache *pcoinsTip;
/** Global variable that points to the active block tree (protected by cs_main) */
extern CBlockTreeDB *pblocktree;
struct CBlockTemplate
{
CBlock block;
std::vector<CAmount> vTxFees;
std::vector<int64_t> vTxSigOps;
};
class CValidationInterface {
protected:
virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {};
virtual void EraseFromWallet(const uint256 &hash) {};
virtual void SetBestChain(const CBlockLocator &locator) {};
virtual void UpdatedTransaction(const uint256 &hash) {};
virtual void Inventory(const uint256 &hash) {};
virtual void ResendWalletTransactions() {};
virtual void BlockChecked(const CBlock&, const CValidationState&) {};
friend void ::RegisterValidationInterface(CValidationInterface*);
friend void ::UnregisterValidationInterface(CValidationInterface*);
friend void ::UnregisterAllValidationInterfaces();
};
#endif // BITCOIN_MAIN_H
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index f9006d46d..b51b002b9 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -1,158 +1,158 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "merkleblock.h"
#include "hash.h"
#include "primitives/block.h" // for MAX_BLOCK_SIZE
#include "utilstrencodings.h"
using namespace std;
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
{
header = block.GetBlockHeader();
vector<bool> vMatch;
vector<uint256> vHashes;
vMatch.reserve(block.vtx.size());
vHashes.reserve(block.vtx.size());
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const uint256& hash = block.vtx[i].GetHash();
if (filter.IsRelevantAndUpdate(block.vtx[i]))
{
vMatch.push_back(true);
vMatchedTxn.push_back(make_pair(i, hash));
}
else
vMatch.push_back(false);
vHashes.push_back(hash);
}
txn = CPartialMerkleTree(vHashes, vMatch);
}
uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid) {
if (height == 0) {
// hash at height 0 is the txids themself
return vTxid[pos];
} else {
// calculate left hash
uint256 left = CalcHash(height-1, pos*2, vTxid), right;
// calculate right hash if not beyond the end of the array - copy left hash otherwise1
if (pos*2+1 < CalcTreeWidth(height-1))
right = CalcHash(height-1, pos*2+1, vTxid);
else
right = left;
// combine subhashes
return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
}
}
void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) {
// determine whether this node is the parent of at least one matched txid
bool fParentOfMatch = false;
for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++)
fParentOfMatch |= vMatch[p];
// store as flag bit
vBits.push_back(fParentOfMatch);
if (height==0 || !fParentOfMatch) {
// if at height 0, or nothing interesting below, store hash and stop
vHash.push_back(CalcHash(height, pos, vTxid));
} else {
// otherwise, don't store any hash, but descend into the subtrees
TraverseAndBuild(height-1, pos*2, vTxid, vMatch);
if (pos*2+1 < CalcTreeWidth(height-1))
TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch);
}
}
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch) {
if (nBitsUsed >= vBits.size()) {
// overflowed the bits array - failure
fBad = true;
- return 0;
+ return uint256();
}
bool fParentOfMatch = vBits[nBitsUsed++];
if (height==0 || !fParentOfMatch) {
// if at height 0, or nothing interesting below, use stored hash and do not descend
if (nHashUsed >= vHash.size()) {
// overflowed the hash array - failure
fBad = true;
- return 0;
+ return uint256();
}
const uint256 &hash = vHash[nHashUsed++];
if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid
vMatch.push_back(hash);
return hash;
} else {
// otherwise, descend into the subtrees to extract matched txids and hashes
uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right;
if (pos*2+1 < CalcTreeWidth(height-1)) {
right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch);
if (right == left) {
// If the left and right branch should never be identical as the transaction
// hashes covered by them must be unique.
fBad = true;
}
} else {
right = left;
}
// and combine them before returning
return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
}
}
CPartialMerkleTree::CPartialMerkleTree(const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) : nTransactions(vTxid.size()), fBad(false) {
// reset state
vBits.clear();
vHash.clear();
// calculate height of tree
int nHeight = 0;
while (CalcTreeWidth(nHeight) > 1)
nHeight++;
// traverse the partial tree
TraverseAndBuild(nHeight, 0, vTxid, vMatch);
}
CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {}
uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
vMatch.clear();
// An empty set will not work
if (nTransactions == 0)
- return 0;
+ return uint256();
// check for excessively high numbers of transactions
if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction
- return 0;
+ return uint256();
// there can never be more hashes provided than one for every txid
if (vHash.size() > nTransactions)
- return 0;
+ return uint256();
// there must be at least one bit per node in the partial tree, and at least one node per hash
if (vBits.size() < vHash.size())
- return 0;
+ return uint256();
// calculate height of tree
int nHeight = 0;
while (CalcTreeWidth(nHeight) > 1)
nHeight++;
// traverse the partial tree
unsigned int nBitsUsed = 0, nHashUsed = 0;
uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch);
// verify that no problems occured during the tree traversal
if (fBad)
- return 0;
+ return uint256();
// verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence)
if ((nBitsUsed+7)/8 != (vBits.size()+7)/8)
- return 0;
+ return uint256();
// verify that all hashes were consumed
if (nHashUsed != vHash.size())
- return 0;
+ return uint256();
return hashMerkleRoot;
}
diff --git a/src/miner.cpp b/src/miner.cpp
index 3bd2a9a41..87cb15833 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -1,601 +1,601 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "miner.h"
#include "amount.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "hash.h"
#include "main.h"
#include "net.h"
#include "pow.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
#ifdef ENABLE_WALLET
#include "wallet.h"
#endif
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
using namespace std;
//////////////////////////////////////////////////////////////////////////////
//
// BitcoinMiner
//
//
// Unconfirmed transactions in the memory pool often depend on other
// transactions in the memory pool. When we select transactions from the
// pool, we select by highest priority or fee rate, so we might consider
// transactions that depend on transactions that aren't yet in the block.
// The COrphan class keeps track of these 'temporary orphans' while
// CreateBlock is figuring out which transactions to include.
//
class COrphan
{
public:
const CTransaction* ptx;
set<uint256> setDependsOn;
CFeeRate feeRate;
double dPriority;
COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
{
}
};
uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;
// We want to sort transactions by priority and fee rate, so:
typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
class TxPriorityCompare
{
bool byFee;
public:
TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
bool operator()(const TxPriority& a, const TxPriority& b)
{
if (byFee)
{
if (a.get<1>() == b.get<1>())
return a.get<0>() < b.get<0>();
return a.get<1>() < b.get<1>();
}
else
{
if (a.get<0>() == b.get<0>())
return a.get<1>() < b.get<1>();
return a.get<0>() < b.get<0>();
}
}
};
void UpdateTime(CBlockHeader* pblock, const CBlockIndex* pindexPrev)
{
pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
// Updating time can change work required on testnet:
if (Params().AllowMinDifficultyBlocks())
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
}
CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{
// Create new block
auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
if(!pblocktemplate.get())
return NULL;
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (Params().MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
// Create coinbase tx
CMutableTransaction txNew;
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
// Add dummy coinbase tx as first transaction
pblock->vtx.push_back(CTransaction());
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOps.push_back(-1); // updated at end
// Largest block you're willing to create:
unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
// Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
// How much of the block should be dedicated to high-priority transactions,
// included regardless of the fees they pay
unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
// Minimum block size you want to create; block will be filled with free transactions
// until there are no more or the block reaches this size:
unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
// Collect memory pool transactions into the block
CAmount nFees = 0;
{
LOCK2(cs_main, mempool.cs);
CBlockIndex* pindexPrev = chainActive.Tip();
const int nHeight = pindexPrev->nHeight + 1;
CCoinsViewCache view(pcoinsTip);
// Priority order to process transactions
list<COrphan> vOrphan; // list memory doesn't move
map<uint256, vector<COrphan*> > mapDependers;
bool fPrintPriority = GetBoolArg("-printpriority", false);
// This vector will be sorted into a priority queue:
vector<TxPriority> vecPriority;
vecPriority.reserve(mempool.mapTx.size());
for (map<uint256, CTxMemPoolEntry>::iterator mi = mempool.mapTx.begin();
mi != mempool.mapTx.end(); ++mi)
{
const CTransaction& tx = mi->second.GetTx();
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight))
continue;
COrphan* porphan = NULL;
double dPriority = 0;
CAmount nTotalIn = 0;
bool fMissingInputs = false;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Read prev transaction
if (!view.HaveCoins(txin.prevout.hash))
{
// This should never happen; all transactions in the memory
// pool should connect to either transactions in the chain
// or other transactions in the memory pool.
if (!mempool.mapTx.count(txin.prevout.hash))
{
LogPrintf("ERROR: mempool transaction missing input\n");
if (fDebug) assert("mempool transaction missing input" == 0);
fMissingInputs = true;
if (porphan)
vOrphan.pop_back();
break;
}
// Has to wait for dependencies
if (!porphan)
{
// Use list for automatic deletion
vOrphan.push_back(COrphan(&tx));
porphan = &vOrphan.back();
}
mapDependers[txin.prevout.hash].push_back(porphan);
porphan->setDependsOn.insert(txin.prevout.hash);
nTotalIn += mempool.mapTx[txin.prevout.hash].GetTx().vout[txin.prevout.n].nValue;
continue;
}
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
assert(coins);
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
nTotalIn += nValueIn;
int nConf = nHeight - coins->nHeight;
dPriority += (double)nValueIn * nConf;
}
if (fMissingInputs) continue;
// Priority is sum(valuein * age) / modified_txsize
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
dPriority = tx.ComputePriority(dPriority, nTxSize);
uint256 hash = tx.GetHash();
mempool.ApplyDeltas(hash, dPriority, nTotalIn);
CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
if (porphan)
{
porphan->dPriority = dPriority;
porphan->feeRate = feeRate;
}
else
vecPriority.push_back(TxPriority(dPriority, feeRate, &mi->second.GetTx()));
}
// Collect transactions into block
uint64_t nBlockSize = 1000;
uint64_t nBlockTx = 0;
int nBlockSigOps = 100;
bool fSortedByFee = (nBlockPrioritySize <= 0);
TxPriorityCompare comparer(fSortedByFee);
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
while (!vecPriority.empty())
{
// Take highest priority transaction off the priority queue:
double dPriority = vecPriority.front().get<0>();
CFeeRate feeRate = vecPriority.front().get<1>();
const CTransaction& tx = *(vecPriority.front().get<2>());
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
vecPriority.pop_back();
// Size limits
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
if (nBlockSize + nTxSize >= nBlockMaxSize)
continue;
// Legacy limits on sigOps:
unsigned int nTxSigOps = GetLegacySigOpCount(tx);
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
// Skip free transactions if we're past the minimum block size:
const uint256& hash = tx.GetHash();
double dPriorityDelta = 0;
CAmount nFeeDelta = 0;
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
continue;
// Prioritise by fee once past the priority size or we run out of high-priority
// transactions:
if (!fSortedByFee &&
((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
{
fSortedByFee = true;
comparer = TxPriorityCompare(fSortedByFee);
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
}
if (!view.HaveInputs(tx))
continue;
CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut();
nTxSigOps += GetP2SHSigOpCount(tx, view);
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
// Note that flags: we don't want to set mempool/IsStandard()
// policy here, but we still have to ensure that the block we
// create only contains transactions that are valid in new blocks.
CValidationState state;
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
continue;
UpdateCoins(tx, state, view, nHeight);
// Added
pblock->vtx.push_back(tx);
pblocktemplate->vTxFees.push_back(nTxFees);
pblocktemplate->vTxSigOps.push_back(nTxSigOps);
nBlockSize += nTxSize;
++nBlockTx;
nBlockSigOps += nTxSigOps;
nFees += nTxFees;
if (fPrintPriority)
{
LogPrintf("priority %.1f fee %s txid %s\n",
dPriority, feeRate.ToString(), tx.GetHash().ToString());
}
// Add transactions that depend on this one to the priority queue
if (mapDependers.count(hash))
{
BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
{
if (!porphan->setDependsOn.empty())
{
porphan->setDependsOn.erase(hash);
if (porphan->setDependsOn.empty())
{
vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
}
}
}
}
}
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);
// Compute final coinbase transaction.
txNew.vout[0].nValue = GetBlockValue(nHeight, nFees);
txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
pblock->vtx[0] = txNew;
pblocktemplate->vTxFees[0] = -nFees;
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
UpdateTime(pblock, pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
pblock->nNonce = 0;
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
CValidationState state;
if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))
throw std::runtime_error("CreateNewBlock() : TestBlockValidity failed");
}
return pblocktemplate.release();
}
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
// Update nExtraNonce
static uint256 hashPrevBlock;
if (hashPrevBlock != pblock->hashPrevBlock)
{
nExtraNonce = 0;
hashPrevBlock = pblock->hashPrevBlock;
}
++nExtraNonce;
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
CMutableTransaction txCoinbase(pblock->vtx[0]);
txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = txCoinbase;
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}
#ifdef ENABLE_WALLET
//////////////////////////////////////////////////////////////////////////////
//
// Internal miner
//
double dHashesPerSec = 0.0;
int64_t nHPSTimerStart = 0;
//
// ScanHash scans nonces looking for a hash with at least some zero bits.
// The nonce is usually preserved between calls, but periodically or if the
// nonce is 0xffff0000 or above, the block is rebuilt and nNonce starts over at
// zero.
//
bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phash)
{
// Write the first 76 bytes of the block header to a double-SHA256 state.
CHash256 hasher;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *pblock;
assert(ss.size() == 80);
hasher.Write((unsigned char*)&ss[0], 76);
while (true) {
nNonce++;
// Write the last 4 bytes of the block header (the nonce) to a copy of
// the double-SHA256 state, and compute the result.
CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash);
// Return the nonce if the hash has at least some zero bits,
// caller will check if it has enough to reach the target
if (((uint16_t*)phash)[15] == 0)
return true;
// If nothing found after trying for a while, return -1
if ((nNonce & 0xffff) == 0)
return false;
if ((nNonce & 0xfff) == 0)
boost::this_thread::interruption_point();
}
}
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
{
CPubKey pubkey;
if (!reservekey.GetReservedKey(pubkey))
return NULL;
CScript scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
return CreateNewBlock(scriptPubKey);
}
static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
{
LogPrintf("%s\n", pblock->ToString());
LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
// Found a solution
{
LOCK(cs_main);
if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
return error("BitcoinMiner : generated block is stale");
}
// Remove key from key pool
reservekey.KeepKey();
// Track how many getdata requests this block gets
{
LOCK(wallet.cs_wallet);
wallet.mapRequestCount[pblock->GetHash()] = 0;
}
// Process this block the same as if we had received it from another node
CValidationState state;
if (!ProcessNewBlock(state, NULL, pblock))
return error("BitcoinMiner : ProcessNewBlock, block not accepted");
return true;
}
void static BitcoinMiner(CWallet *pwallet)
{
LogPrintf("BitcoinMiner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("bitcoin-miner");
// Each thread has its own key and counter
CReserveKey reservekey(pwallet);
unsigned int nExtraNonce = 0;
try {
while (true) {
if (Params().MiningRequiresPeers()) {
// Busy-wait for the network to come online so we don't waste time mining
// on an obsolete chain. In regtest mode we expect to fly solo.
while (vNodes.empty())
MilliSleep(1000);
}
//
// Create new block
//
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrev = chainActive.Tip();
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
if (!pblocktemplate.get())
{
LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
return;
}
CBlock *pblock = &pblocktemplate->block;
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
//
// Search
//
int64_t nStart = GetTime();
- uint256 hashTarget = uint256().SetCompact(pblock->nBits);
+ arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
uint256 hash;
uint32_t nNonce = 0;
uint32_t nOldNonce = 0;
while (true) {
bool fFound = ScanHash(pblock, nNonce, &hash);
uint32_t nHashesDone = nNonce - nOldNonce;
nOldNonce = nNonce;
// Check if something found
if (fFound)
{
- if (hash <= hashTarget)
+ if (UintToArith256(hash) <= hashTarget)
{
// Found a solution
pblock->nNonce = nNonce;
assert(hash == pblock->GetHash());
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("BitcoinMiner:\n");
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex(), hashTarget.GetHex());
ProcessBlockFound(pblock, *pwallet, reservekey);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
// In regression test mode, stop mining after a block is found.
if (Params().MineBlocksOnDemand())
throw boost::thread_interrupted();
break;
}
}
// Meter hashes/sec
static int64_t nHashCounter;
if (nHPSTimerStart == 0)
{
nHPSTimerStart = GetTimeMillis();
nHashCounter = 0;
}
else
nHashCounter += nHashesDone;
if (GetTimeMillis() - nHPSTimerStart > 4000)
{
static CCriticalSection cs;
{
LOCK(cs);
if (GetTimeMillis() - nHPSTimerStart > 4000)
{
dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart);
nHPSTimerStart = GetTimeMillis();
nHashCounter = 0;
static int64_t nLogTime;
if (GetTime() - nLogTime > 30 * 60)
{
nLogTime = GetTime();
LogPrintf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0);
}
}
}
}
// Check for stop or if block needs to be rebuilt
boost::this_thread::interruption_point();
// Regtest mode doesn't require peers
if (vNodes.empty() && Params().MiningRequiresPeers())
break;
if (nNonce >= 0xffff0000)
break;
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
if (pindexPrev != chainActive.Tip())
break;
// Update nTime every few seconds
UpdateTime(pblock, pindexPrev);
if (Params().AllowMinDifficultyBlocks())
{
// Changing pblock->nTime can change work required on testnet:
hashTarget.SetCompact(pblock->nBits);
}
}
}
}
catch (const boost::thread_interrupted&)
{
LogPrintf("BitcoinMiner terminated\n");
throw;
}
}
void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
{
static boost::thread_group* minerThreads = NULL;
if (nThreads < 0) {
// In regtest threads defaults to 1
if (Params().DefaultMinerThreads())
nThreads = Params().DefaultMinerThreads();
else
nThreads = boost::thread::hardware_concurrency();
}
if (minerThreads != NULL)
{
minerThreads->interrupt_all();
delete minerThreads;
minerThreads = NULL;
}
if (nThreads == 0 || !fGenerate)
return;
minerThreads = new boost::thread_group();
for (int i = 0; i < nThreads; i++)
minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
}
#endif // ENABLE_WALLET
diff --git a/src/net.cpp b/src/net.cpp
index 6401ecbf8..054846871 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1,2076 +1,2076 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "net.h"
#include "addrman.h"
#include "chainparams.h"
#include "clientversion.h"
#include "primitives/transaction.h"
#include "ui_interface.h"
#ifdef WIN32
#include <string.h>
#else
#include <fcntl.h>
#endif
#ifdef USE_UPNP
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/miniwget.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/upnperrors.h>
#endif
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
// Dump addresses to peers.dat every 15 minutes (900s)
#define DUMP_ADDRESSES_INTERVAL 900
#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
// Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h.
// Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version.
#ifdef WIN32
#ifndef PROTECTION_LEVEL_UNRESTRICTED
#define PROTECTION_LEVEL_UNRESTRICTED 10
#endif
#ifndef IPV6_PROTECTION_LEVEL
#define IPV6_PROTECTION_LEVEL 23
#endif
#endif
using namespace boost;
using namespace std;
namespace {
const int MAX_OUTBOUND_CONNECTIONS = 8;
struct ListenSocket {
SOCKET socket;
bool whitelisted;
ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
};
}
//
// Global state variables
//
bool fDiscover = true;
bool fListen = true;
uint64_t nLocalServices = NODE_NETWORK;
CCriticalSection cs_mapLocalHost;
map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfReachable[NET_MAX] = {};
static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
uint64_t nLocalHostNonce = 0;
static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman;
int nMaxConnections = 125;
bool fAddressesInitialized = false;
vector<CNode*> vNodes;
CCriticalSection cs_vNodes;
map<CInv, CDataStream> mapRelay;
deque<pair<int64_t, CInv> > vRelayExpiration;
CCriticalSection cs_mapRelay;
limitedmap<CInv, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
static deque<string> vOneShots;
CCriticalSection cs_vOneShots;
set<CNetAddr> setservAddNodeAddresses;
CCriticalSection cs_setservAddNodeAddresses;
vector<std::string> vAddedNodes;
CCriticalSection cs_vAddedNodes;
NodeId nLastNodeId = 0;
CCriticalSection cs_nLastNodeId;
static CSemaphore *semOutbound = NULL;
// Signals for message handling
static CNodeSignals g_signals;
CNodeSignals& GetNodeSignals() { return g_signals; }
void AddOneShot(string strDest)
{
LOCK(cs_vOneShots);
vOneShots.push_back(strDest);
}
unsigned short GetListenPort()
{
return (unsigned short)(GetArg("-port", Params().GetDefaultPort()));
}
// find 'best' local address for a particular peer
bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
{
if (!fListen)
return false;
int nBestScore = -1;
int nBestReachability = -1;
{
LOCK(cs_mapLocalHost);
for (map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++)
{
int nScore = (*it).second.nScore;
int nReachability = (*it).first.GetReachabilityFrom(paddrPeer);
if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))
{
addr = CService((*it).first, (*it).second.nPort);
nBestReachability = nReachability;
nBestScore = nScore;
}
}
}
return nBestScore >= 0;
}
// get best local address for a particular peer as a CAddress
// Otherwise, return the unroutable 0.0.0.0 but filled in with
// the normal parameters, since the IP may be changed to a useful
// one by discovery.
CAddress GetLocalAddress(const CNetAddr *paddrPeer)
{
CAddress ret(CService("0.0.0.0",GetListenPort()),0);
CService addr;
if (GetLocal(addr, paddrPeer))
{
ret = CAddress(addr);
}
ret.nServices = nLocalServices;
ret.nTime = GetAdjustedTime();
return ret;
}
bool RecvLine(SOCKET hSocket, string& strLine)
{
strLine = "";
while (true)
{
char c;
int nBytes = recv(hSocket, &c, 1, 0);
if (nBytes > 0)
{
if (c == '\n')
continue;
if (c == '\r')
return true;
strLine += c;
if (strLine.size() >= 9000)
return true;
}
else if (nBytes <= 0)
{
boost::this_thread::interruption_point();
if (nBytes < 0)
{
int nErr = WSAGetLastError();
if (nErr == WSAEMSGSIZE)
continue;
if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
{
MilliSleep(10);
continue;
}
}
if (!strLine.empty())
return true;
if (nBytes == 0)
{
// socket closed
LogPrint("net", "socket closed\n");
return false;
}
else
{
// socket error
int nErr = WSAGetLastError();
LogPrint("net", "recv failed: %s\n", NetworkErrorString(nErr));
return false;
}
}
}
}
int GetnScore(const CService& addr)
{
LOCK(cs_mapLocalHost);
if (mapLocalHost.count(addr) == LOCAL_NONE)
return 0;
return mapLocalHost[addr].nScore;
}
// Is our peer's addrLocal potentially useful as an external IP source?
bool IsPeerAddrLocalGood(CNode *pnode)
{
return fDiscover && pnode->addr.IsRoutable() && pnode->addrLocal.IsRoutable() &&
!IsLimited(pnode->addrLocal.GetNetwork());
}
// pushes our own address to a peer
void AdvertizeLocal(CNode *pnode)
{
if (fListen && pnode->fSuccessfullyConnected)
{
CAddress addrLocal = GetLocalAddress(&pnode->addr);
// If discovery is enabled, sometimes give our peer the address it
// tells us that it sees us as in case it has a better idea of our
// address than we do.
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0))
{
addrLocal.SetIP(pnode->addrLocal);
}
if (addrLocal.IsRoutable())
{
pnode->PushAddress(addrLocal);
}
}
}
void SetReachable(enum Network net, bool fFlag)
{
LOCK(cs_mapLocalHost);
vfReachable[net] = fFlag;
if (net == NET_IPV6 && fFlag)
vfReachable[NET_IPV4] = true;
}
// learn a new local address
bool AddLocal(const CService& addr, int nScore)
{
if (!addr.IsRoutable())
return false;
if (!fDiscover && nScore < LOCAL_MANUAL)
return false;
if (IsLimited(addr))
return false;
LogPrintf("AddLocal(%s,%i)\n", addr.ToString(), nScore);
{
LOCK(cs_mapLocalHost);
bool fAlready = mapLocalHost.count(addr) > 0;
LocalServiceInfo &info = mapLocalHost[addr];
if (!fAlready || nScore >= info.nScore) {
info.nScore = nScore + (fAlready ? 1 : 0);
info.nPort = addr.GetPort();
}
SetReachable(addr.GetNetwork());
}
return true;
}
bool AddLocal(const CNetAddr &addr, int nScore)
{
return AddLocal(CService(addr, GetListenPort()), nScore);
}
/** Make a particular network entirely off-limits (no automatic connects to it) */
void SetLimited(enum Network net, bool fLimited)
{
if (net == NET_UNROUTABLE)
return;
LOCK(cs_mapLocalHost);
vfLimited[net] = fLimited;
}
bool IsLimited(enum Network net)
{
LOCK(cs_mapLocalHost);
return vfLimited[net];
}
bool IsLimited(const CNetAddr &addr)
{
return IsLimited(addr.GetNetwork());
}
/** vote for a local address */
bool SeenLocal(const CService& addr)
{
{
LOCK(cs_mapLocalHost);
if (mapLocalHost.count(addr) == 0)
return false;
mapLocalHost[addr].nScore++;
}
return true;
}
/** check whether a given address is potentially local */
bool IsLocal(const CService& addr)
{
LOCK(cs_mapLocalHost);
return mapLocalHost.count(addr) > 0;
}
/** check whether a given network is one we can probably connect to */
bool IsReachable(enum Network net)
{
LOCK(cs_mapLocalHost);
return vfReachable[net] && !vfLimited[net];
}
/** check whether a given address is in a network we can probably connect to */
bool IsReachable(const CNetAddr& addr)
{
enum Network net = addr.GetNetwork();
return IsReachable(net);
}
void AddressCurrentlyConnected(const CService& addr)
{
addrman.Connected(addr);
}
uint64_t CNode::nTotalBytesRecv = 0;
uint64_t CNode::nTotalBytesSent = 0;
CCriticalSection CNode::cs_totalBytesRecv;
CCriticalSection CNode::cs_totalBytesSent;
CNode* FindNode(const CNetAddr& ip)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if ((CNetAddr)pnode->addr == ip)
return (pnode);
return NULL;
}
CNode* FindNode(const std::string& addrName)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->addrName == addrName)
return (pnode);
return NULL;
}
CNode* FindNode(const CService& addr)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if ((CService)pnode->addr == addr)
return (pnode);
return NULL;
}
CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
{
if (pszDest == NULL) {
if (IsLocal(addrConnect))
return NULL;
// Look for an existing connection
CNode* pnode = FindNode((CService)addrConnect);
if (pnode)
{
pnode->AddRef();
return pnode;
}
}
/// debug print
LogPrint("net", "trying connection %s lastseen=%.1fhrs\n",
pszDest ? pszDest : addrConnect.ToString(),
pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
// Connect
SOCKET hSocket;
bool proxyConnectionFailed = false;
if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) :
ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed))
{
addrman.Attempt(addrConnect);
// Add node
CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
pnode->AddRef();
{
LOCK(cs_vNodes);
vNodes.push_back(pnode);
}
pnode->nTimeConnected = GetTime();
return pnode;
} else if (!proxyConnectionFailed) {
// If connecting to the node failed, and failure is not caused by a problem connecting to
// the proxy, mark this as an attempt.
addrman.Attempt(addrConnect);
}
return NULL;
}
void CNode::CloseSocketDisconnect()
{
fDisconnect = true;
if (hSocket != INVALID_SOCKET)
{
LogPrint("net", "disconnecting peer=%d\n", id);
CloseSocket(hSocket);
}
// in case this fails, we'll empty the recv buffer when the CNode is deleted
TRY_LOCK(cs_vRecvMsg, lockRecv);
if (lockRecv)
vRecvMsg.clear();
}
void CNode::PushVersion()
{
int nBestHeight = g_signals.GetHeight().get_value_or(0);
int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
CAddress addrMe = GetLocalAddress(&addr);
GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
if (fLogIPs)
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
else
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true);
}
std::map<CNetAddr, int64_t> CNode::setBanned;
CCriticalSection CNode::cs_setBanned;
void CNode::ClearBanned()
{
setBanned.clear();
}
bool CNode::IsBanned(CNetAddr ip)
{
bool fResult = false;
{
LOCK(cs_setBanned);
std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip);
if (i != setBanned.end())
{
int64_t t = (*i).second;
if (GetTime() < t)
fResult = true;
}
}
return fResult;
}
bool CNode::Ban(const CNetAddr &addr) {
int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban
{
LOCK(cs_setBanned);
if (setBanned[addr] < banTime)
setBanned[addr] = banTime;
}
return true;
}
std::vector<CSubNet> CNode::vWhitelistedRange;
CCriticalSection CNode::cs_vWhitelistedRange;
bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
LOCK(cs_vWhitelistedRange);
BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {
if (subnet.Match(addr))
return true;
}
return false;
}
void CNode::AddWhitelistedRange(const CSubNet &subnet) {
LOCK(cs_vWhitelistedRange);
vWhitelistedRange.push_back(subnet);
}
#undef X
#define X(name) stats.name = name
void CNode::copyStats(CNodeStats &stats)
{
stats.nodeid = this->GetId();
X(nServices);
X(nLastSend);
X(nLastRecv);
X(nTimeConnected);
X(nTimeOffset);
X(addrName);
X(nVersion);
X(cleanSubVer);
X(fInbound);
X(nStartingHeight);
X(nSendBytes);
X(nRecvBytes);
X(fWhitelisted);
// It is common for nodes with good ping times to suddenly become lagged,
// due to a new block arriving or other large transfer.
// Merely reporting pingtime might fool the caller into thinking the node was still responsive,
// since pingtime does not update until the ping is complete, which might take a while.
// So, if a ping is taking an unusually long time in flight,
// the caller can immediately detect that this is happening.
int64_t nPingUsecWait = 0;
if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) {
nPingUsecWait = GetTimeMicros() - nPingUsecStart;
}
// Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)
stats.dPingTime = (((double)nPingUsecTime) / 1e6);
stats.dPingWait = (((double)nPingUsecWait) / 1e6);
// Leave string empty if addrLocal invalid (not filled in yet)
stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : "";
}
#undef X
// requires LOCK(cs_vRecvMsg)
bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
{
while (nBytes > 0) {
// get current incomplete message, or create a new one
if (vRecvMsg.empty() ||
vRecvMsg.back().complete())
vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion));
CNetMessage& msg = vRecvMsg.back();
// absorb network data
int handled;
if (!msg.in_data)
handled = msg.readHeader(pch, nBytes);
else
handled = msg.readData(pch, nBytes);
if (handled < 0)
return false;
pch += handled;
nBytes -= handled;
if (msg.complete())
msg.nTime = GetTimeMicros();
}
return true;
}
int CNetMessage::readHeader(const char *pch, unsigned int nBytes)
{
// copy data to temporary parsing buffer
unsigned int nRemaining = 24 - nHdrPos;
unsigned int nCopy = std::min(nRemaining, nBytes);
memcpy(&hdrbuf[nHdrPos], pch, nCopy);
nHdrPos += nCopy;
// if header incomplete, exit
if (nHdrPos < 24)
return nCopy;
// deserialize to CMessageHeader
try {
hdrbuf >> hdr;
}
catch (const std::exception&) {
return -1;
}
// reject messages larger than MAX_SIZE
if (hdr.nMessageSize > MAX_SIZE)
return -1;
// switch state to reading message data
in_data = true;
return nCopy;
}
int CNetMessage::readData(const char *pch, unsigned int nBytes)
{
unsigned int nRemaining = hdr.nMessageSize - nDataPos;
unsigned int nCopy = std::min(nRemaining, nBytes);
if (vRecv.size() < nDataPos + nCopy) {
// Allocate up to 256 KiB ahead, but never more than the total message size.
vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024));
}
memcpy(&vRecv[nDataPos], pch, nCopy);
nDataPos += nCopy;
return nCopy;
}
// requires LOCK(cs_vSend)
void SocketSendData(CNode *pnode)
{
std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin();
while (it != pnode->vSendMsg.end()) {
const CSerializeData &data = *it;
assert(data.size() > pnode->nSendOffset);
int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
if (nBytes > 0) {
pnode->nLastSend = GetTime();
pnode->nSendBytes += nBytes;
pnode->nSendOffset += nBytes;
pnode->RecordBytesSent(nBytes);
if (pnode->nSendOffset == data.size()) {
pnode->nSendOffset = 0;
pnode->nSendSize -= data.size();
it++;
} else {
// could not send full message; stop sending more
break;
}
} else {
if (nBytes < 0) {
// error
int nErr = WSAGetLastError();
if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
{
LogPrintf("socket send error %s\n", NetworkErrorString(nErr));
pnode->CloseSocketDisconnect();
}
}
// couldn't send anything at all
break;
}
}
if (it == pnode->vSendMsg.end()) {
assert(pnode->nSendOffset == 0);
assert(pnode->nSendSize == 0);
}
pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it);
}
static list<CNode*> vNodesDisconnected;
void ThreadSocketHandler()
{
unsigned int nPrevNodeCount = 0;
while (true)
{
//
// Disconnect nodes
//
{
LOCK(cs_vNodes);
// Disconnect unused nodes
vector<CNode*> vNodesCopy = vNodes;
BOOST_FOREACH(CNode* pnode, vNodesCopy)
{
if (pnode->fDisconnect ||
(pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty()))
{
// remove from vNodes
vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
// release outbound grant (if any)
pnode->grantOutbound.Release();
// close socket and cleanup
pnode->CloseSocketDisconnect();
// hold in disconnected pool until all refs are released
if (pnode->fNetworkNode || pnode->fInbound)
pnode->Release();
vNodesDisconnected.push_back(pnode);
}
}
}
{
// Delete disconnected nodes
list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy)
{
// wait until threads are done using it
if (pnode->GetRefCount() <= 0)
{
bool fDelete = false;
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend)
{
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv)
{
TRY_LOCK(pnode->cs_inventory, lockInv);
if (lockInv)
fDelete = true;
}
}
}
if (fDelete)
{
vNodesDisconnected.remove(pnode);
delete pnode;
}
}
}
}
if(vNodes.size() != nPrevNodeCount) {
nPrevNodeCount = vNodes.size();
uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
}
//
// Find which sockets have data to receive
//
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 50000; // frequency to poll pnode->vSend
fd_set fdsetRecv;
fd_set fdsetSend;
fd_set fdsetError;
FD_ZERO(&fdsetRecv);
FD_ZERO(&fdsetSend);
FD_ZERO(&fdsetError);
SOCKET hSocketMax = 0;
bool have_fds = false;
BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) {
FD_SET(hListenSocket.socket, &fdsetRecv);
hSocketMax = max(hSocketMax, hListenSocket.socket);
have_fds = true;
}
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
{
if (pnode->hSocket == INVALID_SOCKET)
continue;
FD_SET(pnode->hSocket, &fdsetError);
hSocketMax = max(hSocketMax, pnode->hSocket);
have_fds = true;
// Implement the following logic:
// * If there is data to send, select() for sending data. As this only
// happens when optimistic write failed, we choose to first drain the
// write buffer in this case before receiving more. This avoids
// needlessly queueing received data, if the remote peer is not themselves
// receiving data. This means properly utilizing TCP flow control signalling.
// * Otherwise, if there is no (complete) message in the receive buffer,
// or there is space left in the buffer, select() for receiving data.
// * (if neither of the above applies, there is certainly one message
// in the receiver buffer ready to be processed).
// Together, that means that at least one of the following is always possible,
// so we don't deadlock:
// * We send some data.
// * We wait for data to be received (and disconnect after timeout).
// * We process a message in the buffer (message handler thread).
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend && !pnode->vSendMsg.empty()) {
FD_SET(pnode->hSocket, &fdsetSend);
continue;
}
}
{
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv && (
pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() ||
pnode->GetTotalRecvSize() <= ReceiveFloodSize()))
FD_SET(pnode->hSocket, &fdsetRecv);
}
}
}
int nSelect = select(have_fds ? hSocketMax + 1 : 0,
&fdsetRecv, &fdsetSend, &fdsetError, &timeout);
boost::this_thread::interruption_point();
if (nSelect == SOCKET_ERROR)
{
if (have_fds)
{
int nErr = WSAGetLastError();
LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
for (unsigned int i = 0; i <= hSocketMax; i++)
FD_SET(i, &fdsetRecv);
}
FD_ZERO(&fdsetSend);
FD_ZERO(&fdsetError);
MilliSleep(timeout.tv_usec/1000);
}
//
// Accept new connections
//
BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)
{
if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
{
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
LogPrintf("Warning: Unknown socket family\n");
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->fInbound)
nInbound++;
}
if (hSocket == INVALID_SOCKET)
{
int nErr = WSAGetLastError();
if (nErr != WSAEWOULDBLOCK)
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
}
else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS)
{
CloseSocket(hSocket);
}
else if (CNode::IsBanned(addr) && !whitelisted)
{
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
}
else
{
CNode* pnode = new CNode(hSocket, addr, "", true);
pnode->AddRef();
pnode->fWhitelisted = whitelisted;
{
LOCK(cs_vNodes);
vNodes.push_back(pnode);
}
}
}
}
//
// Service each socket
//
vector<CNode*> vNodesCopy;
{
LOCK(cs_vNodes);
vNodesCopy = vNodes;
BOOST_FOREACH(CNode* pnode, vNodesCopy)
pnode->AddRef();
}
BOOST_FOREACH(CNode* pnode, vNodesCopy)
{
boost::this_thread::interruption_point();
//
// Receive
//
if (pnode->hSocket == INVALID_SOCKET)
continue;
if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError))
{
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv)
{
{
// typical socket buffer is 8K-64K
char pchBuf[0x10000];
int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
if (nBytes > 0)
{
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
pnode->CloseSocketDisconnect();
pnode->nLastRecv = GetTime();
pnode->nRecvBytes += nBytes;
pnode->RecordBytesRecv(nBytes);
}
else if (nBytes == 0)
{
// socket closed gracefully
if (!pnode->fDisconnect)
LogPrint("net", "socket closed\n");
pnode->CloseSocketDisconnect();
}
else if (nBytes < 0)
{
// error
int nErr = WSAGetLastError();
if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
{
if (!pnode->fDisconnect)
LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
pnode->CloseSocketDisconnect();
}
}
}
}
}
//
// Send
//
if (pnode->hSocket == INVALID_SOCKET)
continue;
if (FD_ISSET(pnode->hSocket, &fdsetSend))
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend)
SocketSendData(pnode);
}
//
// Inactivity checking
//
int64_t nTime = GetTime();
if (nTime - pnode->nTimeConnected > 60)
{
if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
{
LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id);
pnode->fDisconnect = true;
}
else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
{
LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
pnode->fDisconnect = true;
}
else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
{
LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
pnode->fDisconnect = true;
}
else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
{
LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
pnode->fDisconnect = true;
}
}
}
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodesCopy)
pnode->Release();
}
}
}
#ifdef USE_UPNP
void ThreadMapPort()
{
std::string port = strprintf("%u", GetListenPort());
const char * multicastif = 0;
const char * minissdpdpath = 0;
struct UPNPDev * devlist = 0;
char lanaddr[64];
#ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0);
#else
/* miniupnpc 1.6 */
int error = 0;
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
#endif
struct UPNPUrls urls;
struct IGDdatas data;
int r;
r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
if (r == 1)
{
if (fDiscover) {
char externalIPAddress[40];
r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
if(r != UPNPCOMMAND_SUCCESS)
LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
else
{
if(externalIPAddress[0])
{
LogPrintf("UPnP: ExternalIPAddress = %s\n", externalIPAddress);
AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP);
}
else
LogPrintf("UPnP: GetExternalIPAddress failed.\n");
}
}
string strDesc = "Bitcoin " + FormatFullVersion();
try {
while (true) {
#ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);
#else
/* miniupnpc 1.6 */
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
#endif
if(r!=UPNPCOMMAND_SUCCESS)
LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
port, port, lanaddr, r, strupnperror(r));
else
LogPrintf("UPnP Port Mapping successful.\n");;
MilliSleep(20*60*1000); // Refresh every 20 minutes
}
}
catch (const boost::thread_interrupted&)
{
r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
LogPrintf("UPNP_DeletePortMapping() returned : %d\n", r);
freeUPNPDevlist(devlist); devlist = 0;
FreeUPNPUrls(&urls);
throw;
}
} else {
LogPrintf("No valid UPnP IGDs found\n");
freeUPNPDevlist(devlist); devlist = 0;
if (r != 0)
FreeUPNPUrls(&urls);
}
}
void MapPort(bool fUseUPnP)
{
static boost::thread* upnp_thread = NULL;
if (fUseUPnP)
{
if (upnp_thread) {
upnp_thread->interrupt();
upnp_thread->join();
delete upnp_thread;
}
upnp_thread = new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort));
}
else if (upnp_thread) {
upnp_thread->interrupt();
upnp_thread->join();
delete upnp_thread;
upnp_thread = NULL;
}
}
#else
void MapPort(bool)
{
// Intentionally left blank.
}
#endif
void ThreadDNSAddressSeed()
{
// goal: only query DNS seeds if address need is acute
if ((addrman.size() > 0) &&
(!GetBoolArg("-forcednsseed", false))) {
MilliSleep(11 * 1000);
LOCK(cs_vNodes);
if (vNodes.size() >= 2) {
LogPrintf("P2P peers available. Skipped DNS seeding.\n");
return;
}
}
const vector<CDNSSeedData> &vSeeds = Params().DNSSeeds();
int found = 0;
LogPrintf("Loading addresses from DNS seeds (could take a while)\n");
BOOST_FOREACH(const CDNSSeedData &seed, vSeeds) {
if (HaveNameProxy()) {
AddOneShot(seed.host);
} else {
vector<CNetAddr> vIPs;
vector<CAddress> vAdd;
if (LookupHost(seed.host.c_str(), vIPs))
{
BOOST_FOREACH(CNetAddr& ip, vIPs)
{
int nOneDay = 24*3600;
CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()));
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
vAdd.push_back(addr);
found++;
}
}
addrman.Add(vAdd, CNetAddr(seed.name, true));
}
}
LogPrintf("%d addresses found from DNS seeds\n", found);
}
void DumpAddresses()
{
int64_t nStart = GetTimeMillis();
CAddrDB adb;
adb.Write(addrman);
LogPrint("net", "Flushed %d addresses to peers.dat %dms\n",
addrman.size(), GetTimeMillis() - nStart);
}
void static ProcessOneShot()
{
string strDest;
{
LOCK(cs_vOneShots);
if (vOneShots.empty())
return;
strDest = vOneShots.front();
vOneShots.pop_front();
}
CAddress addr;
CSemaphoreGrant grant(*semOutbound, true);
if (grant) {
if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true))
AddOneShot(strDest);
}
}
void ThreadOpenConnections()
{
// Connect to specific addresses
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)
{
for (int64_t nLoop = 0;; nLoop++)
{
ProcessOneShot();
BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"])
{
CAddress addr;
OpenNetworkConnection(addr, NULL, strAddr.c_str());
for (int i = 0; i < 10 && i < nLoop; i++)
{
MilliSleep(500);
}
}
MilliSleep(500);
}
}
// Initiate network connections
int64_t nStart = GetTime();
while (true)
{
ProcessOneShot();
MilliSleep(500);
CSemaphoreGrant grant(*semOutbound);
boost::this_thread::interruption_point();
// Add seed nodes if DNS seeds are all down (an infrastructure attack?).
if (addrman.size() == 0 && (GetTime() - nStart > 60)) {
static bool done = false;
if (!done) {
LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n");
addrman.Add(Params().FixedSeeds(), CNetAddr("127.0.0.1"));
done = true;
}
}
//
// Choose an address to connect to based on most recently seen
//
CAddress addrConnect;
// Only connect out to one peer per network group (/16 for IPv4).
// Do this here so we don't have to critsect vNodes inside mapAddresses critsect.
int nOutbound = 0;
set<vector<unsigned char> > setConnected;
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes) {
if (!pnode->fInbound) {
setConnected.insert(pnode->addr.GetGroup());
nOutbound++;
}
}
}
int64_t nANow = GetAdjustedTime();
int nTries = 0;
while (true)
{
// use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections)
CAddress addr = addrman.Select(10 + min(nOutbound,8)*10);
// if we selected an invalid address, restart
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
break;
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
// already-connected network ranges, ...) before trying new addrman addresses.
nTries++;
if (nTries > 100)
break;
if (IsLimited(addr))
continue;
// only consider very recently tried nodes after 30 failed attempts
if (nANow - addr.nLastTry < 600 && nTries < 30)
continue;
// do not allow non-default ports, unless after 50 invalid addresses selected already
if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50)
continue;
addrConnect = addr;
break;
}
if (addrConnect.IsValid())
OpenNetworkConnection(addrConnect, &grant);
}
}
void ThreadOpenAddedConnections()
{
{
LOCK(cs_vAddedNodes);
vAddedNodes = mapMultiArgs["-addnode"];
}
if (HaveNameProxy()) {
while(true) {
list<string> lAddresses(0);
{
LOCK(cs_vAddedNodes);
BOOST_FOREACH(string& strAddNode, vAddedNodes)
lAddresses.push_back(strAddNode);
}
BOOST_FOREACH(string& strAddNode, lAddresses) {
CAddress addr;
CSemaphoreGrant grant(*semOutbound);
OpenNetworkConnection(addr, &grant, strAddNode.c_str());
MilliSleep(500);
}
MilliSleep(120000); // Retry every 2 minutes
}
}
for (unsigned int i = 0; true; i++)
{
list<string> lAddresses(0);
{
LOCK(cs_vAddedNodes);
BOOST_FOREACH(string& strAddNode, vAddedNodes)
lAddresses.push_back(strAddNode);
}
list<vector<CService> > lservAddressesToAdd(0);
BOOST_FOREACH(string& strAddNode, lAddresses)
{
vector<CService> vservNode(0);
if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0))
{
lservAddressesToAdd.push_back(vservNode);
{
LOCK(cs_setservAddNodeAddresses);
BOOST_FOREACH(CService& serv, vservNode)
setservAddNodeAddresses.insert(serv);
}
}
}
// Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry
// (keeping in mind that addnode entries can have many IPs if fNameLookup)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
for (list<vector<CService> >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++)
BOOST_FOREACH(CService& addrNode, *(it))
if (pnode->addr == addrNode)
{
it = lservAddressesToAdd.erase(it);
it--;
break;
}
}
BOOST_FOREACH(vector<CService>& vserv, lservAddressesToAdd)
{
CSemaphoreGrant grant(*semOutbound);
OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant);
MilliSleep(500);
}
MilliSleep(120000); // Retry every 2 minutes
}
}
// if successful, this moves the passed grant to the constructed node
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot)
{
//
// Initiate outbound network connection
//
boost::this_thread::interruption_point();
if (!pszDest) {
if (IsLocal(addrConnect) ||
FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
FindNode(addrConnect.ToStringIPPort()))
return false;
} else if (FindNode(pszDest))
return false;
CNode* pnode = ConnectNode(addrConnect, pszDest);
boost::this_thread::interruption_point();
if (!pnode)
return false;
if (grantOutbound)
grantOutbound->MoveTo(pnode->grantOutbound);
pnode->fNetworkNode = true;
if (fOneShot)
pnode->fOneShot = true;
return true;
}
void ThreadMessageHandler()
{
SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
while (true)
{
vector<CNode*> vNodesCopy;
{
LOCK(cs_vNodes);
vNodesCopy = vNodes;
BOOST_FOREACH(CNode* pnode, vNodesCopy) {
pnode->AddRef();
}
}
// Poll the connected nodes for messages
CNode* pnodeTrickle = NULL;
if (!vNodesCopy.empty())
pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())];
bool fSleep = true;
BOOST_FOREACH(CNode* pnode, vNodesCopy)
{
if (pnode->fDisconnect)
continue;
// Receive messages
{
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv)
{
if (!g_signals.ProcessMessages(pnode))
pnode->CloseSocketDisconnect();
if (pnode->nSendSize < SendBufferSize())
{
if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete()))
{
fSleep = false;
}
}
}
}
boost::this_thread::interruption_point();
// Send messages
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend)
g_signals.SendMessages(pnode, pnode == pnodeTrickle);
}
boost::this_thread::interruption_point();
}
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodesCopy)
pnode->Release();
}
if (fSleep)
MilliSleep(100);
}
}
bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted)
{
strError = "";
int nOne = 1;
// Create socket for listening for incoming connections
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{
strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString());
LogPrintf("%s\n", strError);
return false;
}
SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
if (hListenSocket == INVALID_SOCKET)
{
strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
return false;
}
#ifndef WIN32
#ifdef SO_NOSIGPIPE
// Different way of disabling SIGPIPE on BSD
setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
#endif
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted. Not an issue on windows!
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
#endif
// Set to non-blocking, incoming connections will also inherit this
if (!SetSocketNonBlocking(hListenSocket, true)) {
strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
return false;
}
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
#ifdef WIN32
setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int));
#else
setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int));
#endif
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
#endif
}
if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
strError = strprintf(_("Unable to bind to %s on this computer. Bitcoin Core is probably already running."), addrBind.ToString());
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
LogPrintf("%s\n", strError);
CloseSocket(hListenSocket);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToString());
// Listen for incoming connections
if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
{
strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
CloseSocket(hListenSocket);
return false;
}
vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
AddLocal(addrBind, LOCAL_BIND);
return true;
}
void static Discover(boost::thread_group& threadGroup)
{
if (!fDiscover)
return;
#ifdef WIN32
// Get local host IP
char pszHostName[256] = "";
if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR)
{
vector<CNetAddr> vaddr;
if (LookupHost(pszHostName, vaddr))
{
BOOST_FOREACH (const CNetAddr &addr, vaddr)
{
if (AddLocal(addr, LOCAL_IF))
LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString());
}
}
}
#else
// Get local host ip
struct ifaddrs* myaddrs;
if (getifaddrs(&myaddrs) == 0)
{
for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL) continue;
if ((ifa->ifa_flags & IFF_UP) == 0) continue;
if (strcmp(ifa->ifa_name, "lo") == 0) continue;
if (strcmp(ifa->ifa_name, "lo0") == 0) continue;
if (ifa->ifa_addr->sa_family == AF_INET)
{
struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
CNetAddr addr(s4->sin_addr);
if (AddLocal(addr, LOCAL_IF))
LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString());
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
CNetAddr addr(s6->sin6_addr);
if (AddLocal(addr, LOCAL_IF))
LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString());
}
}
freeifaddrs(myaddrs);
}
#endif
}
void StartNode(boost::thread_group& threadGroup)
{
uiInterface.InitMessage(_("Loading addresses..."));
// Load addresses for peers.dat
int64_t nStart = GetTimeMillis();
{
CAddrDB adb;
if (!adb.Read(addrman))
LogPrintf("Invalid or missing peers.dat; recreating\n");
}
LogPrintf("Loaded %i addresses from peers.dat %dms\n",
addrman.size(), GetTimeMillis() - nStart);
fAddressesInitialized = true;
if (semOutbound == NULL) {
// initialize semaphore
int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections);
semOutbound = new CSemaphore(nMaxOutbound);
}
if (pnodeLocalHost == NULL)
pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));
Discover(threadGroup);
//
// Start threads
//
if (!GetBoolArg("-dnsseed", true))
LogPrintf("DNS seeding disabled\n");
else
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "dnsseed", &ThreadDNSAddressSeed));
// Map ports with UPnP
MapPort(GetBoolArg("-upnp", DEFAULT_UPNP));
// Send and receive from sockets, accept connections
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "net", &ThreadSocketHandler));
// Initiate outbound connections from -addnode
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "addcon", &ThreadOpenAddedConnections));
// Initiate outbound connections
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "opencon", &ThreadOpenConnections));
// Process messages
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
// Dump network addresses
threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000));
}
bool StopNode()
{
LogPrintf("StopNode()\n");
MapPort(false);
if (semOutbound)
for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
semOutbound->post();
if (fAddressesInitialized)
{
DumpAddresses();
fAddressesInitialized = false;
}
return true;
}
class CNetCleanup
{
public:
CNetCleanup() {}
~CNetCleanup()
{
// Close sockets
BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->hSocket != INVALID_SOCKET)
CloseSocket(pnode->hSocket);
BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
if (hListenSocket.socket != INVALID_SOCKET)
if (!CloseSocket(hListenSocket.socket))
LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
// clean up some globals (to help leak detection)
BOOST_FOREACH(CNode *pnode, vNodes)
delete pnode;
BOOST_FOREACH(CNode *pnode, vNodesDisconnected)
delete pnode;
vNodes.clear();
vNodesDisconnected.clear();
vhListenSocket.clear();
delete semOutbound;
semOutbound = NULL;
delete pnodeLocalHost;
pnodeLocalHost = NULL;
#ifdef WIN32
// Shutdown Windows Sockets
WSACleanup();
#endif
}
}
instance_of_cnetcleanup;
void RelayTransaction(const CTransaction& tx)
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(10000);
ss << tx;
RelayTransaction(tx, ss);
}
void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
{
CInv inv(MSG_TX, tx.GetHash());
{
LOCK(cs_mapRelay);
// Expire old relay messages
while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())
{
mapRelay.erase(vRelayExpiration.front().second);
vRelayExpiration.pop_front();
}
// Save original serialized message so newer versions are preserved
mapRelay.insert(std::make_pair(inv, ss));
vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv));
}
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
{
if(!pnode->fRelayTxes)
continue;
LOCK(pnode->cs_filter);
if (pnode->pfilter)
{
if (pnode->pfilter->IsRelevantAndUpdate(tx))
pnode->PushInventory(inv);
} else
pnode->PushInventory(inv);
}
}
void CNode::RecordBytesRecv(uint64_t bytes)
{
LOCK(cs_totalBytesRecv);
nTotalBytesRecv += bytes;
}
void CNode::RecordBytesSent(uint64_t bytes)
{
LOCK(cs_totalBytesSent);
nTotalBytesSent += bytes;
}
uint64_t CNode::GetTotalBytesRecv()
{
LOCK(cs_totalBytesRecv);
return nTotalBytesRecv;
}
uint64_t CNode::GetTotalBytesSent()
{
LOCK(cs_totalBytesSent);
return nTotalBytesSent;
}
void CNode::Fuzz(int nChance)
{
if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake
if (GetRand(nChance) != 0) return; // Fuzz 1 of every nChance messages
switch (GetRand(3))
{
case 0:
// xor a random byte with a random value:
if (!ssSend.empty()) {
CDataStream::size_type pos = GetRand(ssSend.size());
ssSend[pos] ^= (unsigned char)(GetRand(256));
}
break;
case 1:
// delete a random byte:
if (!ssSend.empty()) {
CDataStream::size_type pos = GetRand(ssSend.size());
ssSend.erase(ssSend.begin()+pos);
}
break;
case 2:
// insert a random byte at a random position
{
CDataStream::size_type pos = GetRand(ssSend.size());
char ch = (char)GetRand(256);
ssSend.insert(ssSend.begin()+pos, ch);
}
break;
}
// Chance of more than one change half the time:
// (more changes exponentially less likely):
Fuzz(2);
}
//
// CAddrDB
//
CAddrDB::CAddrDB()
{
pathAddr = GetDataDir() / "peers.dat";
}
bool CAddrDB::Write(const CAddrMan& addr)
{
// Generate random temporary filename
unsigned short randv = 0;
GetRandBytes((unsigned char*)&randv, sizeof(randv));
std::string tmpfn = strprintf("peers.dat.%04x", randv);
// serialize addresses, checksum data up to that point, then append csum
CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
ssPeers << FLATDATA(Params().MessageStart());
ssPeers << addr;
uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
ssPeers << hash;
// open temp output file, and associate with CAutoFile
boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
FILE *file = fopen(pathTmp.string().c_str(), "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s : Failed to open file %s", __func__, pathTmp.string());
// Write and commit header, data
try {
fileout << ssPeers;
}
catch (const std::exception& e) {
return error("%s : Serialize or I/O error - %s", __func__, e.what());
}
FileCommit(fileout.Get());
fileout.fclose();
// replace existing peers.dat, if any, with new peers.dat.XXXX
if (!RenameOver(pathTmp, pathAddr))
return error("%s : Rename-into-place failed", __func__);
return true;
}
bool CAddrDB::Read(CAddrMan& addr)
{
// open input file, and associate with CAutoFile
FILE *file = fopen(pathAddr.string().c_str(), "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("%s : Failed to open file %s", __func__, pathAddr.string());
// use file size to size memory buffer
int fileSize = boost::filesystem::file_size(pathAddr);
int dataSize = fileSize - sizeof(uint256);
// Don't try to resize to a negative number if file is small
if (dataSize < 0)
dataSize = 0;
vector<unsigned char> vchData;
vchData.resize(dataSize);
uint256 hashIn;
// read data and checksum from file
try {
filein.read((char *)&vchData[0], dataSize);
filein >> hashIn;
}
catch (const std::exception& e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
filein.fclose();
CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
// verify stored checksum matches input data
uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
if (hashIn != hashTmp)
return error("%s : Checksum mismatch, data corrupted", __func__);
unsigned char pchMsgTmp[4];
try {
// de-serialize file header (network specific magic number) and ..
ssPeers >> FLATDATA(pchMsgTmp);
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
return error("%s : Invalid network magic number", __func__);
// de-serialize address data into one CAddrMan object
ssPeers >> addr;
}
catch (const std::exception& e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
return true;
}
unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000)
{
nServices = 0;
hSocket = hSocketIn;
nRecvVersion = INIT_PROTO_VERSION;
nLastSend = 0;
nLastRecv = 0;
nSendBytes = 0;
nRecvBytes = 0;
nTimeConnected = GetTime();
nTimeOffset = 0;
addr = addrIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0;
strSubVer = "";
fWhitelisted = false;
fOneShot = false;
fClient = false; // set by version message
fInbound = fInboundIn;
fNetworkNode = false;
fSuccessfullyConnected = false;
fDisconnect = false;
nRefCount = 0;
nSendSize = 0;
nSendOffset = 0;
- hashContinue = 0;
+ hashContinue = uint256();
nStartingHeight = -1;
fGetAddr = false;
fRelayTxes = false;
setInventoryKnown.max_size(SendBufferSize() / 1000);
pfilter = new CBloomFilter();
nPingNonceSent = 0;
nPingUsecStart = 0;
nPingUsecTime = 0;
fPingQueued = false;
{
LOCK(cs_nLastNodeId);
id = nLastNodeId++;
}
if (fLogIPs)
LogPrint("net", "Added connection to %s peer=%d\n", addrName, id);
else
LogPrint("net", "Added connection peer=%d\n", id);
// Be shy and don't send version until we hear
if (hSocket != INVALID_SOCKET && !fInbound)
PushVersion();
GetNodeSignals().InitializeNode(GetId(), this);
}
CNode::~CNode()
{
CloseSocket(hSocket);
if (pfilter)
delete pfilter;
GetNodeSignals().FinalizeNode(GetId());
}
void CNode::AskFor(const CInv& inv)
{
if (mapAskFor.size() > MAPASKFOR_MAX_SZ)
return;
// We're using mapAskFor as a priority queue,
// the key is the earliest time the request can be sent
int64_t nRequestTime;
limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv);
if (it != mapAlreadyAskedFor.end())
nRequestTime = it->second;
else
nRequestTime = 0;
LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id);
// Make sure not to reuse time indexes to keep things in the same order
int64_t nNow = GetTimeMicros() - 1000000;
static int64_t nLastTime;
++nLastTime;
nNow = std::max(nNow, nLastTime);
nLastTime = nNow;
// Each retry is 2 minutes after the last
nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
if (it != mapAlreadyAskedFor.end())
mapAlreadyAskedFor.update(it, nRequestTime);
else
mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime));
mapAskFor.insert(std::make_pair(nRequestTime, inv));
}
void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend)
{
ENTER_CRITICAL_SECTION(cs_vSend);
assert(ssSend.size() == 0);
ssSend << CMessageHeader(pszCommand, 0);
LogPrint("net", "sending: %s ", pszCommand);
}
void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend)
{
ssSend.clear();
LEAVE_CRITICAL_SECTION(cs_vSend);
LogPrint("net", "(aborted)\n");
}
void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
{
// The -*messagestest options are intentionally not documented in the help message,
// since they are only used during development to debug the networking code and are
// not intended for end-users.
if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0)
{
LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n");
AbortMessage();
return;
}
if (mapArgs.count("-fuzzmessagestest"))
Fuzz(GetArg("-fuzzmessagestest", 10));
if (ssSend.size() == 0)
return;
// Set the size
unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE;
memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize));
// Set the checksum
uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end());
unsigned int nChecksum = 0;
memcpy(&nChecksum, &hash, sizeof(nChecksum));
assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum));
memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum));
LogPrint("net", "(%d bytes) peer=%d\n", nSize, id);
std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData());
ssSend.GetAndClear(*it);
nSendSize += (*it).size();
// If write queue empty, attempt "optimistic write"
if (it == vSendMsg.begin())
SocketSendData(this);
LEAVE_CRITICAL_SECTION(cs_vSend);
}
diff --git a/src/pow.cpp b/src/pow.cpp
index e91e3d893..90bbff0a3 100644
--- a/src/pow.cpp
+++ b/src/pow.cpp
@@ -1,113 +1,114 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "pow.h"
+#include "arith_uint256.h"
#include "chain.h"
#include "chainparams.h"
#include "primitives/block.h"
#include "uint256.h"
#include "util.h"
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock)
{
unsigned int nProofOfWorkLimit = Params().ProofOfWorkLimit().GetCompact();
// Genesis block
if (pindexLast == NULL)
return nProofOfWorkLimit;
// Only change once per interval
if ((pindexLast->nHeight+1) % Params().Interval() != 0)
{
if (Params().AllowMinDifficultyBlocks())
{
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* 10 minutes
// then allow mining of a min-difficulty block.
if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + Params().TargetSpacing()*2)
return nProofOfWorkLimit;
else
{
// Return the last non-special-min-difficulty-rules-block
const CBlockIndex* pindex = pindexLast;
while (pindex->pprev && pindex->nHeight % Params().Interval() != 0 && pindex->nBits == nProofOfWorkLimit)
pindex = pindex->pprev;
return pindex->nBits;
}
}
return pindexLast->nBits;
}
// Go back by what we want to be 14 days worth of blocks
const CBlockIndex* pindexFirst = pindexLast;
for (int i = 0; pindexFirst && i < Params().Interval()-1; i++)
pindexFirst = pindexFirst->pprev;
assert(pindexFirst);
// Limit adjustment step
int64_t nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan);
if (nActualTimespan < Params().TargetTimespan()/4)
nActualTimespan = Params().TargetTimespan()/4;
if (nActualTimespan > Params().TargetTimespan()*4)
nActualTimespan = Params().TargetTimespan()*4;
// Retarget
- uint256 bnNew;
- uint256 bnOld;
+ arith_uint256 bnNew;
+ arith_uint256 bnOld;
bnNew.SetCompact(pindexLast->nBits);
bnOld = bnNew;
bnNew *= nActualTimespan;
bnNew /= Params().TargetTimespan();
if (bnNew > Params().ProofOfWorkLimit())
bnNew = Params().ProofOfWorkLimit();
/// debug print
LogPrintf("GetNextWorkRequired RETARGET\n");
LogPrintf("Params().TargetTimespan() = %d nActualTimespan = %d\n", Params().TargetTimespan(), nActualTimespan);
LogPrintf("Before: %08x %s\n", pindexLast->nBits, bnOld.ToString());
LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.ToString());
return bnNew.GetCompact();
}
bool CheckProofOfWork(uint256 hash, unsigned int nBits)
{
bool fNegative;
bool fOverflow;
- uint256 bnTarget;
+ arith_uint256 bnTarget;
if (Params().SkipProofOfWorkCheck())
return true;
bnTarget.SetCompact(nBits, &fNegative, &fOverflow);
// Check range
if (fNegative || bnTarget == 0 || fOverflow || bnTarget > Params().ProofOfWorkLimit())
return error("CheckProofOfWork() : nBits below minimum work");
// Check proof of work matches claimed amount
- if (hash > bnTarget)
+ if (UintToArith256(hash) > bnTarget)
return error("CheckProofOfWork() : hash doesn't match nBits");
return true;
}
-uint256 GetBlockProof(const CBlockIndex& block)
+arith_uint256 GetBlockProof(const CBlockIndex& block)
{
- uint256 bnTarget;
+ arith_uint256 bnTarget;
bool fNegative;
bool fOverflow;
bnTarget.SetCompact(block.nBits, &fNegative, &fOverflow);
if (fNegative || fOverflow || bnTarget == 0)
return 0;
// We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256
- // as it's too large for a uint256. However, as 2**256 is at least as large
+ // as it's too large for a arith_uint256. However, as 2**256 is at least as large
// as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1,
// or ~bnTarget / (nTarget+1) + 1.
return (~bnTarget / (bnTarget + 1)) + 1;
}
diff --git a/src/pow.h b/src/pow.h
index 89b219f80..3337a30a5 100644
--- a/src/pow.h
+++ b/src/pow.h
@@ -1,21 +1,22 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_POW_H
#define BITCOIN_POW_H
#include <stdint.h>
class CBlockHeader;
class CBlockIndex;
class uint256;
+class arith_uint256;
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock);
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
-uint256 GetBlockProof(const CBlockIndex& block);
+arith_uint256 GetBlockProof(const CBlockIndex& block);
#endif // BITCOIN_POW_H
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index 2b6a302ee..3b4a36039 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -1,130 +1,130 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "primitives/block.h"
#include "hash.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
uint256 CBlockHeader::GetHash() const
{
return Hash(BEGIN(nVersion), END(nNonce));
}
uint256 CBlock::BuildMerkleTree(bool* fMutated) const
{
/* WARNING! If you're reading this because you're learning about crypto
and/or designing a new system that will use merkle trees, keep in mind
that the following merkle tree algorithm has a serious flaw related to
duplicate txids, resulting in a vulnerability (CVE-2012-2459).
The reason is that if the number of hashes in the list at a given time
is odd, the last one is duplicated before computing the next level (which
is unusual in Merkle trees). This results in certain sequences of
transactions leading to the same merkle root. For example, these two
trees:
A A
/ \ / \
B C B C
/ \ | / \ / \
D E F D E F F
/ \ / \ / \ / \ / \ / \ / \
1 2 3 4 5 6 1 2 3 4 5 6 5 6
for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and
6 are repeated) result in the same root hash A (because the hash of both
of (F) and (F,F) is C).
The vulnerability results from being able to send a block with such a
transaction list, with the same merkle root, and the same block hash as
the original without duplication, resulting in failed validation. If the
receiving node proceeds to mark that block as permanently invalid
however, it will fail to accept further unmodified (and thus potentially
valid) versions of the same block. We defend against this by detecting
the case where we would hash two identical hashes at the end of the list
together, and treating that identically to the block having an invalid
merkle root. Assuming no double-SHA256 collisions, this will detect all
known ways of changing the transactions without affecting the merkle
root.
*/
vMerkleTree.clear();
vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
for (std::vector<CTransaction>::const_iterator it(vtx.begin()); it != vtx.end(); ++it)
vMerkleTree.push_back(it->GetHash());
int j = 0;
bool mutated = false;
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
{
for (int i = 0; i < nSize; i += 2)
{
int i2 = std::min(i+1, nSize-1);
if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j+i] == vMerkleTree[j+i2]) {
// Two identical hashes at the end of the list at a particular level.
mutated = true;
}
vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
}
j += nSize;
}
if (fMutated) {
*fMutated = mutated;
}
- return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
+ return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
}
std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
{
if (vMerkleTree.empty())
BuildMerkleTree();
std::vector<uint256> vMerkleBranch;
int j = 0;
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
{
int i = std::min(nIndex^1, nSize-1);
vMerkleBranch.push_back(vMerkleTree[j+i]);
nIndex >>= 1;
j += nSize;
}
return vMerkleBranch;
}
uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
{
if (nIndex == -1)
- return 0;
+ return uint256();
for (std::vector<uint256>::const_iterator it(vMerkleBranch.begin()); it != vMerkleBranch.end(); ++it)
{
if (nIndex & 1)
hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash));
else
hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it));
nIndex >>= 1;
}
return hash;
}
std::string CBlock::ToString() const
{
std::stringstream s;
s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n",
GetHash().ToString(),
nVersion,
hashPrevBlock.ToString(),
hashMerkleRoot.ToString(),
nTime, nBits, nNonce,
vtx.size());
for (unsigned int i = 0; i < vtx.size(); i++)
{
s << " " << vtx[i].ToString() << "\n";
}
s << " vMerkleTree: ";
for (unsigned int i = 0; i < vMerkleTree.size(); i++)
s << " " << vMerkleTree[i].ToString();
s << "\n";
return s.str();
}
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 1449882bd..53fcd104a 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -1,171 +1,171 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 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_PRIMITIVES_BLOCK_H
#define BITCOIN_PRIMITIVES_BLOCK_H
#include "primitives/transaction.h"
#include "serialize.h"
#include "uint256.h"
/** The maximum allowed size for a serialized block, in bytes (network rule) */
static const unsigned int MAX_BLOCK_SIZE = 1000000;
/** Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
* requirements. When they solve the proof-of-work, they broadcast the block
* to everyone and the block is added to the block chain. The first transaction
* in the block is a special one that creates a new coin owned by the creator
* of the block.
*/
class CBlockHeader
{
public:
// header
static const int32_t CURRENT_VERSION=2;
int32_t nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
CBlockHeader()
{
SetNull();
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
}
void SetNull()
{
nVersion = CBlockHeader::CURRENT_VERSION;
- hashPrevBlock = 0;
- hashMerkleRoot = 0;
+ hashPrevBlock.SetNull();
+ hashMerkleRoot.SetNull();
nTime = 0;
nBits = 0;
nNonce = 0;
}
bool IsNull() const
{
return (nBits == 0);
}
uint256 GetHash() const;
int64_t GetBlockTime() const
{
return (int64_t)nTime;
}
};
class CBlock : public CBlockHeader
{
public:
// network and disk
std::vector<CTransaction> vtx;
// memory only
mutable std::vector<uint256> vMerkleTree;
CBlock()
{
SetNull();
}
CBlock(const CBlockHeader &header)
{
SetNull();
*((CBlockHeader*)this) = header;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*(CBlockHeader*)this);
READWRITE(vtx);
}
void SetNull()
{
CBlockHeader::SetNull();
vtx.clear();
vMerkleTree.clear();
}
CBlockHeader GetBlockHeader() const
{
CBlockHeader block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrevBlock;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
// Build the in-memory merkle tree for this block and return the merkle root.
// If non-NULL, *mutated is set to whether mutation was detected in the merkle
// tree (a duplication of transactions in the block leading to an identical
// merkle root).
uint256 BuildMerkleTree(bool* mutated = NULL) const;
std::vector<uint256> GetMerkleBranch(int nIndex) const;
static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);
std::string ToString() const;
};
/** Describes a place in the block chain to another node such that if the
* other node doesn't have the same branch, it can find a recent common trunk.
* The further back it is, the further before the fork it may be.
*/
struct CBlockLocator
{
std::vector<uint256> vHave;
CBlockLocator() {}
CBlockLocator(const std::vector<uint256>& vHaveIn)
{
vHave = vHaveIn;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vHave);
}
void SetNull()
{
vHave.clear();
}
bool IsNull()
{
return vHave.empty();
}
};
#endif // BITCOIN_PRIMITIVES_BLOCK_H
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 230aedd89..848d4d3e6 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -1,142 +1,142 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "primitives/transaction.h"
#include "hash.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
std::string COutPoint::ToString() const
{
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
}
CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn)
{
prevout = prevoutIn;
scriptSig = scriptSigIn;
nSequence = nSequenceIn;
}
CTxIn::CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn, uint32_t nSequenceIn)
{
prevout = COutPoint(hashPrevTx, nOut);
scriptSig = scriptSigIn;
nSequence = nSequenceIn;
}
std::string CTxIn::ToString() const
{
std::string str;
str += "CTxIn(";
str += prevout.ToString();
if (prevout.IsNull())
str += strprintf(", coinbase %s", HexStr(scriptSig));
else
str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24));
if (nSequence != std::numeric_limits<unsigned int>::max())
str += strprintf(", nSequence=%u", nSequence);
str += ")";
return str;
}
CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
{
nValue = nValueIn;
scriptPubKey = scriptPubKeyIn;
}
uint256 CTxOut::GetHash() const
{
return SerializeHash(*this);
}
std::string CTxOut::ToString() const
{
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30));
}
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {}
uint256 CMutableTransaction::GetHash() const
{
return SerializeHash(*this);
}
void CTransaction::UpdateHash() const
{
*const_cast<uint256*>(&hash) = SerializeHash(*this);
}
-CTransaction::CTransaction() : hash(0), nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
+CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {
UpdateHash();
}
CTransaction& CTransaction::operator=(const CTransaction &tx) {
*const_cast<int*>(&nVersion) = tx.nVersion;
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
*const_cast<uint256*>(&hash) = tx.hash;
return *this;
}
CAmount CTransaction::GetValueOut() const
{
CAmount nValueOut = 0;
for (std::vector<CTxOut>::const_iterator it(vout.begin()); it != vout.end(); ++it)
{
nValueOut += it->nValue;
if (!MoneyRange(it->nValue) || !MoneyRange(nValueOut))
throw std::runtime_error("CTransaction::GetValueOut() : value out of range");
}
return nValueOut;
}
double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const
{
nTxSize = CalculateModifiedSize(nTxSize);
if (nTxSize == 0) return 0.0;
return dPriorityInputs / nTxSize;
}
unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const
{
// In order to avoid disincentivizing cleaning up the UTXO set we don't count
// the constant overhead for each txin and up to 110 bytes of scriptSig (which
// is enough to cover a compressed pubkey p2sh redemption) for priority.
// Providing any more cleanup incentive than making additional inputs free would
// risk encouraging people to create junk outputs to redeem later.
if (nTxSize == 0)
nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION);
for (std::vector<CTxIn>::const_iterator it(vin.begin()); it != vin.end(); ++it)
{
unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size());
if (nTxSize > offset)
nTxSize -= offset;
}
return nTxSize;
}
std::string CTransaction::ToString() const
{
std::string str;
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n",
GetHash().ToString().substr(0,10),
nVersion,
vin.size(),
vout.size(),
nLockTime);
for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n";
for (unsigned int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n";
return str;
}
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index da3501230..1b5a47e0d 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -1,276 +1,276 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PRIMITIVES_TRANSACTION_H
#define BITCOIN_PRIMITIVES_TRANSACTION_H
#include "amount.h"
#include "script/script.h"
#include "serialize.h"
#include "uint256.h"
/** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint
{
public:
uint256 hash;
uint32_t n;
COutPoint() { SetNull(); }
COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(FLATDATA(*this));
}
- void SetNull() { hash = 0; n = (uint32_t) -1; }
- bool IsNull() const { return (hash == 0 && n == (uint32_t) -1); }
+ void SetNull() { hash.SetNull(); n = (uint32_t) -1; }
+ bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); }
friend bool operator<(const COutPoint& a, const COutPoint& b)
{
return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n));
}
friend bool operator==(const COutPoint& a, const COutPoint& b)
{
return (a.hash == b.hash && a.n == b.n);
}
friend bool operator!=(const COutPoint& a, const COutPoint& b)
{
return !(a == b);
}
std::string ToString() const;
};
/** An input of a transaction. It contains the location of the previous
* transaction's output that it claims and a signature that matches the
* output's public key.
*/
class CTxIn
{
public:
COutPoint prevout;
CScript scriptSig;
uint32_t nSequence;
CTxIn()
{
nSequence = std::numeric_limits<unsigned int>::max();
}
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max());
CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits<uint32_t>::max());
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(prevout);
READWRITE(scriptSig);
READWRITE(nSequence);
}
bool IsFinal() const
{
return (nSequence == std::numeric_limits<uint32_t>::max());
}
friend bool operator==(const CTxIn& a, const CTxIn& b)
{
return (a.prevout == b.prevout &&
a.scriptSig == b.scriptSig &&
a.nSequence == b.nSequence);
}
friend bool operator!=(const CTxIn& a, const CTxIn& b)
{
return !(a == b);
}
std::string ToString() const;
};
/** An output of a transaction. It contains the public key that the next input
* must be able to sign with to claim it.
*/
class CTxOut
{
public:
CAmount nValue;
CScript scriptPubKey;
CTxOut()
{
SetNull();
}
CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(nValue);
READWRITE(scriptPubKey);
}
void SetNull()
{
nValue = -1;
scriptPubKey.clear();
}
bool IsNull() const
{
return (nValue == -1);
}
uint256 GetHash() const;
bool IsDust(CFeeRate minRelayTxFee) const
{
// "Dust" is defined in terms of CTransaction::minRelayTxFee,
// which has units satoshis-per-kilobyte.
// If you'd pay more than 1/3 in fees
// to spend something, then we consider it dust.
// A typical txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend:
// so dust is a txout less than 546 satoshis
// with default minRelayTxFee.
size_t nSize = GetSerializeSize(SER_DISK,0)+148u;
return (nValue < 3*minRelayTxFee.GetFee(nSize));
}
friend bool operator==(const CTxOut& a, const CTxOut& b)
{
return (a.nValue == b.nValue &&
a.scriptPubKey == b.scriptPubKey);
}
friend bool operator!=(const CTxOut& a, const CTxOut& b)
{
return !(a == b);
}
std::string ToString() const;
};
struct CMutableTransaction;
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
*/
class CTransaction
{
private:
/** Memory only. */
const uint256 hash;
void UpdateHash() const;
public:
static const int32_t CURRENT_VERSION=1;
// The local variables are made const to prevent unintended modification
// without updating the cached hash value. However, CTransaction is not
// actually immutable; deserialization and assignment are implemented,
// and bypass the constness. This is safe, as they update the entire
// structure, including the hash.
const int32_t nVersion;
const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout;
const uint32_t nLockTime;
/** Construct a CTransaction that qualifies as IsNull() */
CTransaction();
/** Convert a CMutableTransaction into a CTransaction. */
CTransaction(const CMutableTransaction &tx);
CTransaction& operator=(const CTransaction& tx);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*const_cast<int32_t*>(&this->nVersion));
nVersion = this->nVersion;
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
READWRITE(*const_cast<uint32_t*>(&nLockTime));
if (ser_action.ForRead())
UpdateHash();
}
bool IsNull() const {
return vin.empty() && vout.empty();
}
const uint256& GetHash() const {
return hash;
}
// Return sum of txouts.
CAmount GetValueOut() const;
// GetValueIn() is a method on CCoinsViewCache, because
// inputs must be known to compute value in.
// Compute priority, given priority of inputs and (optionally) tx size
double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const;
// Compute modified tx size for priority calculation (optionally given tx size)
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
bool IsCoinBase() const
{
return (vin.size() == 1 && vin[0].prevout.IsNull());
}
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
return a.hash == b.hash;
}
friend bool operator!=(const CTransaction& a, const CTransaction& b)
{
return a.hash != b.hash;
}
std::string ToString() const;
};
/** A mutable version of CTransaction. */
struct CMutableTransaction
{
int32_t nVersion;
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
uint32_t nLockTime;
CMutableTransaction();
CMutableTransaction(const CTransaction& tx);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(vin);
READWRITE(vout);
READWRITE(nLockTime);
}
/** Compute the hash of this CMutableTransaction. This is computed on the
* fly, as opposed to GetHash() in CTransaction, which uses a cached result.
*/
uint256 GetHash() const;
};
#endif // BITCOIN_PRIMITIVES_TRANSACTION_H
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 71c6bd152..2dfded43b 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -1,144 +1,144 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "protocol.h"
#include "chainparams.h"
#include "util.h"
#include "utilstrencodings.h"
#ifndef WIN32
# include <arpa/inet.h>
#endif
static const char* ppszTypeName[] =
{
"ERROR",
"tx",
"block",
"filtered block"
};
CMessageHeader::CMessageHeader()
{
memcpy(pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE);
memset(pchCommand, 0, sizeof(pchCommand));
nMessageSize = -1;
nChecksum = 0;
}
CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)
{
memcpy(pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE);
memset(pchCommand, 0, sizeof(pchCommand));
strncpy(pchCommand, pszCommand, COMMAND_SIZE);
nMessageSize = nMessageSizeIn;
nChecksum = 0;
}
std::string CMessageHeader::GetCommand() const
{
return std::string(pchCommand, pchCommand + strnlen(pchCommand, COMMAND_SIZE));
}
bool CMessageHeader::IsValid() const
{
// Check start string
if (memcmp(pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0)
return false;
// Check the command string for errors
for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)
{
if (*p1 == 0)
{
// Must be all zeros after the first zero
for (; p1 < pchCommand + COMMAND_SIZE; p1++)
if (*p1 != 0)
return false;
}
else if (*p1 < ' ' || *p1 > 0x7E)
return false;
}
// Message size
if (nMessageSize > MAX_SIZE)
{
LogPrintf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand(), nMessageSize);
return false;
}
return true;
}
CAddress::CAddress() : CService()
{
Init();
}
CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn)
{
Init();
nServices = nServicesIn;
}
void CAddress::Init()
{
nServices = NODE_NETWORK;
nTime = 100000000;
nLastTry = 0;
}
CInv::CInv()
{
type = 0;
- hash = 0;
+ hash.SetNull();
}
CInv::CInv(int typeIn, const uint256& hashIn)
{
type = typeIn;
hash = hashIn;
}
CInv::CInv(const std::string& strType, const uint256& hashIn)
{
unsigned int i;
for (i = 1; i < ARRAYLEN(ppszTypeName); i++)
{
if (strType == ppszTypeName[i])
{
type = i;
break;
}
}
if (i == ARRAYLEN(ppszTypeName))
throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType));
hash = hashIn;
}
bool operator<(const CInv& a, const CInv& b)
{
return (a.type < b.type || (a.type == b.type && a.hash < b.hash));
}
bool CInv::IsKnownType() const
{
return (type >= 1 && type < (int)ARRAYLEN(ppszTypeName));
}
const char* CInv::GetCommand() const
{
if (!IsKnownType())
throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type));
return ppszTypeName[type];
}
std::string CInv::ToString() const
{
return strprintf("%s %s", GetCommand(), hash.ToString());
}
diff --git a/src/pubkey.h b/src/pubkey.h
index 69b73a4e1..b0768d4f4 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -1,206 +1,206 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PUBKEY_H
#define BITCOIN_PUBKEY_H
#include "hash.h"
#include "serialize.h"
#include "uint256.h"
#include <stdexcept>
#include <vector>
/**
* secp256k1:
* const unsigned int PRIVATE_KEY_SIZE = 279;
* const unsigned int PUBLIC_KEY_SIZE = 65;
* const unsigned int SIGNATURE_SIZE = 72;
*
* see www.keylength.com
* script supports up to 75 for single byte push
*/
/** A reference to a CKey: the Hash160 of its serialized public key */
class CKeyID : public uint160
{
public:
- CKeyID() : uint160(0) {}
+ CKeyID() : uint160() {}
CKeyID(const uint160& in) : uint160(in) {}
};
/** An encapsulated public key. */
class CPubKey
{
private:
/**
* Just store the serialized data.
* Its length can very cheaply be computed from the first byte.
*/
unsigned char vch[65];
//! Compute the length of a pubkey with a given first byte.
unsigned int static GetLen(unsigned char chHeader)
{
if (chHeader == 2 || chHeader == 3)
return 33;
if (chHeader == 4 || chHeader == 6 || chHeader == 7)
return 65;
return 0;
}
//! Set this key data to be invalid
void Invalidate()
{
vch[0] = 0xFF;
}
public:
//! Construct an invalid public key.
CPubKey()
{
Invalidate();
}
//! Initialize a public key using begin/end iterators to byte data.
template <typename T>
void Set(const T pbegin, const T pend)
{
int len = pend == pbegin ? 0 : GetLen(pbegin[0]);
if (len && len == (pend - pbegin))
memcpy(vch, (unsigned char*)&pbegin[0], len);
else
Invalidate();
}
//! Construct a public key using begin/end iterators to byte data.
template <typename T>
CPubKey(const T pbegin, const T pend)
{
Set(pbegin, pend);
}
//! Construct a public key from a byte vector.
CPubKey(const std::vector<unsigned char>& vch)
{
Set(vch.begin(), vch.end());
}
//! Simple read-only vector-like interface to the pubkey data.
unsigned int size() const { return GetLen(vch[0]); }
const unsigned char* begin() const { return vch; }
const unsigned char* end() const { return vch + size(); }
const unsigned char& operator[](unsigned int pos) const { return vch[pos]; }
//! Comparator implementation.
friend bool operator==(const CPubKey& a, const CPubKey& b)
{
return a.vch[0] == b.vch[0] &&
memcmp(a.vch, b.vch, a.size()) == 0;
}
friend bool operator!=(const CPubKey& a, const CPubKey& b)
{
return !(a == b);
}
friend bool operator<(const CPubKey& a, const CPubKey& b)
{
return a.vch[0] < b.vch[0] ||
(a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0);
}
//! Implement serialization, as if this was a byte vector.
unsigned int GetSerializeSize(int nType, int nVersion) const
{
return size() + 1;
}
template <typename Stream>
void Serialize(Stream& s, int nType, int nVersion) const
{
unsigned int len = size();
::WriteCompactSize(s, len);
s.write((char*)vch, len);
}
template <typename Stream>
void Unserialize(Stream& s, int nType, int nVersion)
{
unsigned int len = ::ReadCompactSize(s);
if (len <= 65) {
s.read((char*)vch, len);
} else {
// invalid pubkey, skip available data
char dummy;
while (len--)
s.read(&dummy, 1);
Invalidate();
}
}
//! Get the KeyID of this public key (hash of its serialization)
CKeyID GetID() const
{
return CKeyID(Hash160(vch, vch + size()));
}
//! Get the 256-bit hash of this public key.
uint256 GetHash() const
{
return Hash(vch, vch + size());
}
/*
* Check syntactic correctness.
*
* Note that this is consensus critical as CheckSig() calls it!
*/
bool IsValid() const
{
return size() > 0;
}
//! fully validate whether this is a valid public key (more expensive than IsValid())
bool IsFullyValid() const;
//! Check whether this is a compressed public key.
bool IsCompressed() const
{
return size() == 33;
}
/**
* Verify a DER signature (~72 bytes).
* If this public key is not fully valid, the return value will be false.
*/
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const;
//! Recover a public key from a compact signature.
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig);
//! Turn this public key into an uncompressed public key.
bool Decompress();
//! Derive BIP32 child pubkey.
bool Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const;
};
struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];
unsigned int nChild;
unsigned char vchChainCode[32];
CPubKey pubkey;
friend bool operator==(const CExtPubKey& a, const CExtPubKey& b)
{
return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
memcmp(&a.vchChainCode[0], &b.vchChainCode[0], 32) == 0 && a.pubkey == b.pubkey;
}
void Encode(unsigned char code[74]) const;
void Decode(const unsigned char code[74]);
bool Derive(CExtPubKey& out, unsigned int nChild) const;
};
#endif // BITCOIN_PUBKEY_H
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 60e7d62a7..8422f316f 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -1,811 +1,811 @@
// Copyright (c) 2011-2013 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 "coincontroldialog.h"
#include "ui_coincontroldialog.h"
#include "addresstablemodel.h"
#include "bitcoinunits.h"
#include "guiutil.h"
#include "init.h"
#include "optionsmodel.h"
#include "scicon.h"
#include "walletmodel.h"
#include "coincontrol.h"
#include "main.h"
#include "wallet.h"
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
#include <QApplication>
#include <QCheckBox>
#include <QCursor>
#include <QDialogButtonBox>
#include <QFlags>
#include <QIcon>
#include <QSettings>
#include <QString>
#include <QTreeWidget>
#include <QTreeWidgetItem>
using namespace std;
QList<CAmount> CoinControlDialog::payAmounts;
CCoinControl* CoinControlDialog::coinControl = new CCoinControl();
CoinControlDialog::CoinControlDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::CoinControlDialog),
model(0)
{
ui->setupUi(this);
// context menu actions
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this
lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this
unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
// context menu
contextMenu = new QMenu();
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction);
contextMenu->addAction(copyTransactionHashAction);
contextMenu->addSeparator();
contextMenu->addAction(lockAction);
contextMenu->addAction(unlockAction);
// context menu signals
connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash()));
connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin()));
connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin()));
// clipboard actions
QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity()));
connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount()));
connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee()));
connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee()));
connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes()));
connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority()));
connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput()));
connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange()));
ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
ui->labelCoinControlAmount->addAction(clipboardAmountAction);
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
// toggle tree/list mode
connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool)));
connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool)));
// click on checkbox
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int)));
// click on header
#if QT_VERSION < 0x050000
ui->treeWidget->header()->setClickable(true);
#else
ui->treeWidget->header()->setSectionsClickable(true);
#endif
connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int)));
// ok button
connect(ui->buttonBox, SIGNAL(clicked( QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*)));
// (un)select all
connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));
ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170);
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 110);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100);
ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100);
ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but dont show it
ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but dont show it
ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but dont show it
ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64 in this column, but dont show it
ui->treeWidget->setColumnHidden(COLUMN_DATE_INT64, true); // store date int64 in this column, but dont show it
// default view is sorted by amount desc
sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder);
// restore list mode and sortorder as a convenience feature
QSettings settings;
if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool())
ui->radioTreeMode->click();
if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
sortView(settings.value("nCoinControlSortColumn").toInt(), ((Qt::SortOrder)settings.value("nCoinControlSortOrder").toInt()));
}
CoinControlDialog::~CoinControlDialog()
{
QSettings settings;
settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
settings.setValue("nCoinControlSortColumn", sortColumn);
settings.setValue("nCoinControlSortOrder", (int)sortOrder);
delete ui;
}
void CoinControlDialog::setModel(WalletModel *model)
{
this->model = model;
if(model && model->getOptionsModel() && model->getAddressTableModel())
{
updateView();
updateLabelLocked();
CoinControlDialog::updateLabels(model, this);
}
}
// helper function str_pad
QString CoinControlDialog::strPad(QString s, int nPadLength, QString sPadding)
{
while (s.length() < nPadLength)
s = sPadding + s;
return s;
}
// ok button
void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
{
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
done(QDialog::Accepted); // closes the dialog
}
// (un)select all
void CoinControlDialog::buttonSelectAllClicked()
{
Qt::CheckState state = Qt::Checked;
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
{
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked)
{
state = Qt::Unchecked;
break;
}
}
ui->treeWidget->setEnabled(false);
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state)
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
ui->treeWidget->setEnabled(true);
if (state == Qt::Unchecked)
coinControl->UnSelectAll(); // just to be sure
CoinControlDialog::updateLabels(model, this);
}
// context menu
void CoinControlDialog::showMenu(const QPoint &point)
{
QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
if(item)
{
contextMenuItem = item;
// disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
{
copyTransactionHashAction->setEnabled(true);
- if (model->isLockedCoin(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))
+ if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))
{
lockAction->setEnabled(false);
unlockAction->setEnabled(true);
}
else
{
lockAction->setEnabled(true);
unlockAction->setEnabled(false);
}
}
else // this means click on parent node in tree mode -> disable all
{
copyTransactionHashAction->setEnabled(false);
lockAction->setEnabled(false);
unlockAction->setEnabled(false);
}
// show context menu
contextMenu->exec(QCursor::pos());
}
}
// context menu action: copy amount
void CoinControlDialog::copyAmount()
{
GUIUtil::setClipboard(BitcoinUnits::removeSpaces(contextMenuItem->text(COLUMN_AMOUNT)));
}
// context menu action: copy label
void CoinControlDialog::copyLabel()
{
if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent())
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_LABEL));
else
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_LABEL));
}
// context menu action: copy address
void CoinControlDialog::copyAddress()
{
if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent())
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_ADDRESS));
else
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_ADDRESS));
}
// context menu action: copy transaction id
void CoinControlDialog::copyTransactionHash()
{
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_TXHASH));
}
// context menu action: lock coin
void CoinControlDialog::lockCoin()
{
if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
- COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
model->lockCoin(outpt);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, SingleColorIcon(":/icons/lock_closed"));
updateLabelLocked();
}
// context menu action: unlock coin
void CoinControlDialog::unlockCoin()
{
- COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
model->unlockCoin(outpt);
contextMenuItem->setDisabled(false);
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
updateLabelLocked();
}
// copy label "Quantity" to clipboard
void CoinControlDialog::clipboardQuantity()
{
GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
}
// copy label "Amount" to clipboard
void CoinControlDialog::clipboardAmount()
{
GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
}
// copy label "Fee" to clipboard
void CoinControlDialog::clipboardFee()
{
GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace("~", ""));
}
// copy label "After fee" to clipboard
void CoinControlDialog::clipboardAfterFee()
{
GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace("~", ""));
}
// copy label "Bytes" to clipboard
void CoinControlDialog::clipboardBytes()
{
GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace("~", ""));
}
// copy label "Priority" to clipboard
void CoinControlDialog::clipboardPriority()
{
GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
}
// copy label "Dust" to clipboard
void CoinControlDialog::clipboardLowOutput()
{
GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
}
// copy label "Change" to clipboard
void CoinControlDialog::clipboardChange()
{
GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace("~", ""));
}
// treeview: sort
void CoinControlDialog::sortView(int column, Qt::SortOrder order)
{
sortColumn = column;
sortOrder = order;
ui->treeWidget->sortItems(column, order);
ui->treeWidget->header()->setSortIndicator(getMappedColumn(sortColumn), sortOrder);
}
// treeview: clicked on header
void CoinControlDialog::headerSectionClicked(int logicalIndex)
{
if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing
{
ui->treeWidget->header()->setSortIndicator(getMappedColumn(sortColumn), sortOrder);
}
else
{
logicalIndex = getMappedColumn(logicalIndex, false);
if (sortColumn == logicalIndex)
sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
else
{
sortColumn = logicalIndex;
sortOrder = ((sortColumn == COLUMN_LABEL || sortColumn == COLUMN_ADDRESS) ? Qt::AscendingOrder : Qt::DescendingOrder); // if label or address then default => asc, else default => desc
}
sortView(sortColumn, sortOrder);
}
}
// toggle tree mode
void CoinControlDialog::radioTreeMode(bool checked)
{
if (checked && model)
updateView();
}
// toggle list mode
void CoinControlDialog::radioListMode(bool checked)
{
if (checked && model)
updateView();
}
// checkbox clicked by user
void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
{
if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
{
- COutPoint outpt(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt());
+ COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt());
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
coinControl->UnSelect(outpt);
else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
else
coinControl->Select(outpt);
// selection changed -> update labels
if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
CoinControlDialog::updateLabels(model, this);
}
// todo: this is a temporary qt5 fix: when clicking a parent node in tree mode, the parent node
// including all childs are partially selected. But the parent node should be fully selected
// as well as the childs. Childs should never be partially selected in the first place.
// Please remove this ugly fix, once the bug is solved upstream.
#if QT_VERSION >= 0x050000
else if (column == COLUMN_CHECKBOX && item->childCount() > 0)
{
if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked && item->child(0)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
item->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
#endif
}
// return human readable label for priority number
QString CoinControlDialog::getPriorityLabel(double dPriority, double mempoolEstimatePriority)
{
double dPriorityMedium = mempoolEstimatePriority;
if (dPriorityMedium <= 0)
dPriorityMedium = AllowFreeThreshold(); // not enough data, back to hard-coded
if (dPriority / 1000000 > dPriorityMedium) return tr("highest");
else if (dPriority / 100000 > dPriorityMedium) return tr("higher");
else if (dPriority / 10000 > dPriorityMedium) return tr("high");
else if (dPriority / 1000 > dPriorityMedium) return tr("medium-high");
else if (dPriority > dPriorityMedium) return tr("medium");
else if (dPriority * 10 > dPriorityMedium) return tr("low-medium");
else if (dPriority * 100 > dPriorityMedium) return tr("low");
else if (dPriority * 1000 > dPriorityMedium) return tr("lower");
else return tr("lowest");
}
// shows count of locked unspent outputs
void CoinControlDialog::updateLabelLocked()
{
vector<COutPoint> vOutpts;
model->listLockedCoins(vOutpts);
if (vOutpts.size() > 0)
{
ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
ui->labelLocked->setVisible(true);
}
else ui->labelLocked->setVisible(false);
}
void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
if (!model)
return;
// nPayAmount
CAmount nPayAmount = 0;
bool fDust = false;
CMutableTransaction txDummy;
foreach(const CAmount &amount, CoinControlDialog::payAmounts)
{
nPayAmount += amount;
if (amount > 0)
{
CTxOut txout(amount, (CScript)vector<unsigned char>(24, 0));
txDummy.vout.push_back(txout);
if (txout.IsDust(::minRelayTxFee))
fDust = true;
}
}
QString sPriorityLabel = tr("none");
CAmount nAmount = 0;
CAmount nPayFee = 0;
CAmount nAfterFee = 0;
CAmount nChange = 0;
unsigned int nBytes = 0;
unsigned int nBytesInputs = 0;
double dPriority = 0;
double dPriorityInputs = 0;
unsigned int nQuantity = 0;
int nQuantityUncompressed = 0;
bool fAllowFree = false;
vector<COutPoint> vCoinControl;
vector<COutput> vOutputs;
coinControl->ListSelected(vCoinControl);
model->getOutputs(vCoinControl, vOutputs);
BOOST_FOREACH(const COutput& out, vOutputs)
{
// unselect already spent, very unlikely scenario, this could happen
// when selected are spent elsewhere, like rpc or another computer
uint256 txhash = out.tx->GetHash();
COutPoint outpt(txhash, out.i);
if (model->isSpent(outpt))
{
coinControl->UnSelect(outpt);
continue;
}
// Quantity
nQuantity++;
// Amount
nAmount += out.tx->vout[out.i].nValue;
// Priority
dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
// Bytes
CTxDestination address;
if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
{
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&address);
if (keyid && model->getPubKey(*keyid, pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
if (!pubkey.IsCompressed())
nQuantityUncompressed++;
}
else
nBytesInputs += 148; // in all error cases, simply assume 148 here
}
else nBytesInputs += 148;
}
// calculation
if (nQuantity > 0)
{
// Bytes
nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
// Priority
double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority);
// Fee
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
// Allow free?
double dPriorityNeeded = mempoolEstimatePriority;
if (dPriorityNeeded <= 0)
dPriorityNeeded = AllowFreeThreshold(); // not enough data, back to hard-coded
fAllowFree = (dPriority >= dPriorityNeeded);
if (fSendFreeTransactions)
if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
nPayFee = 0;
if (nPayAmount > 0)
{
nChange = nAmount - nPayFee - nPayAmount;
// Never create dust outputs; if we would, just add the dust to the fee.
if (nChange > 0 && nChange < CENT)
{
CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0));
if (txout.IsDust(::minRelayTxFee))
{
nPayFee += nChange;
nChange = 0;
}
}
if (nChange == 0)
nBytes -= 34;
}
// after fee
nAfterFee = nAmount - nPayFee;
if (nAfterFee < 0)
nAfterFee = 0;
}
// actually update labels
int nDisplayUnit = BitcoinUnits::BTC;
if (model && model->getOptionsModel())
nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
QLabel *l6 = dialog->findChild<QLabel *>("labelCoinControlPriority");
QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
// enable/disable "dust" and "change"
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0);
// stats
l1->setText(QString::number(nQuantity)); // Quantity
l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount
l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee
l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee
l5->setText(((nBytes > 0) ? "~" : "") + QString::number(nBytes)); // Bytes
l6->setText(sPriorityLabel); // Priority
l7->setText(fDust ? tr("yes") : tr("no")); // Dust
l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
if (nPayFee > 0 && !(payTxFee.GetFeePerK() > 0 && fPayAtLeastCustomFee && nBytes < 1000))
{
l3->setText("~" + l3->text());
l4->setText("~" + l4->text());
if (nChange > 0)
l8->setText("~" + l8->text());
}
// turn labels "red"
l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000
l6->setStyleSheet((dPriority > 0 && !fAllowFree) ? "color:red;" : ""); // Priority < "medium"
l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes"
// tool tips
QString toolTip1 = tr("This label turns red, if the transaction size is greater than 1000 bytes.") + "<br /><br />";
toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::minTxFee.GetFeePerK())) + "<br /><br />";
toolTip1 += tr("Can vary +/- 1 byte per input.");
QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "<br /><br />";
toolTip2 += tr("This label turns red, if the priority is smaller than \"medium\".") + "<br /><br />";
toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::minTxFee.GetFeePerK()));
QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, ::minRelayTxFee.GetFee(546)));
// how many satoshis the estimated fee can vary per byte we guess wrong
double dFeeVary;
if (payTxFee.GetFeePerK() > 0)
dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), payTxFee.GetFeePerK()) / 1000;
else
dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), mempool.estimateFee(nTxConfirmTarget).GetFeePerK()) / 1000;
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
l4->setToolTip(toolTip4);
l5->setToolTip(toolTip1);
l6->setToolTip(toolTip2);
l7->setToolTip(toolTip3);
l8->setToolTip(toolTip4);
dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip());
dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip());
dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip());
dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip());
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
// Insufficient funds
QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
if (label)
label->setVisible(nChange < 0);
}
void CoinControlDialog::updateView()
{
if (!model || !model->getOptionsModel() || !model->getAddressTableModel())
return;
bool treeMode = ui->radioTreeMode->isChecked();
ui->treeWidget->clear();
ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
ui->treeWidget->setAlternatingRowColors(!treeMode);
QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
map<QString, vector<COutput> > mapCoins;
model->listCoins(mapCoins);
BOOST_FOREACH(PAIRTYPE(QString, vector<COutput>) coins, mapCoins)
{
QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem();
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
QString sWalletAddress = coins.first;
QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
if (sWalletLabel.isEmpty())
sWalletLabel = tr("(no label)");
if (treeMode)
{
// wallet address
ui->treeWidget->addTopLevelItem(itemWalletAddress);
itemWalletAddress->setFlags(flgTristate);
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
// label
itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
// address
itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
}
CAmount nSum = 0;
double dPrioritySum = 0;
int nChildren = 0;
int nInputSum = 0;
BOOST_FOREACH(const COutput& out, coins.second)
{
int nInputSize = 0;
nSum += out.tx->vout[out.i].nValue;
nChildren++;
QTreeWidgetItem *itemOutput;
if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress);
else itemOutput = new QTreeWidgetItem(ui->treeWidget);
itemOutput->setFlags(flgCheckbox);
itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
// address
CTxDestination outputAddress;
QString sAddress = "";
if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress))
{
sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString());
// if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress)))
itemOutput->setText(COLUMN_ADDRESS, sAddress);
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&outputAddress);
if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed())
nInputSize = 29; // 29 = 180 - 151 (public key is 180 bytes, priority free area is 151 bytes)
}
// label
if (!(sAddress == sWalletAddress)) // change
{
// tooltip from where the change comes from
itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
itemOutput->setText(COLUMN_LABEL, tr("(change)"));
}
else if (!treeMode)
{
QString sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
if (sLabel.isEmpty())
sLabel = tr("(no label)");
itemOutput->setText(COLUMN_LABEL, sLabel);
}
// amount
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly
// date
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
itemOutput->setText(COLUMN_DATE_INT64, strPad(QString::number(out.tx->GetTxTime()), 20, " "));
// confirmations
itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
// priority
double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority));
itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
nInputSum += nInputSize;
// transaction hash
uint256 txhash = out.tx->GetHash();
itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex()));
// vout index
itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
// disable locked coins
if (model->isLockedCoin(txhash, out.i))
{
COutPoint outpt(txhash, out.i);
coinControl->UnSelect(outpt); // just to be sure
itemOutput->setDisabled(true);
itemOutput->setIcon(COLUMN_CHECKBOX, SingleColorIcon(":/icons/lock_closed"));
}
// set checkbox
if (coinControl->IsSelected(txhash, out.i))
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
// amount
if (treeMode)
{
dPrioritySum = dPrioritySum / (nInputSum + 78);
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum, mempoolEstimatePriority));
itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " "));
}
}
// expand all partially selected
if (treeMode)
{
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
ui->treeWidget->topLevelItem(i)->setExpanded(true);
}
// sort view
sortView(sortColumn, sortOrder);
ui->treeWidget->setEnabled(true);
}
diff --git a/src/rest.cpp b/src/rest.cpp
index 4702f0d70..1ee1d5291 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -1,317 +1,317 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "main.h"
#include "rpcserver.h"
#include "streams.h"
#include "sync.h"
#include "utilstrencodings.h"
#include "version.h"
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace json_spirit;
enum RetFormat {
RF_UNDEF,
RF_BINARY,
RF_HEX,
RF_JSON,
};
static const struct {
enum RetFormat rf;
const char* name;
} rf_names[] = {
{RF_UNDEF, ""},
{RF_BINARY, "bin"},
{RF_HEX, "hex"},
{RF_JSON, "json"},
};
class RestErr
{
public:
enum HTTPStatusCode status;
string message;
};
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
static RestErr RESTERR(enum HTTPStatusCode status, string message)
{
RestErr re;
re.status = status;
re.message = message;
return re;
}
static enum RetFormat ParseDataFormat(vector<string>& params, const string strReq)
{
boost::split(params, strReq, boost::is_any_of("."));
if (params.size() > 1) {
for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
if (params[1] == rf_names[i].name)
return rf_names[i].rf;
}
return rf_names[0].rf;
}
static string AvailableDataFormatsString()
{
string formats = "";
for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
if (strlen(rf_names[i].name) > 0) {
formats.append(".");
formats.append(rf_names[i].name);
formats.append(", ");
}
if (formats.length() > 0)
return formats.substr(0, formats.length() - 2);
return formats;
}
static bool ParseHashStr(const string& strReq, uint256& v)
{
if (!IsHex(strReq) || (strReq.size() != 64))
return false;
v.SetHex(strReq);
return true;
}
static bool rest_headers(AcceptedConnection* conn,
const std::string& strReq,
const std::map<std::string, std::string>& mapHeaders,
bool fRun)
{
vector<string> params;
enum RetFormat rf = ParseDataFormat(params, strReq);
vector<string> path;
boost::split(path, params[0], boost::is_any_of("/"));
if (path.size() != 2)
throw RESTERR(HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
long count = strtol(path[0].c_str(), NULL, 10);
if (count < 1 || count > 2000)
throw RESTERR(HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
string hashStr = path[1];
uint256 hash;
if (!ParseHashStr(hashStr, hash))
throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
std::vector<CBlockHeader> headers;
headers.reserve(count);
{
LOCK(cs_main);
BlockMap::const_iterator it = mapBlockIndex.find(hash);
const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
while (pindex != NULL && chainActive.Contains(pindex)) {
headers.push_back(pindex->GetBlockHeader());
if (headers.size() == (unsigned long)count)
break;
pindex = chainActive.Next(pindex);
}
}
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
BOOST_FOREACH(const CBlockHeader &header, headers) {
ssHeader << header;
}
switch (rf) {
case RF_BINARY: {
string binaryHeader = ssHeader.str();
conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryHeader.size(), "application/octet-stream") << binaryHeader << std::flush;
return true;
}
case RF_HEX: {
string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
return true;
}
default: {
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
}
}
// not reached
return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_block(AcceptedConnection* conn,
const std::string& strReq,
const std::map<std::string, std::string>& mapHeaders,
bool fRun,
bool showTxDetails)
{
vector<string> params;
enum RetFormat rf = ParseDataFormat(params, strReq);
string hashStr = params[0];
uint256 hash;
if (!ParseHashStr(hashStr, hash))
throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
CBlock block;
CBlockIndex* pblockindex = NULL;
{
LOCK(cs_main);
if (mapBlockIndex.count(hash) == 0)
throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
pblockindex = mapBlockIndex[hash];
if (!ReadBlockFromDisk(block, pblockindex))
throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
}
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << block;
switch (rf) {
case RF_BINARY: {
string binaryBlock = ssBlock.str();
conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryBlock.size(), "application/octet-stream") << binaryBlock << std::flush;
return true;
}
case RF_HEX: {
string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
return true;
}
case RF_JSON: {
Object objBlock = blockToJSON(block, pblockindex, showTxDetails);
string strJSON = write_string(Value(objBlock), false) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
return true;
}
default: {
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
}
}
// not reached
return true; // continue to process further HTTP reqs on this cxn
}
static bool rest_block_extended(AcceptedConnection* conn,
const std::string& strReq,
const std::map<std::string, std::string>& mapHeaders,
bool fRun)
{
return rest_block(conn, strReq, mapHeaders, fRun, true);
}
static bool rest_block_notxdetails(AcceptedConnection* conn,
const std::string& strReq,
const std::map<std::string, std::string>& mapHeaders,
bool fRun)
{
return rest_block(conn, strReq, mapHeaders, fRun, false);
}
static bool rest_tx(AcceptedConnection* conn,
const std::string& strReq,
const std::map<std::string, std::string>& mapHeaders,
bool fRun)
{
vector<string> params;
enum RetFormat rf = ParseDataFormat(params, strReq);
string hashStr = params[0];
uint256 hash;
if (!ParseHashStr(hashStr, hash))
throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
CTransaction tx;
- uint256 hashBlock = 0;
+ uint256 hashBlock = uint256();
if (!GetTransaction(hash, tx, hashBlock, true))
throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << tx;
switch (rf) {
case RF_BINARY: {
string binaryTx = ssTx.str();
conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryTx.size(), "application/octet-stream") << binaryTx << std::flush;
return true;
}
case RF_HEX: {
string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
return true;
}
case RF_JSON: {
Object objTx;
TxToJSON(tx, hashBlock, objTx);
string strJSON = write_string(Value(objTx), false) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
return true;
}
default: {
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
}
}
// not reached
return true; // continue to process further HTTP reqs on this cxn
}
static const struct {
const char* prefix;
bool (*handler)(AcceptedConnection* conn,
const std::string& strURI,
const std::map<std::string, std::string>& mapHeaders,
bool fRun);
} uri_prefixes[] = {
{"/rest/tx/", rest_tx},
{"/rest/block/notxdetails/", rest_block_notxdetails},
{"/rest/block/", rest_block_extended},
{"/rest/headers/", rest_headers},
};
bool HTTPReq_REST(AcceptedConnection* conn,
const std::string& strURI,
const std::map<std::string, std::string>& mapHeaders,
bool fRun)
{
try {
std::string statusmessage;
if (RPCIsInWarmup(&statusmessage))
throw RESTERR(HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) {
unsigned int plen = strlen(uri_prefixes[i].prefix);
if (strURI.substr(0, plen) == uri_prefixes[i].prefix) {
string strReq = strURI.substr(plen);
return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun);
}
}
} catch (const RestErr& re) {
conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush;
return false;
}
conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
return false;
}
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index 71fb7c823..cfc559d19 100644
--- a/src/rpcblockchain.cpp
+++ b/src/rpcblockchain.cpp
@@ -1,682 +1,682 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "checkpoints.h"
#include "main.h"
#include "rpcserver.h"
#include "sync.h"
#include "util.h"
#include <stdint.h>
#include "json/json_spirit_value.h"
using namespace json_spirit;
using namespace std;
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex);
double GetDifficulty(const CBlockIndex* blockindex)
{
// Floating point number that is a multiple of the minimum difficulty,
// minimum difficulty = 1.0.
if (blockindex == NULL)
{
if (chainActive.Tip() == NULL)
return 1.0;
else
blockindex = chainActive.Tip();
}
int nShift = (blockindex->nBits >> 24) & 0xff;
double dDiff =
(double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff);
while (nShift < 29)
{
dDiff *= 256.0;
nShift++;
}
while (nShift > 29)
{
dDiff /= 256.0;
nShift--;
}
return dDiff;
}
Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
{
Object result;
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;
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()));
Array txs;
BOOST_FOREACH(const CTransaction&tx, block.vtx)
{
if(txDetails)
{
Object objTx;
- TxToJSON(tx, uint256(0), objTx);
+ TxToJSON(tx, uint256(), objTx);
txs.push_back(objTx);
}
else
txs.push_back(tx.GetHash().GetHex());
}
result.push_back(Pair("tx", txs));
result.push_back(Pair("time", block.GetBlockTime()));
result.push_back(Pair("nonce", (uint64_t)block.nNonce));
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;
}
Value getblockcount(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getblockcount\n"
"\nReturns the number of blocks in the longest block chain.\n"
"\nResult:\n"
"n (numeric) The current block count\n"
"\nExamples:\n"
+ HelpExampleCli("getblockcount", "")
+ HelpExampleRpc("getblockcount", "")
);
return chainActive.Height();
}
Value getbestblockhash(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getbestblockhash\n"
"\nReturns the hash of the best (tip) block in the longest block chain.\n"
"\nResult\n"
"\"hex\" (string) the block hash hex encoded\n"
"\nExamples\n"
+ HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
);
return chainActive.Tip()->GetBlockHash().GetHex();
}
Value getdifficulty(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getdifficulty\n"
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
"\nResult:\n"
"n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
"\nExamples:\n"
+ HelpExampleCli("getdifficulty", "")
+ HelpExampleRpc("getdifficulty", "")
);
return GetDifficulty();
}
Value getrawmempool(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"getrawmempool ( verbose )\n"
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
"\nArguments:\n"
"1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n"
"\nResult: (for verbose = false):\n"
"[ (json array of string)\n"
" \"transactionid\" (string) The transaction id\n"
" ,...\n"
"]\n"
"\nResult: (for verbose = true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n"
" \"size\" : n, (numeric) transaction size in bytes\n"
" \"fee\" : n, (numeric) transaction fee in bitcoins\n"
" \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
" \"height\" : n, (numeric) block height when transaction entered pool\n"
" \"startingpriority\" : n, (numeric) priority when transaction entered pool\n"
" \"currentpriority\" : n, (numeric) transaction priority now\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
" ... ]\n"
" }, ...\n"
"]\n"
"\nExamples\n"
+ HelpExampleCli("getrawmempool", "true")
+ HelpExampleRpc("getrawmempool", "true")
);
bool fVerbose = false;
if (params.size() > 0)
fVerbose = params[0].get_bool();
if (fVerbose)
{
LOCK(mempool.cs);
Object o;
BOOST_FOREACH(const PAIRTYPE(uint256, CTxMemPoolEntry)& entry, mempool.mapTx)
{
const uint256& hash = entry.first;
const CTxMemPoolEntry& e = entry.second;
Object info;
info.push_back(Pair("size", (int)e.GetTxSize()));
info.push_back(Pair("fee", ValueFromAmount(e.GetFee())));
info.push_back(Pair("time", e.GetTime()));
info.push_back(Pair("height", (int)e.GetHeight()));
info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight())));
info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height())));
const CTransaction& tx = e.GetTx();
set<string> setDepends;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
if (mempool.exists(txin.prevout.hash))
setDepends.insert(txin.prevout.hash.ToString());
}
Array depends(setDepends.begin(), setDepends.end());
info.push_back(Pair("depends", depends));
o.push_back(Pair(hash.ToString(), info));
}
return o;
}
else
{
vector<uint256> vtxid;
mempool.queryHashes(vtxid);
Array a;
BOOST_FOREACH(const uint256& hash, vtxid)
a.push_back(hash.ToString());
return a;
}
}
Value getblockhash(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"getblockhash index\n"
"\nReturns hash of block in best-block-chain at index provided.\n"
"\nArguments:\n"
"1. index (numeric, required) The block index\n"
"\nResult:\n"
"\"hash\" (string) The block hash\n"
"\nExamples:\n"
+ HelpExampleCli("getblockhash", "1000")
+ HelpExampleRpc("getblockhash", "1000")
);
int nHeight = params[0].get_int();
if (nHeight < 0 || nHeight > chainActive.Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
CBlockIndex* pblockindex = chainActive[nHeight];
return pblockindex->GetBlockHash().GetHex();
}
Value getblock(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"getblock \"hash\" ( verbose )\n"
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
"If verbose is true, returns an Object with information about block <hash>.\n"
"\nArguments:\n"
"1. \"hash\" (string, required) The block hash\n"
"2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n"
"\nResult (for verbose = true):\n"
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
" \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
" \"size\" : n, (numeric) The block size\n"
" \"height\" : n, (numeric) The block height or index\n"
" \"version\" : n, (numeric) The block version\n"
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
" \"tx\" : [ (array of string) The transaction ids\n"
" \"transactionid\" (string) The transaction id\n"
" ,...\n"
" ],\n"
" \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"nonce\" : n, (numeric) The nonce\n"
" \"bits\" : \"1d00ffff\", (string) The bits\n"
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
" \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
" \"nextblockhash\" : \"hash\" (string) The hash of the next block\n"
"}\n"
"\nResult (for verbose=false):\n"
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
"\nExamples:\n"
+ HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
);
std::string strHash = params[0].get_str();
- uint256 hash(strHash);
+ uint256 hash(uint256S(strHash));
bool fVerbose = true;
if (params.size() > 1)
fVerbose = params[1].get_bool();
if (mapBlockIndex.count(hash) == 0)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
CBlock block;
CBlockIndex* pblockindex = mapBlockIndex[hash];
if(!ReadBlockFromDisk(block, pblockindex))
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
if (!fVerbose)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << block;
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
return strHex;
}
return blockToJSON(block, pblockindex);
}
Value gettxoutsetinfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"gettxoutsetinfo\n"
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n"
"\nResult:\n"
"{\n"
" \"height\":n, (numeric) The current block height (index)\n"
" \"bestblock\": \"hex\", (string) the best block hash hex\n"
" \"transactions\": n, (numeric) The number of transactions\n"
" \"txouts\": n, (numeric) The number of output transactions\n"
" \"bytes_serialized\": n, (numeric) The serialized size\n"
" \"hash_serialized\": \"hash\", (string) The serialized hash\n"
" \"total_amount\": x.xxx (numeric) The total amount\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("gettxoutsetinfo", "")
+ HelpExampleRpc("gettxoutsetinfo", "")
);
Object ret;
CCoinsStats stats;
FlushStateToDisk();
if (pcoinsTip->GetStats(stats)) {
ret.push_back(Pair("height", (int64_t)stats.nHeight));
ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
ret.push_back(Pair("transactions", (int64_t)stats.nTransactions));
ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs));
ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize));
ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex()));
ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
}
return ret;
}
Value gettxout(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 3)
throw runtime_error(
"gettxout \"txid\" n ( includemempool )\n"
"\nReturns details about an unspent transaction output.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. n (numeric, required) vout value\n"
"3. includemempool (boolean, optional) Whether to included the mem pool\n"
"\nResult:\n"
"{\n"
" \"bestblock\" : \"hash\", (string) the block hash\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"value\" : x.xxx, (numeric) The transaction value in btc\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"code\", (string) \n"
" \"hex\" : \"hex\", (string) \n"
" \"reqSigs\" : n, (numeric) Number of required signatures\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n"
" \"addresses\" : [ (array of string) array of bitcoin addresses\n"
" \"bitcoinaddress\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
" },\n"
" \"version\" : n, (numeric) The version\n"
" \"coinbase\" : true|false (boolean) Coinbase or not\n"
"}\n"
"\nExamples:\n"
"\nGet unspent transactions\n"
+ HelpExampleCli("listunspent", "") +
"\nView the details\n"
+ HelpExampleCli("gettxout", "\"txid\" 1") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("gettxout", "\"txid\", 1")
);
Object ret;
std::string strHash = params[0].get_str();
- uint256 hash(strHash);
+ uint256 hash(uint256S(strHash));
int n = params[1].get_int();
bool fMempool = true;
if (params.size() > 2)
fMempool = params[2].get_bool();
CCoins coins;
if (fMempool) {
LOCK(mempool.cs);
CCoinsViewMemPool view(pcoinsTip, mempool);
if (!view.GetCoins(hash, coins))
return Value::null;
mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool
} else {
if (!pcoinsTip->GetCoins(hash, coins))
return Value::null;
}
if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull())
return Value::null;
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
CBlockIndex *pindex = it->second;
ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex()));
if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT)
ret.push_back(Pair("confirmations", 0));
else
ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1));
ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue)));
Object o;
ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true);
ret.push_back(Pair("scriptPubKey", o));
ret.push_back(Pair("version", coins.nVersion));
ret.push_back(Pair("coinbase", coins.fCoinBase));
return ret;
}
Value verifychain(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 2)
throw runtime_error(
"verifychain ( checklevel numblocks )\n"
"\nVerifies blockchain database.\n"
"\nArguments:\n"
"1. checklevel (numeric, optional, 0-4, default=3) How thorough the block verification is.\n"
"2. numblocks (numeric, optional, default=288, 0=all) The number of blocks to check.\n"
"\nResult:\n"
"true|false (boolean) Verified or not\n"
"\nExamples:\n"
+ HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
);
int nCheckLevel = GetArg("-checklevel", 3);
int nCheckDepth = GetArg("-checkblocks", 288);
if (params.size() > 0)
nCheckLevel = params[0].get_int();
if (params.size() > 1)
nCheckDepth = params[1].get_int();
return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth);
}
Value getblockchaininfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getblockchaininfo\n"
"Returns an object containing various state info regarding block chain processing.\n"
"\nResult:\n"
"{\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
" \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
" \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n"
" \"bestblockhash\": \"...\", (string) the hash of the currently best block\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n"
" \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getblockchaininfo", "")
+ HelpExampleRpc("getblockchaininfo", "")
);
Object obj;
obj.push_back(Pair("chain", Params().NetworkIDString()));
obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1));
obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(chainActive.Tip())));
obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
return obj;
}
/** Comparison function for sorting the getchaintips heads. */
struct CompareBlocksByHeight
{
bool operator()(const CBlockIndex* a, const CBlockIndex* b) const
{
/* Make sure that unequal blocks with the same height do not compare
equal. Use the pointers themselves to make a distinction. */
if (a->nHeight != b->nHeight)
return (a->nHeight > b->nHeight);
return a < b;
}
};
Value getchaintips(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getchaintips\n"
"Return information about all known tips in the block tree,"
" including the main chain as well as orphaned branches.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"height\": xxxx, (numeric) height of the chain tip\n"
" \"hash\": \"xxxx\", (string) block hash of the tip\n"
" \"branchlen\": 0 (numeric) zero for main chain\n"
" \"status\": \"active\" (string) \"active\" for the main chain\n"
" },\n"
" {\n"
" \"height\": xxxx,\n"
" \"hash\": \"xxxx\",\n"
" \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n"
" \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n"
" }\n"
"]\n"
"Possible values for status:\n"
"1. \"invalid\" This branch contains at least one invalid block\n"
"2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n"
"3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n"
"4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n"
"5. \"active\" This is the tip of the active main chain, which is certainly valid\n"
"\nExamples:\n"
+ HelpExampleCli("getchaintips", "")
+ HelpExampleRpc("getchaintips", "")
);
/* Build up a list of chain tips. We start with the list of all
known blocks, and successively remove blocks that appear as pprev
of another block. */
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex)
setTips.insert(item.second);
BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex)
{
const CBlockIndex* pprev = item.second->pprev;
if (pprev)
setTips.erase(pprev);
}
// Always report the currently active tip.
setTips.insert(chainActive.Tip());
/* Construct the output array. */
Array res;
BOOST_FOREACH(const CBlockIndex* block, setTips)
{
Object obj;
obj.push_back(Pair("height", block->nHeight));
obj.push_back(Pair("hash", block->phashBlock->GetHex()));
const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight;
obj.push_back(Pair("branchlen", branchLen));
string status;
if (chainActive.Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {
// This block or one of its ancestors is invalid.
status = "invalid";
} else if (block->nChainTx == 0) {
// This block cannot be connected because full block data for it or one of its parents is missing.
status = "headers-only";
} else if (block->IsValid(BLOCK_VALID_SCRIPTS)) {
// This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized.
status = "valid-fork";
} else if (block->IsValid(BLOCK_VALID_TREE)) {
// The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain.
status = "valid-headers";
} else {
// No clue.
status = "unknown";
}
obj.push_back(Pair("status", status));
res.push_back(obj);
}
return res;
}
Value getmempoolinfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getmempoolinfo\n"
"\nReturns details on the active state of the TX memory pool.\n"
"\nResult:\n"
"{\n"
" \"size\": xxxxx (numeric) Current tx count\n"
" \"bytes\": xxxxx (numeric) Sum of all tx sizes\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getmempoolinfo", "")
+ HelpExampleRpc("getmempoolinfo", "")
);
Object ret;
ret.push_back(Pair("size", (int64_t) mempool.size()));
ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize()));
return ret;
}
Value invalidateblock(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"invalidateblock \"hash\"\n"
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n"
"\nArguments:\n"
"1. hash (string, required) the hash of the block to mark as invalid\n"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("invalidateblock", "\"blockhash\"")
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
);
std::string strHash = params[0].get_str();
- uint256 hash(strHash);
+ uint256 hash(uint256S(strHash));
CValidationState state;
{
LOCK(cs_main);
if (mapBlockIndex.count(hash) == 0)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
CBlockIndex* pblockindex = mapBlockIndex[hash];
InvalidateBlock(state, pblockindex);
}
if (state.IsValid()) {
ActivateBestChain(state);
}
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
}
return Value::null;
}
Value reconsiderblock(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"reconsiderblock \"hash\"\n"
"\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
"This can be used to undo the effects of invalidateblock.\n"
"\nArguments:\n"
"1. hash (string, required) the hash of the block to reconsider\n"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("reconsiderblock", "\"blockhash\"")
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
);
std::string strHash = params[0].get_str();
- uint256 hash(strHash);
+ uint256 hash(uint256S(strHash));
CValidationState state;
{
LOCK(cs_main);
if (mapBlockIndex.count(hash) == 0)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
CBlockIndex* pblockindex = mapBlockIndex[hash];
ReconsiderBlock(state, pblockindex);
}
if (state.IsValid()) {
ActivateBestChain(state);
}
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
}
return Value::null;
}
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index 35760f9af..603e2935d 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -1,728 +1,728 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "amount.h"
#include "chainparams.h"
#include "core_io.h"
#include "init.h"
#include "net.h"
#include "main.h"
#include "miner.h"
#include "pow.h"
#include "rpcserver.h"
#include "util.h"
#ifdef ENABLE_WALLET
#include "db.h"
#include "wallet.h"
#endif
#include <stdint.h>
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
using namespace json_spirit;
using namespace std;
/**
* Return average network hashes per second based on the last 'lookup' blocks,
* or from the last difficulty change if 'lookup' is nonpositive.
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
*/
Value GetNetworkHashPS(int lookup, int height) {
CBlockIndex *pb = chainActive.Tip();
if (height >= 0 && height < chainActive.Height())
pb = chainActive[height];
if (pb == NULL || !pb->nHeight)
return 0;
// If lookup is -1, then use blocks since last difficulty change.
if (lookup <= 0)
lookup = pb->nHeight % 2016 + 1;
// If lookup is larger than chain, then set it to chain length.
if (lookup > pb->nHeight)
lookup = pb->nHeight;
CBlockIndex *pb0 = pb;
int64_t minTime = pb0->GetBlockTime();
int64_t maxTime = minTime;
for (int i = 0; i < lookup; i++) {
pb0 = pb0->pprev;
int64_t time = pb0->GetBlockTime();
minTime = std::min(time, minTime);
maxTime = std::max(time, maxTime);
}
// In case there's a situation where minTime == maxTime, we don't want a divide by zero exception.
if (minTime == maxTime)
return 0;
- uint256 workDiff = pb->nChainWork - pb0->nChainWork;
+ arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork;
int64_t timeDiff = maxTime - minTime;
return (int64_t)(workDiff.getdouble() / timeDiff);
}
Value getnetworkhashps(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 2)
throw runtime_error(
"getnetworkhashps ( blocks height )\n"
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n"
"\nArguments:\n"
"1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n"
"2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n"
"\nResult:\n"
"x (numeric) Hashes per second estimated\n"
"\nExamples:\n"
+ HelpExampleCli("getnetworkhashps", "")
+ HelpExampleRpc("getnetworkhashps", "")
);
return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1);
}
#ifdef ENABLE_WALLET
Value getgenerate(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getgenerate\n"
"\nReturn if the server is set to generate coins or not. The default is false.\n"
"It is set with the command line argument -gen (or bitcoin.conf setting gen)\n"
"It can also be set with the setgenerate call.\n"
"\nResult\n"
"true|false (boolean) If the server is set to generate coins or not\n"
"\nExamples:\n"
+ HelpExampleCli("getgenerate", "")
+ HelpExampleRpc("getgenerate", "")
);
return GetBoolArg("-gen", false);
}
Value setgenerate(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"setgenerate generate ( genproclimit )\n"
"\nSet 'generate' true or false to turn generation on or off.\n"
"Generation is limited to 'genproclimit' processors, -1 is unlimited.\n"
"See the getgenerate call for the current setting.\n"
"\nArguments:\n"
"1. generate (boolean, required) Set to true to turn on generation, off to turn off.\n"
"2. genproclimit (numeric, optional) Set the processor limit for when generation is on. Can be -1 for unlimited.\n"
" Note: in -regtest mode, genproclimit controls how many blocks are generated immediately.\n"
"\nResult\n"
"[ blockhashes ] (array, -regtest only) hashes of blocks generated\n"
"\nExamples:\n"
"\nSet the generation on with a limit of one processor\n"
+ HelpExampleCli("setgenerate", "true 1") +
"\nCheck the setting\n"
+ HelpExampleCli("getgenerate", "") +
"\nTurn off generation\n"
+ HelpExampleCli("setgenerate", "false") +
"\nUsing json rpc\n"
+ HelpExampleRpc("setgenerate", "true, 1")
);
if (pwalletMain == NULL)
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)");
bool fGenerate = true;
if (params.size() > 0)
fGenerate = params[0].get_bool();
int nGenProcLimit = -1;
if (params.size() > 1)
{
nGenProcLimit = params[1].get_int();
if (nGenProcLimit == 0)
fGenerate = false;
}
// -regtest mode: don't return until nGenProcLimit blocks are generated
if (fGenerate && Params().MineBlocksOnDemand())
{
int nHeightStart = 0;
int nHeightEnd = 0;
int nHeight = 0;
int nGenerate = (nGenProcLimit > 0 ? nGenProcLimit : 1);
CReserveKey reservekey(pwalletMain);
{ // Don't keep cs_main locked
LOCK(cs_main);
nHeightStart = chainActive.Height();
nHeight = nHeightStart;
nHeightEnd = nHeightStart+nGenerate;
}
unsigned int nExtraNonce = 0;
Array blockHashes;
while (nHeight < nHeightEnd)
{
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty");
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
}
while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits)) {
// Yes, there is a chance every nonce could fail to satisfy the -regtest
// target -- 1 in 2^(2^32). That ain't gonna happen.
++pblock->nNonce;
}
CValidationState state;
if (!ProcessNewBlock(state, NULL, pblock))
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
}
return blockHashes;
}
else // Not -regtest: start generate thread, return immediately
{
mapArgs["-gen"] = (fGenerate ? "1" : "0");
mapArgs ["-genproclimit"] = itostr(nGenProcLimit);
GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit);
}
return Value::null;
}
Value gethashespersec(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"gethashespersec\n"
"\nReturns a recent hashes per second performance measurement while generating.\n"
"See the getgenerate and setgenerate calls to turn generation on and off.\n"
"\nResult:\n"
"n (numeric) The recent hashes per second when generation is on (will return 0 if generation is off)\n"
"\nExamples:\n"
+ HelpExampleCli("gethashespersec", "")
+ HelpExampleRpc("gethashespersec", "")
);
if (GetTimeMillis() - nHPSTimerStart > 8000)
return (int64_t)0;
return (int64_t)dHashesPerSec;
}
#endif
Value getmininginfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getmininginfo\n"
"\nReturns a json object containing mining-related information."
"\nResult:\n"
"{\n"
" \"blocks\": nnn, (numeric) The current block\n"
" \"currentblocksize\": nnn, (numeric) The last block size\n"
" \"currentblocktx\": nnn, (numeric) The last block transaction\n"
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
" \"errors\": \"...\" (string) Current errors\n"
" \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n"
" \"genproclimit\": n (numeric) The processor limit for generation. -1 if no generation. (see getgenerate or setgenerate calls)\n"
" \"hashespersec\": n (numeric) The hashes per second of the generation, or 0 if no generation.\n"
" \"pooledtx\": n (numeric) The size of the mem pool\n"
" \"testnet\": true|false (boolean) If using testnet or not\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
);
Object obj;
obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1)));
obj.push_back(Pair("networkhashps", getnetworkhashps(params, false)));
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
obj.push_back(Pair("chain", Params().NetworkIDString()));
#ifdef ENABLE_WALLET
obj.push_back(Pair("generate", getgenerate(params, false)));
obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
#endif
return obj;
}
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
Value prioritisetransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 3)
throw runtime_error(
"prioritisetransaction <txid> <priority delta> <fee delta>\n"
"Accepts the transaction into mined blocks at a higher (or lower) priority\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id.\n"
"2. priority delta (numeric, required) The priority to add or subtract.\n"
" The transaction selection algorithm considers the tx as it would have a higher priority.\n"
" (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n"
"3. fee delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n"
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
" considers the transaction as it would have paid a higher (or lower) fee.\n"
"\nResult\n"
"true (boolean) Returns true\n"
"\nExamples:\n"
+ HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
);
uint256 hash = ParseHashStr(params[0].get_str(), "txid");
CAmount nAmount = params[2].get_int64();
mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount);
return true;
}
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
static Value BIP22ValidationResult(const CValidationState& state)
{
if (state.IsValid())
return Value::null;
std::string strRejectReason = state.GetRejectReason();
if (state.IsError())
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
if (state.IsInvalid())
{
if (strRejectReason.empty())
return "rejected";
return strRejectReason;
}
// Should be impossible
return "valid?";
}
Value getblocktemplate(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"getblocktemplate ( \"jsonrequestobject\" )\n"
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
"\nArguments:\n"
"1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n"
" {\n"
" \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n"
" \"capabilities\":[ (array, optional) A list of strings\n"
" \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
" ,...\n"
" ]\n"
" }\n"
"\n"
"\nResult:\n"
"{\n"
" \"version\" : n, (numeric) The block version\n"
" \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
" \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
" {\n"
" \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
" \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n"
" \"depends\" : [ (array) array of numbers \n"
" n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
" ,...\n"
" ],\n"
" \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
" \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n"
" \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
" }\n"
" ,...\n"
" ],\n"
" \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
" \"flags\" : \"flags\" (string) \n"
" },\n"
" \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n"
" \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
" \"target\" : \"xxxx\", (string) The hash target\n"
" \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"mutable\" : [ (array of string) list of ways the block template may be changed \n"
" \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
" ,...\n"
" ],\n"
" \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n"
" \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n"
" \"sizelimit\" : n, (numeric) limit of block size\n"
" \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
" \"bits\" : \"xxx\", (string) compressed target of next block\n"
" \"height\" : n (numeric) The height of the next block\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getblocktemplate", "")
+ HelpExampleRpc("getblocktemplate", "")
);
std::string strMode = "template";
Value lpval = Value::null;
if (params.size() > 0)
{
const Object& oparam = params[0].get_obj();
const Value& modeval = find_value(oparam, "mode");
if (modeval.type() == str_type)
strMode = modeval.get_str();
else if (modeval.type() == null_type)
{
/* Do nothing */
}
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
lpval = find_value(oparam, "longpollid");
if (strMode == "proposal")
{
const Value& dataval = find_value(oparam, "data");
if (dataval.type() != str_type)
throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
CBlock block;
if (!DecodeHexBlk(block, dataval.get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
uint256 hash = block.GetHash();
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
return "duplicate";
if (pindex->nStatus & BLOCK_FAILED_MASK)
return "duplicate-invalid";
return "duplicate-inconclusive";
}
CBlockIndex* const pindexPrev = chainActive.Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
CValidationState state;
TestBlockValidity(state, block, pindexPrev, false, true);
return BIP22ValidationResult(state);
}
}
if (strMode != "template")
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
if (vNodes.empty())
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!");
if (IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks...");
static unsigned int nTransactionsUpdatedLast;
if (lpval.type() != null_type)
{
// Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
uint256 hashWatchedChain;
boost::system_time checktxtime;
unsigned int nTransactionsUpdatedLastLP;
if (lpval.type() == str_type)
{
// Format: <hashBestChain><nTransactionsUpdatedLast>
std::string lpstr = lpval.get_str();
hashWatchedChain.SetHex(lpstr.substr(0, 64));
nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
}
else
{
// NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
hashWatchedChain = chainActive.Tip()->GetBlockHash();
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
// Release the wallet and main lock while waiting
#ifdef ENABLE_WALLET
if(pwalletMain)
LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet);
#endif
LEAVE_CRITICAL_SECTION(cs_main);
{
checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
boost::unique_lock<boost::mutex> lock(csBestBlock);
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
{
if (!cvBlockChange.timed_wait(lock, checktxtime))
{
// Timeout: Check transactions for update
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
break;
checktxtime += boost::posix_time::seconds(10);
}
}
}
ENTER_CRITICAL_SECTION(cs_main);
#ifdef ENABLE_WALLET
if(pwalletMain)
ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet);
#endif
if (!IsRPCRunning())
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
// Update block
static CBlockIndex* pindexPrev;
static int64_t nStart;
static CBlockTemplate* pblocktemplate;
if (pindexPrev != chainActive.Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
pindexPrev = NULL;
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrevNew = chainActive.Tip();
nStart = GetTime();
// Create new block
if(pblocktemplate)
{
delete pblocktemplate;
pblocktemplate = NULL;
}
CScript scriptDummy = CScript() << OP_TRUE;
pblocktemplate = CreateNewBlock(scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew;
}
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
// Update nTime
UpdateTime(pblock, pindexPrev);
pblock->nNonce = 0;
static const Array aCaps = boost::assign::list_of("proposal");
Array transactions;
map<uint256, int64_t> setTxIndex;
int i = 0;
BOOST_FOREACH (CTransaction& tx, pblock->vtx)
{
uint256 txHash = tx.GetHash();
setTxIndex[txHash] = i++;
if (tx.IsCoinBase())
continue;
Object entry;
entry.push_back(Pair("data", EncodeHexTx(tx)));
entry.push_back(Pair("hash", txHash.GetHex()));
Array deps;
BOOST_FOREACH (const CTxIn &in, tx.vin)
{
if (setTxIndex.count(in.prevout.hash))
deps.push_back(setTxIndex[in.prevout.hash]);
}
entry.push_back(Pair("depends", deps));
int index_in_template = i - 1;
entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template]));
transactions.push_back(entry);
}
Object aux;
aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
- uint256 hashTarget = uint256().SetCompact(pblock->nBits);
+ arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
static Array aMutable;
if (aMutable.empty())
{
aMutable.push_back("time");
aMutable.push_back("transactions");
aMutable.push_back("prevblock");
}
Object result;
result.push_back(Pair("capabilities", aCaps));
result.push_back(Pair("version", pblock->nVersion));
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux));
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
result.push_back(Pair("mutable", aMutable));
result.push_back(Pair("noncerange", "00000000ffffffff"));
result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
result.push_back(Pair("curtime", pblock->GetBlockTime()));
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));
return result;
}
class submitblock_StateCatcher : public CValidationInterface
{
public:
uint256 hash;
bool found;
CValidationState state;
submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {};
protected:
virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
if (block.GetHash() != hash)
return;
found = true;
state = stateIn;
};
};
Value submitblock(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"submitblock \"hexdata\" ( \"jsonparametersobject\" )\n"
"\nAttempts to submit new block to network.\n"
"The 'jsonparametersobject' parameter is currently ignored.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
"\nArguments\n"
"1. \"hexdata\" (string, required) the hex-encoded block data to submit\n"
"2. \"jsonparametersobject\" (string, optional) object of optional parameters\n"
" {\n"
" \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n"
" }\n"
"\nResult:\n"
"\nExamples:\n"
+ HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
);
CBlock block;
if (!DecodeHexBlk(block, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
uint256 hash = block.GetHash();
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
return "duplicate";
if (pindex->nStatus & BLOCK_FAILED_MASK)
return "duplicate-invalid";
// Otherwise, we might only have the header - process the block before returning
}
CValidationState state;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
bool fAccepted = ProcessNewBlock(state, NULL, &block);
UnregisterValidationInterface(&sc);
if (mi != mapBlockIndex.end())
{
if (fAccepted && !sc.found)
return "duplicate-inconclusive";
return "duplicate";
}
if (fAccepted)
{
if (!sc.found)
return "inconclusive";
state = sc.state;
}
return BIP22ValidationResult(state);
}
Value estimatefee(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"estimatefee nblocks\n"
"\nEstimates the approximate fee per kilobyte\n"
"needed for a transaction to begin confirmation\n"
"within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric)\n"
"\nResult:\n"
"n : (numeric) estimated fee-per-kilobyte\n"
"\n"
"-1.0 is returned if not enough transactions and\n"
"blocks have been observed to make an estimate.\n"
"\nExample:\n"
+ HelpExampleCli("estimatefee", "6")
);
RPCTypeCheck(params, boost::assign::list_of(int_type));
int nBlocks = params[0].get_int();
if (nBlocks < 1)
nBlocks = 1;
CFeeRate feeRate = mempool.estimateFee(nBlocks);
if (feeRate == CFeeRate(0))
return -1.0;
return ValueFromAmount(feeRate.GetFeePerK());
}
Value estimatepriority(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"estimatepriority nblocks\n"
"\nEstimates the approximate priority\n"
"a zero-fee transaction needs to begin confirmation\n"
"within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric)\n"
"\nResult:\n"
"n : (numeric) estimated priority\n"
"\n"
"-1.0 is returned if not enough transactions and\n"
"blocks have been observed to make an estimate.\n"
"\nExample:\n"
+ HelpExampleCli("estimatepriority", "6")
);
RPCTypeCheck(params, boost::assign::list_of(int_type));
int nBlocks = params[0].get_int();
if (nBlocks < 1)
nBlocks = 1;
return mempool.estimatePriority(nBlocks);
}
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index b87a8baac..10dc2ca93 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -1,758 +1,758 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
#include "primitives/transaction.h"
#include "core_io.h"
#include "init.h"
#include "keystore.h"
#include "main.h"
#include "net.h"
#include "rpcserver.h"
#include "script/script.h"
#include "script/sign.h"
#include "script/standard.h"
#include "uint256.h"
#ifdef ENABLE_WALLET
#include "wallet.h"
#endif
#include <stdint.h>
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
using namespace boost;
using namespace boost::assign;
using namespace json_spirit;
using namespace std;
void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex)
{
txnouttype type;
vector<CTxDestination> addresses;
int nRequired;
out.push_back(Pair("asm", scriptPubKey.ToString()));
if (fIncludeHex)
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
out.push_back(Pair("type", GetTxnOutputType(type)));
return;
}
out.push_back(Pair("reqSigs", nRequired));
out.push_back(Pair("type", GetTxnOutputType(type)));
Array a;
BOOST_FOREACH(const CTxDestination& addr, addresses)
a.push_back(CBitcoinAddress(addr).ToString());
out.push_back(Pair("addresses", a));
}
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
{
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
entry.push_back(Pair("version", tx.nVersion));
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
Array vin;
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
Object in;
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));
Object o;
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));
Array vout;
for (unsigned int i = 0; i < tx.vout.size(); i++) {
const CTxOut& txout = tx.vout[i];
Object out;
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
out.push_back(Pair("n", (int64_t)i));
Object o;
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
out.push_back(Pair("scriptPubKey", o));
vout.push_back(out);
}
entry.push_back(Pair("vout", vout));
- if (hashBlock != 0) {
+ 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("confirmations", 1 + chainActive.Height() - pindex->nHeight));
entry.push_back(Pair("time", pindex->GetBlockTime()));
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
}
else
entry.push_back(Pair("confirmations", 0));
}
}
}
Value getrawtransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"getrawtransaction \"txid\" ( verbose )\n"
"\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n"
"or there is an unspent output in the utxo for this transaction. To make it always work,\n"
"you need to maintain a transaction index, using the -txindex command line option.\n"
"\nReturn the raw transaction data.\n"
"\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n"
"If verbose is non-zero, returns an Object with information about 'txid'.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n"
"\nResult (if verbose is not set or set to 0):\n"
"\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
"\nResult (if verbose > 0):\n"
"{\n"
" \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric) \n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in btc\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"bitcoinaddress\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
" \"blockhash\" : \"hash\", (string) the block hash\n"
" \"confirmations\" : n, (numeric) The confirmations\n"
" \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getrawtransaction", "\"mytxid\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" 1")
+ HelpExampleRpc("getrawtransaction", "\"mytxid\", 1")
);
uint256 hash = ParseHashV(params[0], "parameter 1");
bool fVerbose = false;
if (params.size() > 1)
fVerbose = (params[1].get_int() != 0);
CTransaction tx;
- uint256 hashBlock = 0;
+ uint256 hashBlock;
if (!GetTransaction(hash, tx, hashBlock, true))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
string strHex = EncodeHexTx(tx);
if (!fVerbose)
return strHex;
Object result;
result.push_back(Pair("hex", strHex));
TxToJSON(tx, hashBlock, result);
return result;
}
#ifdef ENABLE_WALLET
Value listunspent(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 3)
throw runtime_error(
"listunspent ( minconf maxconf [\"address\",...] )\n"
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n"
"Results are an array of Objects, each of which has:\n"
"{txid, vout, scriptPubKey, amount, confirmations}\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
"2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
"3. \"addresses\" (string) A json array of bitcoin addresses to filter\n"
" [\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
"\nResult\n"
"[ (array of json object)\n"
" {\n"
" \"txid\" : \"txid\", (string) the transaction id \n"
" \"vout\" : n, (numeric) the vout value\n"
" \"address\" : \"address\", (string) the bitcoin address\n"
" \"account\" : \"account\", (string) The associated account, or \"\" for the default account\n"
" \"scriptPubKey\" : \"key\", (string) the script key\n"
" \"amount\" : x.xxx, (numeric) the transaction amount in btc\n"
" \"confirmations\" : n (numeric) The number of confirmations\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples\n"
+ HelpExampleCli("listunspent", "")
+ HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
);
RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
int nMinDepth = 1;
if (params.size() > 0)
nMinDepth = params[0].get_int();
int nMaxDepth = 9999999;
if (params.size() > 1)
nMaxDepth = params[1].get_int();
set<CBitcoinAddress> setAddress;
if (params.size() > 2) {
Array inputs = params[2].get_array();
BOOST_FOREACH(Value& input, inputs) {
CBitcoinAddress address(input.get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str());
if (setAddress.count(address))
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str());
setAddress.insert(address);
}
}
Array results;
vector<COutput> vecOutputs;
assert(pwalletMain != NULL);
pwalletMain->AvailableCoins(vecOutputs, false);
BOOST_FOREACH(const COutput& out, vecOutputs) {
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue;
if (setAddress.size()) {
CTxDestination address;
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
continue;
if (!setAddress.count(address))
continue;
}
CAmount nValue = out.tx->vout[out.i].nValue;
const CScript& pk = out.tx->vout[out.i].scriptPubKey;
Object entry;
entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
entry.push_back(Pair("vout", out.i));
CTxDestination address;
if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
if (pwalletMain->mapAddressBook.count(address))
entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name));
}
entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
if (pk.IsPayToScriptHash()) {
CTxDestination address;
if (ExtractDestination(pk, address)) {
const CScriptID& hash = boost::get<const CScriptID&>(address);
CScript redeemScript;
if (pwalletMain->GetCScript(hash, redeemScript))
entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
}
}
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
entry.push_back(Pair("confirmations",out.nDepth));
entry.push_back(Pair("spendable", out.fSpendable));
results.push_back(entry);
}
return results;
}
#endif
Value createrawtransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 2)
throw runtime_error(
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n"
"\nCreate a transaction spending the given inputs and sending to the given addresses.\n"
"Returns hex-encoded raw transaction.\n"
"Note that the transaction's inputs are not signed, and\n"
"it is not stored in the wallet or transmitted to the network.\n"
"\nArguments:\n"
"1. \"transactions\" (string, required) A json array of json objects\n"
" [\n"
" {\n"
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n (numeric, required) The output number\n"
" }\n"
" ,...\n"
" ]\n"
"2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n"
" {\n"
" \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the btc amount\n"
" ,...\n"
" }\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
"\nExamples\n"
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"")
);
RPCTypeCheck(params, list_of(array_type)(obj_type));
Array inputs = params[0].get_array();
Object sendTo = params[1].get_obj();
CMutableTransaction rawTx;
BOOST_FOREACH(const Value& input, inputs) {
const Object& o = input.get_obj();
uint256 txid = ParseHashO(o, "txid");
const Value& vout_v = find_value(o, "vout");
if (vout_v.type() != int_type)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
int nOutput = vout_v.get_int();
if (nOutput < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
CTxIn in(COutPoint(txid, nOutput));
rawTx.vin.push_back(in);
}
set<CBitcoinAddress> setAddress;
BOOST_FOREACH(const Pair& s, sendTo) {
CBitcoinAddress address(s.name_);
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
if (setAddress.count(address))
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
setAddress.insert(address);
CScript scriptPubKey = GetScriptForDestination(address.Get());
CAmount nAmount = AmountFromValue(s.value_);
CTxOut out(nAmount, scriptPubKey);
rawTx.vout.push_back(out);
}
return EncodeHexTx(rawTx);
}
Value decoderawtransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"decoderawtransaction \"hexstring\"\n"
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
"\nArguments:\n"
"1. \"hex\" (string, required) The transaction hex string\n"
"\nResult:\n"
"{\n"
" \"txid\" : \"id\", (string) The transaction id\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric) The output number\n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in btc\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
);
RPCTypeCheck(params, list_of(str_type));
CTransaction tx;
if (!DecodeHexTx(tx, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
Object result;
- TxToJSON(tx, 0, result);
+ TxToJSON(tx, uint256(), result);
return result;
}
Value decodescript(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"decodescript \"hex\"\n"
"\nDecode a hex-encoded script.\n"
"\nArguments:\n"
"1. \"hex\" (string) the hex encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
" \"hex\":\"hex\", (string) hex encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
" \"p2sh\",\"address\" (string) script address\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
);
RPCTypeCheck(params, list_of(str_type));
Object r;
CScript script;
if (params[0].get_str().size() > 0){
vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
script = CScript(scriptData.begin(), scriptData.end());
} else {
// Empty scripts are valid
}
ScriptPubKeyToJSON(script, r, false);
r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString()));
return r;
}
Value signrawtransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error(
"signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain.\n"
"The third optional argument (may be null) is an array of base58-encoded private\n"
"keys that, if given, will be the only keys used to sign the transaction.\n"
#ifdef ENABLE_WALLET
+ HelpRequiringPassphrase() + "\n"
#endif
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex string\n"
"2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
" [ (json array of json objects, or 'null' if none provided)\n"
" {\n"
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
" \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n"
" }\n"
" ,...\n"
" ]\n"
"3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n"
" [ (json array of strings, or 'null' if none provided)\n"
" \"privatekey\" (string) private key in base58-encoding\n"
" ,...\n"
" ]\n"
"4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\"\n"
"\nResult:\n"
"{\n"
" \"hex\": \"value\", (string) The raw transaction with signature(s) (hex-encoded string)\n"
" \"complete\": n (numeric) if transaction has a complete set of signature (0 if not)\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("signrawtransaction", "\"myhex\"")
+ HelpExampleRpc("signrawtransaction", "\"myhex\"")
);
RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
vector<CMutableTransaction> txVariants;
while (!ssData.empty()) {
try {
CMutableTransaction tx;
ssData >> tx;
txVariants.push_back(tx);
}
catch (const std::exception&) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
}
if (txVariants.empty())
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
// mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx:
CMutableTransaction mergedTx(txVariants[0]);
bool fComplete = true;
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
LOCK(mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
const uint256& prevHash = txin.prevout.hash;
CCoins coins;
view.AccessCoins(prevHash); // this is certainly allowed to fail
}
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
}
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
if (params.size() > 2 && params[2].type() != null_type) {
fGivenKeys = true;
Array keys = params[2].get_array();
BOOST_FOREACH(Value k, keys) {
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(k.get_str());
if (!fGood)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
CKey key = vchSecret.GetKey();
if (!key.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
tempKeystore.AddKey(key);
}
}
#ifdef ENABLE_WALLET
else
EnsureWalletIsUnlocked();
#endif
// Add previous txouts given in the RPC call:
if (params.size() > 1 && params[1].type() != null_type) {
Array prevTxs = params[1].get_array();
BOOST_FOREACH(Value& p, prevTxs) {
if (p.type() != obj_type)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
Object prevOut = p.get_obj();
RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
uint256 txid = ParseHashO(prevOut, "txid");
int nOut = find_value(prevOut, "vout").get_int();
if (nOut < 0)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
CCoinsModifier coins = view.ModifyCoins(txid);
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
string err("Previous output scriptPubKey mismatch:\n");
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
scriptPubKey.ToString();
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
}
if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1);
coins->vout[nOut].scriptPubKey = scriptPubKey;
coins->vout[nOut].nValue = 0; // we don't know the actual output value
}
// if redeemScript given and not using the local wallet (private keys
// given), add redeemScript to the tempKeystore so it can be signed:
if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) {
RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
Value v = find_value(prevOut, "redeemScript");
if (!(v == Value::null)) {
vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
}
}
#ifdef ENABLE_WALLET
const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain);
#else
const CKeyStore& keystore = tempKeystore;
#endif
int nHashType = SIGHASH_ALL;
if (params.size() > 3 && params[3].type() != null_type) {
static map<string, int> mapSigHashValues =
boost::assign::map_list_of
(string("ALL"), int(SIGHASH_ALL))
(string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
(string("NONE"), int(SIGHASH_NONE))
(string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
(string("SINGLE"), int(SIGHASH_SINGLE))
(string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
;
string strHashType = params[3].get_str();
if (mapSigHashValues.count(strHashType))
nHashType = mapSigHashValues[strHashType];
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
}
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
fComplete = false;
continue;
}
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
txin.scriptSig.clear();
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
// ... and merge in other signatures:
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
}
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(mergedTx, i)))
fComplete = false;
}
Object result;
result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
result.push_back(Pair("complete", fComplete));
return result;
}
Value sendrawtransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"sendrawtransaction \"hexstring\" ( allowhighfees )\n"
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nAlso see createrawtransaction and signrawtransaction calls.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw transaction)\n"
"2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
"\nResult:\n"
"\"hex\" (string) The transaction hash in hex\n"
"\nExamples:\n"
"\nCreate a transaction\n"
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
"Sign the transaction, and get back the hex\n"
+ HelpExampleCli("signrawtransaction", "\"myhex\"") +
"\nSend the transaction (signed hex)\n"
+ HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
RPCTypeCheck(params, list_of(str_type)(bool_type));
// parse hex string from parameter
CTransaction tx;
if (!DecodeHexTx(tx, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
uint256 hashTx = tx.GetHash();
bool fOverrideFees = false;
if (params.size() > 1)
fOverrideFees = params[1].get_bool();
CCoinsViewCache &view = *pcoinsTip;
const CCoins* existingCoins = view.AccessCoins(hashTx);
bool fHaveMempool = mempool.exists(hashTx);
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
if (!fHaveMempool && !fHaveChain) {
// push to local node and sync with wallets
CValidationState state;
if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) {
if(state.IsInvalid())
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
else
throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
}
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
}
RelayTransaction(tx);
return hashTx.GetHex();
}
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index af7e8221c..470a48e4f 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -1,2017 +1,2017 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "amount.h"
#include "base58.h"
#include "core_io.h"
#include "rpcserver.h"
#include "init.h"
#include "net.h"
#include "netbase.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
#include "wallet.h"
#include "walletdb.h"
#include <stdint.h>
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
using namespace std;
using namespace boost;
using namespace boost::assign;
using namespace json_spirit;
int64_t nWalletUnlockTime;
static CCriticalSection cs_nWalletUnlockTime;
std::string HelpRequiringPassphrase()
{
return pwalletMain && pwalletMain->IsCrypted()
? "\nRequires wallet passphrase to be set with walletpassphrase call."
: "";
}
void EnsureWalletIsUnlocked()
{
if (pwalletMain->IsLocked())
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
}
void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
{
int confirms = wtx.GetDepthInMainChain();
entry.push_back(Pair("confirmations", confirms));
if (wtx.IsCoinBase())
entry.push_back(Pair("generated", true));
if (confirms > 0)
{
entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
entry.push_back(Pair("blockindex", wtx.nIndex));
entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
}
uint256 hash = wtx.GetHash();
entry.push_back(Pair("txid", hash.GetHex()));
Array conflicts;
BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts())
conflicts.push_back(conflict.GetHex());
entry.push_back(Pair("walletconflicts", conflicts));
entry.push_back(Pair("time", wtx.GetTxTime()));
entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
entry.push_back(Pair(item.first, item.second));
}
string AccountFromValue(const Value& value)
{
string strAccount = value.get_str();
if (strAccount == "*")
throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
return strAccount;
}
Value getnewaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"getnewaddress ( \"account\" )\n"
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'account' is specified (recommended), it is added to the address book \n"
"so payments received with the address will be credited to 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) The account name for the address to be linked to. if not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n"
"\nResult:\n"
"\"bitcoinaddress\" (string) The new bitcoin address\n"
"\nExamples:\n"
+ HelpExampleCli("getnewaddress", "")
+ HelpExampleCli("getnewaddress", "\"\"")
+ HelpExampleCli("getnewaddress", "\"myaccount\"")
+ HelpExampleRpc("getnewaddress", "\"myaccount\"")
);
// Parse the account first so we don't generate a key if there's an error
string strAccount;
if (params.size() > 0)
strAccount = AccountFromValue(params[0]);
if (!pwalletMain->IsLocked())
pwalletMain->TopUpKeyPool();
// Generate a new key that is added to wallet
CPubKey newKey;
if (!pwalletMain->GetKeyFromPool(newKey))
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
CKeyID keyID = newKey.GetID();
pwalletMain->SetAddressBook(keyID, strAccount, "receive");
return CBitcoinAddress(keyID).ToString();
}
CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
{
CWalletDB walletdb(pwalletMain->strWalletFile);
CAccount account;
walletdb.ReadAccount(strAccount, account);
bool bKeyUsed = false;
// Check if the current key has been used
if (account.vchPubKey.IsValid())
{
CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID());
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
++it)
{
const CWalletTx& wtx = (*it).second;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (txout.scriptPubKey == scriptPubKey)
bKeyUsed = true;
}
}
// Generate a new key
if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
{
if (!pwalletMain->GetKeyFromPool(account.vchPubKey))
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");
walletdb.WriteAccount(strAccount, account);
}
return CBitcoinAddress(account.vchPubKey.GetID());
}
Value getaccountaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"getaccountaddress \"account\"\n"
"\nReturns the current Bitcoin address for receiving payments to this account.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The account name for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n"
"\nResult:\n"
"\"bitcoinaddress\" (string) The account bitcoin address\n"
"\nExamples:\n"
+ HelpExampleCli("getaccountaddress", "")
+ HelpExampleCli("getaccountaddress", "\"\"")
+ HelpExampleCli("getaccountaddress", "\"myaccount\"")
+ HelpExampleRpc("getaccountaddress", "\"myaccount\"")
);
// Parse the account first so we don't generate a key if there's an error
string strAccount = AccountFromValue(params[0]);
Value ret;
ret = GetAccountAddress(strAccount).ToString();
return ret;
}
Value getrawchangeaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"getrawchangeaddress\n"
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n"
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n"
+ HelpExampleCli("getrawchangeaddress", "")
+ HelpExampleRpc("getrawchangeaddress", "")
);
if (!pwalletMain->IsLocked())
pwalletMain->TopUpKeyPool();
CReserveKey reservekey(pwalletMain);
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey))
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
reservekey.KeepKey();
CKeyID keyID = vchPubKey.GetID();
return CBitcoinAddress(keyID).ToString();
}
Value setaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"setaccount \"bitcoinaddress\" \"account\"\n"
"\nSets the account associated with the given address.\n"
"\nArguments:\n"
"1. \"bitcoinaddress\" (string, required) The bitcoin address to be associated with an account.\n"
"2. \"account\" (string, required) The account to assign the address to.\n"
"\nExamples:\n"
+ HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"tabby\"")
+ HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"tabby\"")
);
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
string strAccount;
if (params.size() > 1)
strAccount = AccountFromValue(params[1]);
// Only add the account if the address is yours.
if (IsMine(*pwalletMain, address.Get()))
{
// Detect when changing the account of an address that is the 'unused current key' of another account:
if (pwalletMain->mapAddressBook.count(address.Get()))
{
string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name;
if (address == GetAccountAddress(strOldAccount))
GetAccountAddress(strOldAccount, true);
}
pwalletMain->SetAddressBook(address.Get(), strAccount, "receive");
}
else
throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address");
return Value::null;
}
Value getaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"getaccount \"bitcoinaddress\"\n"
"\nReturns the account associated with the given address.\n"
"\nArguments:\n"
"1. \"bitcoinaddress\" (string, required) The bitcoin address for account lookup.\n"
"\nResult:\n"
"\"accountname\" (string) the account address\n"
"\nExamples:\n"
+ HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"")
+ HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"")
);
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
string strAccount;
map<CTxDestination, CAddressBookData>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty())
strAccount = (*mi).second.name;
return strAccount;
}
Value getaddressesbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"getaddressesbyaccount \"account\"\n"
"\nReturns the list of addresses for the given account.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The account name.\n"
"\nResult:\n"
"[ (json array of string)\n"
" \"bitcoinaddress\" (string) a bitcoin address associated with the given account\n"
" ,...\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("getaddressesbyaccount", "\"tabby\"")
+ HelpExampleRpc("getaddressesbyaccount", "\"tabby\"")
);
string strAccount = AccountFromValue(params[0]);
// Find all addresses that have the given account
Array ret;
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
const string& strName = item.second.name;
if (strName == strAccount)
ret.push_back(address.ToString());
}
return ret;
}
void SendMoney(const CTxDestination &address, CAmount nValue, CWalletTx& wtxNew)
{
// Check amount
if (nValue <= 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
if (nValue > pwalletMain->GetBalance())
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
string strError;
if (pwalletMain->IsLocked())
{
strError = "Error: Wallet locked, unable to create transaction!";
LogPrintf("SendMoney() : %s", strError);
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
// Parse Bitcoin address
CScript scriptPubKey = GetScriptForDestination(address);
// Create and send the transaction
CReserveKey reservekey(pwalletMain);
CAmount nFeeRequired;
if (!pwalletMain->CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError))
{
if (nValue + nFeeRequired > pwalletMain->GetBalance())
strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
LogPrintf("SendMoney() : %s\n", strError);
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
if (!pwalletMain->CommitTransaction(wtxNew, reservekey))
throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
}
Value sendtoaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 4)
throw runtime_error(
"sendtoaddress \"bitcoinaddress\" amount ( \"comment\" \"comment-to\" )\n"
"\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n"
+ HelpRequiringPassphrase() +
"\nArguments:\n"
"1. \"bitcoinaddress\" (string, required) The bitcoin address to send to.\n"
"2. \"amount\" (numeric, required) The amount in btc to send. eg 0.1\n"
"3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
" This is not part of the transaction, just kept in your wallet.\n"
"4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n"
" to which you're sending the transaction. This is not part of the \n"
" transaction, just kept in your wallet.\n"
"\nResult:\n"
"\"transactionid\" (string) The transaction id.\n"
"\nExamples:\n"
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"")
+ HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"")
);
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
// Amount
CAmount nAmount = AmountFromValue(params[1]);
// Wallet comments
CWalletTx wtx;
if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
wtx.mapValue["comment"] = params[2].get_str();
if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
wtx.mapValue["to"] = params[3].get_str();
EnsureWalletIsUnlocked();
SendMoney(address.Get(), nAmount, wtx);
return wtx.GetHash().GetHex();
}
Value listaddressgroupings(const Array& params, bool fHelp)
{
if (fHelp)
throw runtime_error(
"listaddressgroupings\n"
"\nLists groups of addresses which have had their common ownership\n"
"made public by common use as inputs or as the resulting change\n"
"in past transactions\n"
"\nResult:\n"
"[\n"
" [\n"
" [\n"
" \"bitcoinaddress\", (string) The bitcoin address\n"
" amount, (numeric) The amount in btc\n"
" \"account\" (string, optional) The account\n"
" ]\n"
" ,...\n"
" ]\n"
" ,...\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("listaddressgroupings", "")
+ HelpExampleRpc("listaddressgroupings", "")
);
Array jsonGroupings;
map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances();
BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
{
Array jsonGrouping;
BOOST_FOREACH(CTxDestination address, grouping)
{
Array addressInfo;
addressInfo.push_back(CBitcoinAddress(address).ToString());
addressInfo.push_back(ValueFromAmount(balances[address]));
{
LOCK(pwalletMain->cs_wallet);
if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name);
}
jsonGrouping.push_back(addressInfo);
}
jsonGroupings.push_back(jsonGrouping);
}
return jsonGroupings;
}
Value signmessage(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 2)
throw runtime_error(
"signmessage \"bitcoinaddress\" \"message\"\n"
"\nSign a message with the private key of an address"
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
"1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the private key.\n"
"2. \"message\" (string, required) The message to create a signature of.\n"
"\nResult:\n"
"\"signature\" (string) The signature of the message encoded in base 64\n"
"\nExamples:\n"
"\nUnlock the wallet for 30 seconds\n"
+ HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
"\nCreate the signature\n"
+ HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") +
"\nAs json rpc\n"
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"my message\"")
);
EnsureWalletIsUnlocked();
string strAddress = params[0].get_str();
string strMessage = params[1].get_str();
CBitcoinAddress addr(strAddress);
if (!addr.IsValid())
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
CKeyID keyID;
if (!addr.GetKeyID(keyID))
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
CKey key;
if (!pwalletMain->GetKey(keyID, key))
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
vector<unsigned char> vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
return EncodeBase64(&vchSig[0], vchSig.size());
}
Value getreceivedbyaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"getreceivedbyaddress \"bitcoinaddress\" ( minconf )\n"
"\nReturns the total amount received by the given bitcoinaddress in transactions with at least minconf confirmations.\n"
"\nArguments:\n"
"1. \"bitcoinaddress\" (string, required) The bitcoin address for transactions.\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
"\nResult:\n"
"amount (numeric) The total amount in btc received at this address.\n"
"\nExamples:\n"
"\nThe amount from transactions with at least 1 confirmation\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") +
"\nThe amount including unconfirmed transactions, zero confirmations\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 0") +
"\nThe amount with at least 6 confirmation, very safe\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 6") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", 6")
);
// Bitcoin address
CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
CScript scriptPubKey = GetScriptForDestination(address.Get());
if (!IsMine(*pwalletMain,scriptPubKey))
return (double)0.0;
// Minimum confirmations
int nMinDepth = 1;
if (params.size() > 1)
nMinDepth = params[1].get_int();
// Tally
CAmount nAmount = 0;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (wtx.IsCoinBase() || !IsFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (txout.scriptPubKey == scriptPubKey)
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
}
return ValueFromAmount(nAmount);
}
Value getreceivedbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"getreceivedbyaccount \"account\" ( minconf )\n"
"\nReturns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The selected account, may be the default account using \"\".\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
"\nResult:\n"
"amount (numeric) The total amount in btc received for this account.\n"
"\nExamples:\n"
"\nAmount received by the default account with at least 1 confirmation\n"
+ HelpExampleCli("getreceivedbyaccount", "\"\"") +
"\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n"
+ HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") +
"\nThe amount with at least 6 confirmation, very safe\n"
+ HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6")
);
// Minimum confirmations
int nMinDepth = 1;
if (params.size() > 1)
nMinDepth = params[1].get_int();
// Get the set of pub keys assigned to account
string strAccount = AccountFromValue(params[0]);
set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount);
// Tally
CAmount nAmount = 0;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (wtx.IsCoinBase() || !IsFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
}
}
return (double)nAmount / (double)COIN;
}
CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
{
CAmount nBalance = 0;
// Tally wallet transactions
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0)
continue;
CAmount nReceived, nSent, nFee;
wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter);
if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
nBalance += nReceived;
nBalance -= nSent + nFee;
}
// Tally internal accounting entries
nBalance += walletdb.GetAccountCreditDebit(strAccount);
return nBalance;
}
CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
{
CWalletDB walletdb(pwalletMain->strWalletFile);
return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
}
Value getbalance(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 3)
throw runtime_error(
"getbalance ( \"account\" minconf includeWatchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"If account is specified, returns the balance in the account.\n"
"Note that the account \"\" is not the same as leaving the parameter out.\n"
"The server total may be different to the balance in the default \"\" account.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
"3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in btc received for this account.\n"
"\nExamples:\n"
"\nThe total amount in the server across all accounts\n"
+ HelpExampleCli("getbalance", "") +
"\nThe total amount in the server across all accounts, with at least 5 confirmations\n"
+ HelpExampleCli("getbalance", "\"*\" 6") +
"\nThe total amount in the default account with at least 1 confirmation\n"
+ HelpExampleCli("getbalance", "\"\"") +
"\nThe total amount in the account named tabby with at least 6 confirmations\n"
+ HelpExampleCli("getbalance", "\"tabby\" 6") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("getbalance", "\"tabby\", 6")
);
if (params.size() == 0)
return ValueFromAmount(pwalletMain->GetBalance());
int nMinDepth = 1;
if (params.size() > 1)
nMinDepth = params[1].get_int();
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 2)
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
if (params[0].get_str() == "*") {
// Calculate total balance a different way from GetBalance()
// (GetBalance() sums up all unspent TxOuts)
// getbalance and getbalance '*' 0 should return the same number
CAmount nBalance = 0;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0)
continue;
CAmount allFee;
string strSentAccount;
list<COutputEntry> listReceived;
list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (wtx.GetDepthInMainChain() >= nMinDepth)
{
BOOST_FOREACH(const COutputEntry& r, listReceived)
nBalance += r.amount;
}
BOOST_FOREACH(const COutputEntry& s, listSent)
nBalance -= s.amount;
nBalance -= allFee;
}
return ValueFromAmount(nBalance);
}
string strAccount = AccountFromValue(params[0]);
CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
return ValueFromAmount(nBalance);
}
Value getunconfirmedbalance(const Array &params, bool fHelp)
{
if (fHelp || params.size() > 0)
throw runtime_error(
"getunconfirmedbalance\n"
"Returns the server's total unconfirmed balance\n");
return ValueFromAmount(pwalletMain->GetUnconfirmedBalance());
}
Value movecmd(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 3 || params.size() > 5)
throw runtime_error(
"move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n"
"\nMove a specified amount from one account in your wallet to another.\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) The name of the account to move funds from. May be the default account using \"\".\n"
"2. \"toaccount\" (string, required) The name of the account to move funds to. May be the default account using \"\".\n"
"3. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n"
"4. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n"
"\nResult:\n"
"true|false (boolean) true if successfull.\n"
"\nExamples:\n"
"\nMove 0.01 btc from the default account to the account named tabby\n"
+ HelpExampleCli("move", "\"\" \"tabby\" 0.01") +
"\nMove 0.01 btc timotei to akiko with a comment and funds have 6 confirmations\n"
+ HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"")
);
string strFrom = AccountFromValue(params[0]);
string strTo = AccountFromValue(params[1]);
CAmount nAmount = AmountFromValue(params[2]);
if (params.size() > 3)
// unused parameter, used to be nMinDepth, keep type-checking it though
(void)params[3].get_int();
string strComment;
if (params.size() > 4)
strComment = params[4].get_str();
CWalletDB walletdb(pwalletMain->strWalletFile);
if (!walletdb.TxnBegin())
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
int64_t nNow = GetAdjustedTime();
// Debit
CAccountingEntry debit;
debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
debit.strAccount = strFrom;
debit.nCreditDebit = -nAmount;
debit.nTime = nNow;
debit.strOtherAccount = strTo;
debit.strComment = strComment;
walletdb.WriteAccountingEntry(debit);
// Credit
CAccountingEntry credit;
credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
credit.strAccount = strTo;
credit.nCreditDebit = nAmount;
credit.nTime = nNow;
credit.strOtherAccount = strFrom;
credit.strComment = strComment;
walletdb.WriteAccountingEntry(credit);
if (!walletdb.TxnCommit())
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
return true;
}
Value sendfrom(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 3 || params.size() > 6)
throw runtime_error(
"sendfrom \"fromaccount\" \"tobitcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n"
"\nSent an amount from an account to a bitcoin address.\n"
"The amount is a real and is rounded to the nearest 0.00000001."
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n"
"2. \"tobitcoinaddress\" (string, required) The bitcoin address to send funds to.\n"
"3. amount (numeric, required) The amount in btc. (transaction fee is added on top).\n"
"4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n"
"5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
" This is not part of the transaction, just kept in your wallet.\n"
"6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n"
" to which you're sending the transaction. This is not part of the transaction, \n"
" it is just kept in your wallet.\n"
"\nResult:\n"
"\"transactionid\" (string) The transaction id.\n"
"\nExamples:\n"
"\nSend 0.01 btc from the default account to the address, must have at least 1 confirmation\n"
+ HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") +
"\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n"
+ HelpExampleCli("sendfrom", "\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"")
);
string strAccount = AccountFromValue(params[0]);
CBitcoinAddress address(params[1].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
CAmount nAmount = AmountFromValue(params[2]);
int nMinDepth = 1;
if (params.size() > 3)
nMinDepth = params[3].get_int();
CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
wtx.mapValue["comment"] = params[4].get_str();
if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
wtx.mapValue["to"] = params[5].get_str();
EnsureWalletIsUnlocked();
// Check funds
CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
SendMoney(address.Get(), nAmount, wtx);
return wtx.GetHash().GetHex();
}
Value sendmany(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 4)
throw runtime_error(
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" )\n"
"\nSend multiple times. Amounts are double-precision floating point numbers."
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) The account to send the funds from, can be \"\" for the default account\n"
"2. \"amounts\" (string, required) A json object with addresses and amounts\n"
" {\n"
" \"address\":amount (numeric) The bitcoin address is the key, the numeric amount in btc is the value\n"
" ,...\n"
" }\n"
"3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n"
"4. \"comment\" (string, optional) A comment\n"
"\nResult:\n"
"\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
" the number of addresses.\n"
"\nExamples:\n"
"\nSend two amounts to two different addresses:\n"
+ HelpExampleCli("sendmany", "\"tabby\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") +
"\nSend two amounts to two different addresses setting the confirmation and comment:\n"
+ HelpExampleCli("sendmany", "\"tabby\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("sendmany", "\"tabby\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"")
);
string strAccount = AccountFromValue(params[0]);
Object sendTo = params[1].get_obj();
int nMinDepth = 1;
if (params.size() > 2)
nMinDepth = params[2].get_int();
CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
wtx.mapValue["comment"] = params[3].get_str();
set<CBitcoinAddress> setAddress;
vector<pair<CScript, CAmount> > vecSend;
CAmount totalAmount = 0;
BOOST_FOREACH(const Pair& s, sendTo)
{
CBitcoinAddress address(s.name_);
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
if (setAddress.count(address))
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
setAddress.insert(address);
CScript scriptPubKey = GetScriptForDestination(address.Get());
CAmount nAmount = AmountFromValue(s.value_);
totalAmount += nAmount;
vecSend.push_back(make_pair(scriptPubKey, nAmount));
}
EnsureWalletIsUnlocked();
// Check funds
CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (totalAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
// Send
CReserveKey keyChange(pwalletMain);
CAmount nFeeRequired = 0;
string strFailReason;
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
if (!pwalletMain->CommitTransaction(wtx, keyChange))
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
return wtx.GetHash().GetHex();
}
// Defined in rpcmisc.cpp
extern CScript _createmultisig_redeemScript(const Array& params);
Value addmultisigaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 3)
{
string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n"
"\nAdd a nrequired-to-sign multisignature address to the wallet.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"If 'account' is specified, assign address to that account.\n"
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
"2. \"keysobject\" (string, required) A json array of bitcoin addresses or hex-encoded public keys\n"
" [\n"
" \"address\" (string) bitcoin address or hex-encoded public key\n"
" ...,\n"
" ]\n"
"3. \"account\" (string, optional) An account to assign the addresses to.\n"
"\nResult:\n"
"\"bitcoinaddress\" (string) A bitcoin address associated with the keys.\n"
"\nExamples:\n"
"\nAdd a multisig address from 2 addresses\n"
+ HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
"\nAs json rpc call\n"
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")
;
throw runtime_error(msg);
}
string strAccount;
if (params.size() > 2)
strAccount = AccountFromValue(params[2]);
// Construct using pay-to-script-hash:
CScript inner = _createmultisig_redeemScript(params);
CScriptID innerID(inner);
pwalletMain->AddCScript(inner);
pwalletMain->SetAddressBook(innerID, strAccount, "send");
return CBitcoinAddress(innerID).ToString();
}
struct tallyitem
{
CAmount nAmount;
int nConf;
vector<uint256> txids;
bool fIsWatchonly;
tallyitem()
{
nAmount = 0;
nConf = std::numeric_limits<int>::max();
fIsWatchonly = false;
}
};
Value ListReceived(const Array& params, bool fByAccounts)
{
// Minimum confirmations
int nMinDepth = 1;
if (params.size() > 0)
nMinDepth = params[0].get_int();
// Whether to include empty accounts
bool fIncludeEmpty = false;
if (params.size() > 1)
fIncludeEmpty = params[1].get_bool();
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 2)
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
// Tally
map<CBitcoinAddress, tallyitem> mapTally;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (wtx.IsCoinBase() || !IsFinalTx(wtx))
continue;
int nDepth = wtx.GetDepthInMainChain();
if (nDepth < nMinDepth)
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address))
continue;
isminefilter mine = IsMine(*pwalletMain, address);
if(!(mine & filter))
continue;
tallyitem& item = mapTally[address];
item.nAmount += txout.nValue;
item.nConf = min(item.nConf, nDepth);
item.txids.push_back(wtx.GetHash());
if (mine & ISMINE_WATCH_ONLY)
item.fIsWatchonly = true;
}
}
// Reply
Array ret;
map<string, tallyitem> mapAccountTally;
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
const string& strAccount = item.second.name;
map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty)
continue;
CAmount nAmount = 0;
int nConf = std::numeric_limits<int>::max();
bool fIsWatchonly = false;
if (it != mapTally.end())
{
nAmount = (*it).second.nAmount;
nConf = (*it).second.nConf;
fIsWatchonly = (*it).second.fIsWatchonly;
}
if (fByAccounts)
{
tallyitem& item = mapAccountTally[strAccount];
item.nAmount += nAmount;
item.nConf = min(item.nConf, nConf);
item.fIsWatchonly = fIsWatchonly;
}
else
{
Object obj;
if(fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true));
obj.push_back(Pair("address", address.ToString()));
obj.push_back(Pair("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
Array transactions;
if (it != mapTally.end())
{
BOOST_FOREACH(const uint256& item, (*it).second.txids)
{
transactions.push_back(item.GetHex());
}
}
obj.push_back(Pair("txids", transactions));
ret.push_back(obj);
}
}
if (fByAccounts)
{
for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
{
CAmount nAmount = (*it).second.nAmount;
int nConf = (*it).second.nConf;
Object obj;
if((*it).second.fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true));
obj.push_back(Pair("account", (*it).first));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
ret.push_back(obj);
}
}
return ret;
}
Value listreceivedbyaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 3)
throw runtime_error(
"listreceivedbyaddress ( minconf includeempty includeWatchonly)\n"
"\nList balances by receiving address.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. includeempty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n"
"3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n"
" \"address\" : \"receivingaddress\", (string) The receiving address\n"
" \"account\" : \"accountname\", (string) The account of the receiving address. The default account is \"\".\n"
" \"amount\" : x.xxx, (numeric) The total amount in btc received by the address\n"
" \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("listreceivedbyaddress", "")
+ HelpExampleCli("listreceivedbyaddress", "6 true")
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true")
);
return ListReceived(params, false);
}
Value listreceivedbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 3)
throw runtime_error(
"listreceivedbyaccount ( minconf includeempty includeWatchonly)\n"
"\nList balances by account.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n"
"3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n"
" \"account\" : \"accountname\", (string) The account name of the receiving account\n"
" \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n"
" \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("listreceivedbyaccount", "")
+ HelpExampleCli("listreceivedbyaccount", "6 true")
+ HelpExampleRpc("listreceivedbyaccount", "6, true, true")
);
return ListReceived(params, true);
}
static void MaybePushAddress(Object & entry, const CTxDestination &dest)
{
CBitcoinAddress addr;
if (addr.Set(dest))
entry.push_back(Pair("address", addr.ToString()));
}
void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
{
CAmount nFee;
string strSentAccount;
list<COutputEntry> listReceived;
list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter);
bool fAllAccounts = (strAccount == string("*"));
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
// Sent
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
{
BOOST_FOREACH(const COutputEntry& s, listSent)
{
Object entry;
if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", strSentAccount));
MaybePushAddress(entry, s.destination);
entry.push_back(Pair("category", "send"));
entry.push_back(Pair("amount", ValueFromAmount(-s.amount)));
entry.push_back(Pair("vout", s.vout));
entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
if (fLong)
WalletTxToJSON(wtx, entry);
ret.push_back(entry);
}
}
// Received
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
{
BOOST_FOREACH(const COutputEntry& r, listReceived)
{
string account;
if (pwalletMain->mapAddressBook.count(r.destination))
account = pwalletMain->mapAddressBook[r.destination].name;
if (fAllAccounts || (account == strAccount))
{
Object entry;
if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", account));
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
if (wtx.GetDepthInMainChain() < 1)
entry.push_back(Pair("category", "orphan"));
else if (wtx.GetBlocksToMaturity() > 0)
entry.push_back(Pair("category", "immature"));
else
entry.push_back(Pair("category", "generate"));
}
else
{
entry.push_back(Pair("category", "receive"));
}
entry.push_back(Pair("amount", ValueFromAmount(r.amount)));
entry.push_back(Pair("vout", r.vout));
if (fLong)
WalletTxToJSON(wtx, entry);
ret.push_back(entry);
}
}
}
}
void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
{
bool fAllAccounts = (strAccount == string("*"));
if (fAllAccounts || acentry.strAccount == strAccount)
{
Object entry;
entry.push_back(Pair("account", acentry.strAccount));
entry.push_back(Pair("category", "move"));
entry.push_back(Pair("time", acentry.nTime));
entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
entry.push_back(Pair("comment", acentry.strComment));
ret.push_back(entry);
}
}
Value listtransactions(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 4)
throw runtime_error(
"listtransactions ( \"account\" count from includeWatchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) The account name. If not included, it will list all transactions for all accounts.\n"
" If \"\" is set, it will list transactions for the default account.\n"
"2. count (numeric, optional, default=10) The number of transactions to return\n"
"3. from (numeric, optional, default=0) The number of transactions to skip\n"
"4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"[\n"
" {\n"
" \"account\":\"accountname\", (string) The account name associated with the transaction. \n"
" It will be \"\" for the default account.\n"
" \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for \n"
" move transactions (category = move).\n"
" \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n"
" transaction between accounts, and not associated with an address,\n"
" transaction id or block. 'send' and 'receive' transactions are \n"
" associated with an address, transaction id and block details\n"
" \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the\n"
" 'move' category for moves outbound. It is positive for the 'receive' category,\n"
" and for the 'move' category for inbound funds.\n"
" \"vout\" : n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the \n"
" 'send' category of transactions.\n"
" \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n"
" 'receive' category of transactions.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n"
" category of transactions.\n"
" \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n"
" category of transactions.\n"
" \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n"
" \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n"
" for 'send' and 'receive' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
" \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n"
" from (for receiving funds, positive amounts), or went to (for sending funds,\n"
" negative amounts).\n"
" }\n"
"]\n"
"\nExamples:\n"
"\nList the most recent 10 transactions in the systems\n"
+ HelpExampleCli("listtransactions", "") +
"\nList the most recent 10 transactions for the tabby account\n"
+ HelpExampleCli("listtransactions", "\"tabby\"") +
"\nList transactions 100 to 120 from the tabby account\n"
+ HelpExampleCli("listtransactions", "\"tabby\" 20 100") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("listtransactions", "\"tabby\", 20, 100")
);
string strAccount = "*";
if (params.size() > 0)
strAccount = params[0].get_str();
int nCount = 10;
if (params.size() > 1)
nCount = params[1].get_int();
int nFrom = 0;
if (params.size() > 2)
nFrom = params[2].get_int();
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 3)
if(params[3].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
if (nCount < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
if (nFrom < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
Array ret;
std::list<CAccountingEntry> acentries;
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
// iterate backwards until we have nCount items to return:
for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != 0)
ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
CAccountingEntry *const pacentry = (*it).second.second;
if (pacentry != 0)
AcentryToJSON(*pacentry, strAccount, ret);
if ((int)ret.size() >= (nCount+nFrom)) break;
}
// ret is newest to oldest
if (nFrom > (int)ret.size())
nFrom = ret.size();
if ((nFrom + nCount) > (int)ret.size())
nCount = ret.size() - nFrom;
Array::iterator first = ret.begin();
std::advance(first, nFrom);
Array::iterator last = ret.begin();
std::advance(last, nFrom+nCount);
if (last != ret.end()) ret.erase(last, ret.end());
if (first != ret.begin()) ret.erase(ret.begin(), first);
std::reverse(ret.begin(), ret.end()); // Return oldest to newest
return ret;
}
Value listaccounts(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 2)
throw runtime_error(
"listaccounts ( minconf includeWatchonly)\n"
"\nReturns Object that has account names as keys, account balances as values.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n"
"2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"{ (json object where keys are account names, and values are numeric balances\n"
" \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n"
" ...\n"
"}\n"
"\nExamples:\n"
"\nList account balances where there at least 1 confirmation\n"
+ HelpExampleCli("listaccounts", "") +
"\nList account balances including zero confirmation transactions\n"
+ HelpExampleCli("listaccounts", "0") +
"\nList account balances for 6 or more confirmations\n"
+ HelpExampleCli("listaccounts", "6") +
"\nAs json rpc call\n"
+ HelpExampleRpc("listaccounts", "6")
);
int nMinDepth = 1;
if (params.size() > 0)
nMinDepth = params[0].get_int();
isminefilter includeWatchonly = ISMINE_SPENDABLE;
if(params.size() > 1)
if(params[1].get_bool())
includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;
map<string, CAmount> mapAccountBalances;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) {
if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me
mapAccountBalances[entry.second.name] = 0;
}
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
CAmount nFee;
string strSentAccount;
list<COutputEntry> listReceived;
list<COutputEntry> listSent;
int nDepth = wtx.GetDepthInMainChain();
if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0)
continue;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly);
mapAccountBalances[strSentAccount] -= nFee;
BOOST_FOREACH(const COutputEntry& s, listSent)
mapAccountBalances[strSentAccount] -= s.amount;
if (nDepth >= nMinDepth)
{
BOOST_FOREACH(const COutputEntry& r, listReceived)
if (pwalletMain->mapAddressBook.count(r.destination))
mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount;
else
mapAccountBalances[""] += r.amount;
}
}
list<CAccountingEntry> acentries;
CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
BOOST_FOREACH(const CAccountingEntry& entry, acentries)
mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
Object ret;
BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) {
ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
}
return ret;
}
Value listsinceblock(const Array& params, bool fHelp)
{
if (fHelp)
throw runtime_error(
"listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n"
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
"\nArguments:\n"
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
"2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
"3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
" \"account\":\"accountname\", (string) The account name associated with the transaction. Will be \"\" for the default account.\n"
" \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n"
" \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
" \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n"
" outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n"
" \"vout\" : n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the 'send' category of transactions.\n"
" \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n"
" \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n"
" \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
" \"to\": \"...\", (string) If a comment to is associated with the transaction.\n"
" ],\n"
" \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("listsinceblock", "")
+ HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
+ HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
);
CBlockIndex *pindex = NULL;
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
if (params.size() > 0)
{
- uint256 blockId = 0;
+ uint256 blockId;
blockId.SetHex(params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
if (it != mapBlockIndex.end())
pindex = it->second;
}
if (params.size() > 1)
{
target_confirms = params[1].get_int();
if (target_confirms < 1)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
if(params.size() > 2)
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
Array transactions;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
{
CWalletTx tx = (*it).second;
if (depth == -1 || tx.GetDepthInMainChain() < depth)
ListTransactions(tx, "*", 0, true, transactions, filter);
}
CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
- uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : 0;
+ uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256();
Object ret;
ret.push_back(Pair("transactions", transactions));
ret.push_back(Pair("lastblock", lastblock.GetHex()));
return ret;
}
Value gettransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"gettransaction \"txid\" ( includeWatchonly )\n"
"\nGet detailed information about in-wallet transaction <txid>\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n"
"\nResult:\n"
"{\n"
" \"amount\" : x.xxx, (numeric) The transaction amount in btc\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"blockhash\" : \"hash\", (string) The block hash\n"
" \"blockindex\" : xx, (numeric) The block index\n"
" \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n"
" \"txid\" : \"transactionid\", (string) The transaction id.\n"
" \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n"
" \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n"
" \"details\" : [\n"
" {\n"
" \"account\" : \"accountname\", (string) The account name involved in the transaction, can be \"\" for the default account.\n"
" \"address\" : \"bitcoinaddress\", (string) The bitcoin address involved in the transaction\n"
" \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n"
" \"amount\" : x.xxx (numeric) The amount in btc\n"
" \"vout\" : n, (numeric) the vout value\n"
" }\n"
" ,...\n"
" ],\n"
" \"hex\" : \"data\" (string) Raw data for transaction\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true")
+ HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
);
uint256 hash;
hash.SetHex(params[0].get_str());
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 1)
if(params[1].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
Object entry;
if (!pwalletMain->mapWallet.count(hash))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
const CWalletTx& wtx = pwalletMain->mapWallet[hash];
CAmount nCredit = wtx.GetCredit(filter);
CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
if (wtx.IsFromMe(filter))
entry.push_back(Pair("fee", ValueFromAmount(nFee)));
WalletTxToJSON(wtx, entry);
Array details;
ListTransactions(wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
entry.push_back(Pair("hex", strHex));
return entry;
}
Value backupwallet(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"backupwallet \"destination\"\n"
"\nSafely copies wallet.dat to destination, which can be a directory or a path with filename.\n"
"\nArguments:\n"
"1. \"destination\" (string) The destination directory or file\n"
"\nExamples:\n"
+ HelpExampleCli("backupwallet", "\"backup.dat\"")
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
);
string strDest = params[0].get_str();
if (!BackupWallet(*pwalletMain, strDest))
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
return Value::null;
}
Value keypoolrefill(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"keypoolrefill ( newsize )\n"
"\nFills the keypool."
+ HelpRequiringPassphrase() + "\n"
"\nArguments\n"
"1. newsize (numeric, optional, default=100) The new keypool size\n"
"\nExamples:\n"
+ HelpExampleCli("keypoolrefill", "")
+ HelpExampleRpc("keypoolrefill", "")
);
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
unsigned int kpSize = 0;
if (params.size() > 0) {
if (params[0].get_int() < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
kpSize = (unsigned int)params[0].get_int();
}
EnsureWalletIsUnlocked();
pwalletMain->TopUpKeyPool(kpSize);
if (pwalletMain->GetKeyPoolSize() < kpSize)
throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
return Value::null;
}
static void LockWallet(CWallet* pWallet)
{
LOCK(cs_nWalletUnlockTime);
nWalletUnlockTime = 0;
pWallet->Lock();
}
Value walletpassphrase(const Array& params, bool fHelp)
{
if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
throw runtime_error(
"walletpassphrase \"passphrase\" timeout\n"
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
"This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
"\nArguments:\n"
"1. \"passphrase\" (string, required) The wallet passphrase\n"
"2. timeout (numeric, required) The time to keep the decryption key in seconds.\n"
"\nNote:\n"
"Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
"time that overrides the old one.\n"
"\nExamples:\n"
"\nunlock the wallet for 60 seconds\n"
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
"\nLock the wallet again (before 60 seconds)\n"
+ HelpExampleCli("walletlock", "") +
"\nAs json rpc call\n"
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
);
if (fHelp)
return true;
if (!pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
// Note that the walletpassphrase is stored in params[0] which is not mlock()ed
SecureString strWalletPass;
strWalletPass.reserve(100);
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make params[0] mlock()'d to begin with.
strWalletPass = params[0].get_str().c_str();
if (strWalletPass.length() > 0)
{
if (!pwalletMain->Unlock(strWalletPass))
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
}
else
throw runtime_error(
"walletpassphrase <passphrase> <timeout>\n"
"Stores the wallet decryption key in memory for <timeout> seconds.");
pwalletMain->TopUpKeyPool();
int64_t nSleepTime = params[1].get_int64();
LOCK(cs_nWalletUnlockTime);
nWalletUnlockTime = GetTime() + nSleepTime;
RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime);
return Value::null;
}
Value walletpassphrasechange(const Array& params, bool fHelp)
{
if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
throw runtime_error(
"walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
"\nArguments:\n"
"1. \"oldpassphrase\" (string) The current passphrase\n"
"2. \"newpassphrase\" (string) The new passphrase\n"
"\nExamples:\n"
+ HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
+ HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
);
if (fHelp)
return true;
if (!pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
// TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
// Alternately, find a way to make params[0] mlock()'d to begin with.
SecureString strOldWalletPass;
strOldWalletPass.reserve(100);
strOldWalletPass = params[0].get_str().c_str();
SecureString strNewWalletPass;
strNewWalletPass.reserve(100);
strNewWalletPass = params[1].get_str().c_str();
if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
throw runtime_error(
"walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
"Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
return Value::null;
}
Value walletlock(const Array& params, bool fHelp)
{
if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
throw runtime_error(
"walletlock\n"
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
"After calling this method, you will need to call walletpassphrase again\n"
"before being able to call any methods which require the wallet to be unlocked.\n"
"\nExamples:\n"
"\nSet the passphrase for 2 minutes to perform a transaction\n"
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
"\nPerform a send (requires passphrase set)\n"
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
"\nClear the passphrase since we are done before 2 minutes is up\n"
+ HelpExampleCli("walletlock", "") +
"\nAs json rpc call\n"
+ HelpExampleRpc("walletlock", "")
);
if (fHelp)
return true;
if (!pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
{
LOCK(cs_nWalletUnlockTime);
pwalletMain->Lock();
nWalletUnlockTime = 0;
}
return Value::null;
}
Value encryptwallet(const Array& params, bool fHelp)
{
if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
throw runtime_error(
"encryptwallet \"passphrase\"\n"
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
"After this, any calls that interact with private keys such as sending or signing \n"
"will require the passphrase to be set prior the making these calls.\n"
"Use the walletpassphrase call for this, and then walletlock call.\n"
"If the wallet is already encrypted, use the walletpassphrasechange call.\n"
"Note that this will shutdown the server.\n"
"\nArguments:\n"
"1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n"
"\nExamples:\n"
"\nEncrypt you wallet\n"
+ HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
"\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n"
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
"\nNow we can so something like sign\n"
+ HelpExampleCli("signmessage", "\"bitcoinaddress\" \"test message\"") +
"\nNow lock the wallet again by removing the passphrase\n"
+ HelpExampleCli("walletlock", "") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
);
if (fHelp)
return true;
if (pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make params[0] mlock()'d to begin with.
SecureString strWalletPass;
strWalletPass.reserve(100);
strWalletPass = params[0].get_str().c_str();
if (strWalletPass.length() < 1)
throw runtime_error(
"encryptwallet <passphrase>\n"
"Encrypts the wallet with <passphrase>.");
if (!pwalletMain->EncryptWallet(strWalletPass))
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
// BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
StartShutdown();
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
}
Value lockunspent(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n"
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
"Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
"is always cleared (by virtue of process exit) when a node stops or fails.\n"
"Also see the listunspent call\n"
"\nArguments:\n"
"1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n"
"2. \"transactions\" (string, required) A json array of objects. Each object the txid (string) vout (numeric)\n"
" [ (json array of json objects)\n"
" {\n"
" \"txid\":\"id\", (string) The transaction id\n"
" \"vout\": n (numeric) The output number\n"
" }\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"true|false (boolean) Whether the command was successful or not\n"
"\nExamples:\n"
"\nList the unspent transactions\n"
+ HelpExampleCli("listunspent", "") +
"\nLock an unspent transaction\n"
+ HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
"\nList the locked transactions\n"
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
);
if (params.size() == 1)
RPCTypeCheck(params, list_of(bool_type));
else
RPCTypeCheck(params, list_of(bool_type)(array_type));
bool fUnlock = params[0].get_bool();
if (params.size() == 1) {
if (fUnlock)
pwalletMain->UnlockAllCoins();
return true;
}
Array outputs = params[1].get_array();
BOOST_FOREACH(Value& output, outputs)
{
if (output.type() != obj_type)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
const Object& o = output.get_obj();
RPCTypeCheck(o, map_list_of("txid", str_type)("vout", int_type));
string txid = find_value(o, "txid").get_str();
if (!IsHex(txid))
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
int nOutput = find_value(o, "vout").get_int();
if (nOutput < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
- COutPoint outpt(uint256(txid), nOutput);
+ COutPoint outpt(uint256S(txid), nOutput);
if (fUnlock)
pwalletMain->UnlockCoin(outpt);
else
pwalletMain->LockCoin(outpt);
}
return true;
}
Value listlockunspent(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 0)
throw runtime_error(
"listlockunspent\n"
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for spending.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"txid\" : \"transactionid\", (string) The transaction id locked\n"
" \"vout\" : n (numeric) The vout value\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n"
"\nList the unspent transactions\n"
+ HelpExampleCli("listunspent", "") +
"\nLock an unspent transaction\n"
+ HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
"\nList the locked transactions\n"
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("listlockunspent", "")
);
vector<COutPoint> vOutpts;
pwalletMain->ListLockedCoins(vOutpts);
Array ret;
BOOST_FOREACH(COutPoint &outpt, vOutpts) {
Object o;
o.push_back(Pair("txid", outpt.hash.GetHex()));
o.push_back(Pair("vout", (int)outpt.n));
ret.push_back(o);
}
return ret;
}
Value settxfee(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 1)
throw runtime_error(
"settxfee amount\n"
"\nSet the transaction fee per kB.\n"
"\nArguments:\n"
"1. amount (numeric, required) The transaction fee in BTC/kB rounded to the nearest 0.00000001\n"
"\nResult\n"
"true|false (boolean) Returns true if successful\n"
"\nExamples:\n"
+ HelpExampleCli("settxfee", "0.00001")
+ HelpExampleRpc("settxfee", "0.00001")
);
// Amount
CAmount nAmount = 0;
if (params[0].get_real() != 0.0)
nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts
payTxFee = CFeeRate(nAmount, 1000);
return true;
}
Value getwalletinfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getwalletinfo\n"
"Returns an object containing various wallet state info.\n"
"\nResult:\n"
"{\n"
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
" \"balance\": xxxxxxx, (numeric) the total confirmed bitcoin balance of the wallet\n"
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed bitcoin balance of the wallet\n"
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet\n"
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
+ HelpExampleRpc("getwalletinfo", "")
);
Object obj;
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance())));
obj.push_back(Pair("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance())));
obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size()));
obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
if (pwalletMain->IsCrypted())
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
return obj;
}
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 80a32e78f..e979f61f6 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1,1132 +1,1133 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "interpreter.h"
#include "primitives/transaction.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "eccryptoverify.h"
#include "pubkey.h"
#include "script/script.h"
#include "uint256.h"
using namespace std;
typedef vector<unsigned char> valtype;
namespace {
inline bool set_success(ScriptError* ret)
{
if (ret)
*ret = SCRIPT_ERR_OK;
return true;
}
inline bool set_error(ScriptError* ret, const ScriptError serror)
{
if (ret)
*ret = serror;
return false;
}
} // anon namespace
bool CastToBool(const valtype& vch)
{
for (unsigned int i = 0; i < vch.size(); i++)
{
if (vch[i] != 0)
{
// Can be negative zero
if (i == vch.size()-1 && vch[i] == 0x80)
return false;
return true;
}
}
return false;
}
/**
* Script is a stack machine (like Forth) that evaluates a predicate
* returning a bool indicating valid or not. There are no loops.
*/
#define stacktop(i) (stack.at(stack.size()+(i)))
#define altstacktop(i) (altstack.at(altstack.size()+(i)))
static inline void popstack(vector<valtype>& stack)
{
if (stack.empty())
throw runtime_error("popstack() : stack empty");
stack.pop_back();
}
bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
if (vchPubKey.size() < 33) {
// Non-canonical public key: too short
return false;
}
if (vchPubKey[0] == 0x04) {
if (vchPubKey.size() != 65) {
// Non-canonical public key: invalid length for uncompressed key
return false;
}
} else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
if (vchPubKey.size() != 33) {
// Non-canonical public key: invalid length for compressed key
return false;
}
} else {
// Non-canonical public key: neither compressed nor uncompressed
return false;
}
return true;
}
/**
* A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
* Where R and S are not negative (their first byte has its highest bit not set), and not
* excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
* in which case a single 0 byte is necessary and even required).
*
* See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
*/
bool static IsDERSignature(const valtype &vchSig) {
if (vchSig.size() < 9) {
// Non-canonical signature: too short
return false;
}
if (vchSig.size() > 73) {
// Non-canonical signature: too long
return false;
}
if (vchSig[0] != 0x30) {
// Non-canonical signature: wrong type
return false;
}
if (vchSig[1] != vchSig.size()-3) {
// Non-canonical signature: wrong length marker
return false;
}
unsigned int nLenR = vchSig[3];
if (5 + nLenR >= vchSig.size()) {
// Non-canonical signature: S length misplaced
return false;
}
unsigned int nLenS = vchSig[5+nLenR];
if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) {
// Non-canonical signature: R+S length mismatch
return false;
}
const unsigned char *R = &vchSig[4];
if (R[-2] != 0x02) {
// Non-canonical signature: R value type mismatch
return false;
}
if (nLenR == 0) {
// Non-canonical signature: R length is zero
return false;
}
if (R[0] & 0x80) {
// Non-canonical signature: R value negative
return false;
}
if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) {
// Non-canonical signature: R value excessively padded
return false;
}
const unsigned char *S = &vchSig[6+nLenR];
if (S[-2] != 0x02) {
// Non-canonical signature: S value type mismatch
return false;
}
if (nLenS == 0) {
// Non-canonical signature: S length is zero
return false;
}
if (S[0] & 0x80) {
// Non-canonical signature: S value negative
return false;
}
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) {
// Non-canonical signature: S value excessively padded
return false;
}
return true;
}
bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
if (!IsDERSignature(vchSig)) {
return set_error(serror, SCRIPT_ERR_SIG_DER);
}
unsigned int nLenR = vchSig[3];
unsigned int nLenS = vchSig[5+nLenR];
const unsigned char *S = &vchSig[6+nLenR];
// If the S value is above the order of the curve divided by two, its
// complement modulo the order could have been used instead, which is
// one byte shorter when encoded correctly.
if (!eccrypto::CheckSignatureElement(S, nLenS, true))
return set_error(serror, SCRIPT_ERR_SIG_HIGH_S);
return true;
}
bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
if (vchSig.size() == 0) {
return false;
}
unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));
if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
return false;
return true;
}
bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsDERSignature(vchSig)) {
return set_error(serror, SCRIPT_ERR_SIG_DER);
} else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig, serror)) {
// serror is set
return false;
} else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSig)) {
return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE);
}
return true;
}
bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) {
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
}
return true;
}
bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
if (data.size() == 0) {
// Could have used OP_0.
return opcode == OP_0;
} else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) {
// Could have used OP_1 .. OP_16.
return opcode == OP_1 + (data[0] - 1);
} else if (data.size() == 1 && data[0] == 0x81) {
// Could have used OP_1NEGATE.
return opcode == OP_1NEGATE;
} else if (data.size() <= 75) {
// Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
return opcode == data.size();
} else if (data.size() <= 255) {
// Could have used OP_PUSHDATA.
return opcode == OP_PUSHDATA1;
} else if (data.size() <= 65535) {
// Could have used OP_PUSHDATA2.
return opcode == OP_PUSHDATA2;
}
return true;
}
bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
static const CScriptNum bnZero(0);
static const CScriptNum bnOne(1);
static const CScriptNum bnFalse(0);
static const CScriptNum bnTrue(1);
static const valtype vchFalse(0);
static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
opcodetype opcode;
valtype vchPushValue;
vector<bool> vfExec;
vector<valtype> altstack;
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
if (script.size() > 10000)
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
int nOpCount = 0;
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;
try
{
while (pc < pend)
{
bool fExec = !count(vfExec.begin(), vfExec.end(), false);
//
// Read instruction
//
if (!script.GetOp(pc, opcode, vchPushValue))
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE)
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
// Note how OP_RESERVED does not count towards the opcode limit.
if (opcode > OP_16 && ++nOpCount > 201)
return set_error(serror, SCRIPT_ERR_OP_COUNT);
if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
opcode == OP_LEFT ||
opcode == OP_RIGHT ||
opcode == OP_INVERT ||
opcode == OP_AND ||
opcode == OP_OR ||
opcode == OP_XOR ||
opcode == OP_2MUL ||
opcode == OP_2DIV ||
opcode == OP_MUL ||
opcode == OP_DIV ||
opcode == OP_MOD ||
opcode == OP_LSHIFT ||
opcode == OP_RSHIFT)
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
return set_error(serror, SCRIPT_ERR_MINIMALDATA);
}
stack.push_back(vchPushValue);
} else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
switch (opcode)
{
//
// Push value
//
case OP_1NEGATE:
case OP_1:
case OP_2:
case OP_3:
case OP_4:
case OP_5:
case OP_6:
case OP_7:
case OP_8:
case OP_9:
case OP_10:
case OP_11:
case OP_12:
case OP_13:
case OP_14:
case OP_15:
case OP_16:
{
// ( -- value)
CScriptNum bn((int)opcode - (int)(OP_1 - 1));
stack.push_back(bn.getvch());
// The result of these opcodes should always be the minimal way to push the data
// they push, so no need for a CheckMinimalPush here.
}
break;
//
// Control
//
case OP_NOP:
break;
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
{
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
}
break;
case OP_IF:
case OP_NOTIF:
{
// <expression> if [statements] [else [statements]] endif
bool fValue = false;
if (fExec)
{
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
valtype& vch = stacktop(-1);
fValue = CastToBool(vch);
if (opcode == OP_NOTIF)
fValue = !fValue;
popstack(stack);
}
vfExec.push_back(fValue);
}
break;
case OP_ELSE:
{
if (vfExec.empty())
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.back() = !vfExec.back();
}
break;
case OP_ENDIF:
{
if (vfExec.empty())
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
vfExec.pop_back();
}
break;
case OP_VERIFY:
{
// (true -- ) or
// (false -- false) and return
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
bool fValue = CastToBool(stacktop(-1));
if (fValue)
popstack(stack);
else
return set_error(serror, SCRIPT_ERR_VERIFY);
}
break;
case OP_RETURN:
{
return set_error(serror, SCRIPT_ERR_OP_RETURN);
}
break;
//
// Stack ops
//
case OP_TOALTSTACK:
{
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
altstack.push_back(stacktop(-1));
popstack(stack);
}
break;
case OP_FROMALTSTACK:
{
if (altstack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_ALTSTACK_OPERATION);
stack.push_back(altstacktop(-1));
popstack(altstack);
}
break;
case OP_2DROP:
{
// (x1 x2 -- )
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack);
popstack(stack);
}
break;
case OP_2DUP:
{
// (x1 x2 -- x1 x2 x1 x2)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-2);
valtype vch2 = stacktop(-1);
stack.push_back(vch1);
stack.push_back(vch2);
}
break;
case OP_3DUP:
{
// (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
if (stack.size() < 3)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-3);
valtype vch2 = stacktop(-2);
valtype vch3 = stacktop(-1);
stack.push_back(vch1);
stack.push_back(vch2);
stack.push_back(vch3);
}
break;
case OP_2OVER:
{
// (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
if (stack.size() < 4)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-4);
valtype vch2 = stacktop(-3);
stack.push_back(vch1);
stack.push_back(vch2);
}
break;
case OP_2ROT:
{
// (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
if (stack.size() < 6)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-6);
valtype vch2 = stacktop(-5);
stack.erase(stack.end()-6, stack.end()-4);
stack.push_back(vch1);
stack.push_back(vch2);
}
break;
case OP_2SWAP:
{
// (x1 x2 x3 x4 -- x3 x4 x1 x2)
if (stack.size() < 4)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-4), stacktop(-2));
swap(stacktop(-3), stacktop(-1));
}
break;
case OP_IFDUP:
{
// (x - 0 | x x)
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1);
if (CastToBool(vch))
stack.push_back(vch);
}
break;
case OP_DEPTH:
{
// -- stacksize
CScriptNum bn(stack.size());
stack.push_back(bn.getvch());
}
break;
case OP_DROP:
{
// (x -- )
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
popstack(stack);
}
break;
case OP_DUP:
{
// (x -- x x)
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1);
stack.push_back(vch);
}
break;
case OP_NIP:
{
// (x1 x2 -- x2)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
stack.erase(stack.end() - 2);
}
break;
case OP_OVER:
{
// (x1 x2 -- x1 x2 x1)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-2);
stack.push_back(vch);
}
break;
case OP_PICK:
case OP_ROLL:
{
// (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();
popstack(stack);
if (n < 0 || n >= (int)stack.size())
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-n-1);
if (opcode == OP_ROLL)
stack.erase(stack.end()-n-1);
stack.push_back(vch);
}
break;
case OP_ROT:
{
// (x1 x2 x3 -- x2 x3 x1)
// x2 x1 x3 after first swap
// x2 x3 x1 after second swap
if (stack.size() < 3)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-3), stacktop(-2));
swap(stacktop(-2), stacktop(-1));
}
break;
case OP_SWAP:
{
// (x1 x2 -- x2 x1)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
swap(stacktop(-2), stacktop(-1));
}
break;
case OP_TUCK:
{
// (x1 x2 -- x2 x1 x2)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch = stacktop(-1);
stack.insert(stack.end()-2, vch);
}
break;
case OP_SIZE:
{
// (in -- in size)
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn(stacktop(-1).size());
stack.push_back(bn.getvch());
}
break;
//
// Bitwise logic
//
case OP_EQUAL:
case OP_EQUALVERIFY:
//case OP_NOTEQUAL: // use OP_NUMNOTEQUAL
{
// (x1 x2 - bool)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch1 = stacktop(-2);
valtype& vch2 = stacktop(-1);
bool fEqual = (vch1 == vch2);
// OP_NOTEQUAL is disabled because it would be too easy to say
// something like n != 1 and have some wiseguy pass in 1 with extra
// zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
//if (opcode == OP_NOTEQUAL)
// fEqual = !fEqual;
popstack(stack);
popstack(stack);
stack.push_back(fEqual ? vchTrue : vchFalse);
if (opcode == OP_EQUALVERIFY)
{
if (fEqual)
popstack(stack);
else
return set_error(serror, SCRIPT_ERR_EQUALVERIFY);
}
}
break;
//
// Numeric
//
case OP_1ADD:
case OP_1SUB:
case OP_NEGATE:
case OP_ABS:
case OP_NOT:
case OP_0NOTEQUAL:
{
// (in -- out)
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn(stacktop(-1), fRequireMinimal);
switch (opcode)
{
case OP_1ADD: bn += bnOne; break;
case OP_1SUB: bn -= bnOne; break;
case OP_NEGATE: bn = -bn; break;
case OP_ABS: if (bn < bnZero) bn = -bn; break;
case OP_NOT: bn = (bn == bnZero); break;
case OP_0NOTEQUAL: bn = (bn != bnZero); break;
default: assert(!"invalid opcode"); break;
}
popstack(stack);
stack.push_back(bn.getvch());
}
break;
case OP_ADD:
case OP_SUB:
case OP_BOOLAND:
case OP_BOOLOR:
case OP_NUMEQUAL:
case OP_NUMEQUALVERIFY:
case OP_NUMNOTEQUAL:
case OP_LESSTHAN:
case OP_GREATERTHAN:
case OP_LESSTHANOREQUAL:
case OP_GREATERTHANOREQUAL:
case OP_MIN:
case OP_MAX:
{
// (x1 x2 -- out)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn1(stacktop(-2), fRequireMinimal);
CScriptNum bn2(stacktop(-1), fRequireMinimal);
CScriptNum bn(0);
switch (opcode)
{
case OP_ADD:
bn = bn1 + bn2;
break;
case OP_SUB:
bn = bn1 - bn2;
break;
case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break;
case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break;
case OP_NUMEQUAL: bn = (bn1 == bn2); break;
case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break;
case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break;
case OP_LESSTHAN: bn = (bn1 < bn2); break;
case OP_GREATERTHAN: bn = (bn1 > bn2); break;
case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break;
case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break;
case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break;
case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break;
default: assert(!"invalid opcode"); break;
}
popstack(stack);
popstack(stack);
stack.push_back(bn.getvch());
if (opcode == OP_NUMEQUALVERIFY)
{
if (CastToBool(stacktop(-1)))
popstack(stack);
else
return set_error(serror, SCRIPT_ERR_NUMEQUALVERIFY);
}
}
break;
case OP_WITHIN:
{
// (x min max -- out)
if (stack.size() < 3)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
CScriptNum bn1(stacktop(-3), fRequireMinimal);
CScriptNum bn2(stacktop(-2), fRequireMinimal);
CScriptNum bn3(stacktop(-1), fRequireMinimal);
bool fValue = (bn2 <= bn1 && bn1 < bn3);
popstack(stack);
popstack(stack);
popstack(stack);
stack.push_back(fValue ? vchTrue : vchFalse);
}
break;
//
// Crypto
//
case OP_RIPEMD160:
case OP_SHA1:
case OP_SHA256:
case OP_HASH160:
case OP_HASH256:
{
// (in -- hash)
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch = stacktop(-1);
valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32);
if (opcode == OP_RIPEMD160)
CRIPEMD160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
else if (opcode == OP_SHA1)
CSHA1().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
else if (opcode == OP_SHA256)
CSHA256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
else if (opcode == OP_HASH160)
CHash160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
else if (opcode == OP_HASH256)
CHash256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash));
popstack(stack);
stack.push_back(vchHash);
}
break;
case OP_CODESEPARATOR:
{
// Hash starts after the code separator
pbegincodehash = pc;
}
break;
case OP_CHECKSIG:
case OP_CHECKSIGVERIFY:
{
// (sig pubkey -- bool)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vchSig = stacktop(-2);
valtype& vchPubKey = stacktop(-1);
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
// Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig));
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
//serror is set
return false;
}
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode);
popstack(stack);
popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse);
if (opcode == OP_CHECKSIGVERIFY)
{
if (fSuccess)
popstack(stack);
else
return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
}
}
break;
case OP_CHECKMULTISIG:
case OP_CHECKMULTISIGVERIFY:
{
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
int i = 1;
if ((int)stack.size() < i)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
if (nKeysCount < 0 || nKeysCount > 20)
return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT);
nOpCount += nKeysCount;
if (nOpCount > 201)
return set_error(serror, SCRIPT_ERR_OP_COUNT);
int ikey = ++i;
i += nKeysCount;
if ((int)stack.size() < i)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int nSigsCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
if (nSigsCount < 0 || nSigsCount > nKeysCount)
return set_error(serror, SCRIPT_ERR_SIG_COUNT);
int isig = ++i;
i += nSigsCount;
if ((int)stack.size() < i)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
// Drop the signatures, since there's no way for a signature to sign itself
for (int k = 0; k < nSigsCount; k++)
{
valtype& vchSig = stacktop(-isig-k);
scriptCode.FindAndDelete(CScript(vchSig));
}
bool fSuccess = true;
while (fSuccess && nSigsCount > 0)
{
valtype& vchSig = stacktop(-isig);
valtype& vchPubKey = stacktop(-ikey);
// Note how this makes the exact order of pubkey/signature evaluation
// distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
// See the script_(in)valid tests for details.
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
// serror is set
return false;
}
// Check signature
bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode);
if (fOk) {
isig++;
nSigsCount--;
}
ikey++;
nKeysCount--;
// If there are more signatures left than keys left,
// then too many signatures have failed. Exit early,
// without checking any further signatures.
if (nSigsCount > nKeysCount)
fSuccess = false;
}
// Clean up stack of actual arguments
while (i-- > 1)
popstack(stack);
// A bug causes CHECKMULTISIG to consume one extra argument
// whose contents were not checked in any way.
//
// Unfortunately this is a potential source of mutability,
// so optionally verify it is exactly equal to zero prior
// to removing it from the stack.
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size())
return set_error(serror, SCRIPT_ERR_SIG_NULLDUMMY);
popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse);
if (opcode == OP_CHECKMULTISIGVERIFY)
{
if (fSuccess)
popstack(stack);
else
return set_error(serror, SCRIPT_ERR_CHECKMULTISIGVERIFY);
}
}
break;
default:
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
// Size limits
if (stack.size() + altstack.size() > 1000)
return set_error(serror, SCRIPT_ERR_STACK_SIZE);
}
}
catch (...)
{
return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
}
if (!vfExec.empty())
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
return set_success(serror);
}
namespace {
/**
* Wrapper that serializes like CTransaction, but with the modifications
* required for the signature hash done in-place
*/
class CTransactionSignatureSerializer {
private:
const CTransaction &txTo; //! reference to the spending transaction (the one being serialized)
const CScript &scriptCode; //! output script being consumed
const unsigned int nIn; //! input index of txTo being signed
const bool fAnyoneCanPay; //! whether the hashtype has the SIGHASH_ANYONECANPAY flag set
const bool fHashSingle; //! whether the hashtype is SIGHASH_SINGLE
const bool fHashNone; //! whether the hashtype is SIGHASH_NONE
public:
CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn),
fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)),
fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE),
fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {}
/** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */
template<typename S>
void SerializeScriptCode(S &s, int nType, int nVersion) const {
CScript::const_iterator it = scriptCode.begin();
CScript::const_iterator itBegin = it;
opcodetype opcode;
unsigned int nCodeSeparators = 0;
while (scriptCode.GetOp(it, opcode)) {
if (opcode == OP_CODESEPARATOR)
nCodeSeparators++;
}
::WriteCompactSize(s, scriptCode.size() - nCodeSeparators);
it = itBegin;
while (scriptCode.GetOp(it, opcode)) {
if (opcode == OP_CODESEPARATOR) {
s.write((char*)&itBegin[0], it-itBegin-1);
itBegin = it;
}
}
if (itBegin != scriptCode.end())
s.write((char*)&itBegin[0], it-itBegin);
}
/** Serialize an input of txTo */
template<typename S>
void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const {
// In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized
if (fAnyoneCanPay)
nInput = nIn;
// Serialize the prevout
::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion);
// Serialize the script
if (nInput != nIn)
// Blank out other inputs' signatures
::Serialize(s, CScript(), nType, nVersion);
else
SerializeScriptCode(s, nType, nVersion);
// Serialize the nSequence
if (nInput != nIn && (fHashSingle || fHashNone))
// let the others update at will
::Serialize(s, (int)0, nType, nVersion);
else
::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion);
}
/** Serialize an output of txTo */
template<typename S>
void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const {
if (fHashSingle && nOutput != nIn)
// Do not lock-in the txout payee at other indices as txin
::Serialize(s, CTxOut(), nType, nVersion);
else
::Serialize(s, txTo.vout[nOutput], nType, nVersion);
}
/** Serialize txTo */
template<typename S>
void Serialize(S &s, int nType, int nVersion) const {
// Serialize nVersion
::Serialize(s, txTo.nVersion, nType, nVersion);
// Serialize vin
unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
::WriteCompactSize(s, nInputs);
for (unsigned int nInput = 0; nInput < nInputs; nInput++)
SerializeInput(s, nInput, nType, nVersion);
// Serialize vout
unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size());
::WriteCompactSize(s, nOutputs);
for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
SerializeOutput(s, nOutput, nType, nVersion);
// Serialize nLockTime
::Serialize(s, txTo.nLockTime, nType, nVersion);
}
};
} // anon namespace
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
+ static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
if (nIn >= txTo.vin.size()) {
// nIn out of range
- return 1;
+ return one;
}
// Check for invalid use of SIGHASH_SINGLE
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
if (nIn >= txTo.vout.size()) {
// nOut out of range
- return 1;
+ return one;
}
}
// Wrapper to serialize only the necessary parts of the transaction being signed
CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType);
// Serialize and hash
CHashWriter ss(SER_GETHASH, 0);
ss << txTmp << nHashType;
return ss.GetHash();
}
bool SignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
return pubkey.Verify(sighash, vchSig);
}
bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vector<unsigned char>& vchPubKey, const CScript& scriptCode) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
return false;
// Hash type is one byte tacked on to the end of the signature
vector<unsigned char> vchSig(vchSigIn);
if (vchSig.empty())
return false;
int nHashType = vchSig.back();
vchSig.pop_back();
uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType);
if (!VerifySignature(vchSig, pubkey, sighash))
return false;
return true;
}
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) {
return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
}
vector<vector<unsigned char> > stack, stackCopy;
if (!EvalScript(stack, scriptSig, flags, checker, serror))
// serror is set
return false;
if (flags & SCRIPT_VERIFY_P2SH)
stackCopy = stack;
if (!EvalScript(stack, scriptPubKey, flags, checker, serror))
// serror is set
return false;
if (stack.empty())
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
if (CastToBool(stack.back()) == false)
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
// Additional validation for spend-to-script-hash transactions:
if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash())
{
// scriptSig must be literals-only or validation fails
if (!scriptSig.IsPushOnly())
return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
// stackCopy cannot be empty here, because if it was the
// P2SH HASH <> EQUAL scriptPubKey would be evaluated with
// an empty stack and the EvalScript above would return false.
assert(!stackCopy.empty());
const valtype& pubKeySerialized = stackCopy.back();
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
popstack(stackCopy);
if (!EvalScript(stackCopy, pubKey2, flags, checker, serror))
// serror is set
return false;
if (stackCopy.empty())
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
if (!CastToBool(stackCopy.back()))
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
else
return set_success(serror);
}
return set_success(serror);
}
diff --git a/src/script/standard.h b/src/script/standard.h
index 1d29ef57a..dbeeace4d 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -1,93 +1,93 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
#include "script/interpreter.h"
#include "uint256.h"
#include <boost/variant.hpp>
#include <stdint.h>
class CKeyID;
class CScript;
/** A reference to a CScript: the Hash160 of its serialization (see script.h) */
class CScriptID : public uint160
{
public:
- CScriptID() : uint160(0) {}
+ CScriptID() : uint160() {}
CScriptID(const CScript& in);
CScriptID(const uint160& in) : uint160(in) {}
};
static const unsigned int MAX_OP_RETURN_RELAY = 40; //! bytes
extern unsigned nMaxDatacarrierBytes;
/**
* Mandatory script verification flags that all new blocks must comply with for
* them to be valid. (but old blocks may not comply with) Currently just P2SH,
* but in the future other flags may be added, such as a soft-fork to enforce
* strict DER encoding.
*
* Failing one of these tests may trigger a DoS ban - see CheckInputs() for
* details.
*/
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
* blocks and we must accept those blocks.
*/
static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
SCRIPT_VERIFY_STRICTENC |
SCRIPT_VERIFY_MINIMALDATA |
SCRIPT_VERIFY_NULLDUMMY |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS;
/** For convenience, standard but not mandatory verify flags. */
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
enum txnouttype
{
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEYHASH,
TX_SCRIPTHASH,
TX_MULTISIG,
TX_NULL_DATA,
};
class CNoDestination {
public:
friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; }
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
* * CKeyID: TX_PUBKEYHASH destination
* * CScriptID: TX_SCRIPTHASH destination
* A CTxDestination is the internal data type encoded in a CBitcoinAddress
*/
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
const char* GetTxnOutputType(txnouttype t);
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
CScript GetScriptForDestination(const CTxDestination& dest);
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
#endif // BITCOIN_SCRIPT_STANDARD_H
diff --git a/src/test/Checkpoints_tests.cpp b/src/test/Checkpoints_tests.cpp
index 4ea6a91d5..a9b6cd44a 100644
--- a/src/test/Checkpoints_tests.cpp
+++ b/src/test/Checkpoints_tests.cpp
@@ -1,38 +1,38 @@
// Copyright (c) 2011-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// Unit tests for block-chain checkpoints
//
#include "checkpoints.h"
#include "uint256.h"
#include <boost/test/unit_test.hpp>
using namespace std;
BOOST_AUTO_TEST_SUITE(Checkpoints_tests)
BOOST_AUTO_TEST_CASE(sanity)
{
- uint256 p11111 = uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d");
- uint256 p134444 = uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe");
+ uint256 p11111 = uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d");
+ uint256 p134444 = uint256S("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe");
BOOST_CHECK(Checkpoints::CheckBlock(11111, p11111));
BOOST_CHECK(Checkpoints::CheckBlock(134444, p134444));
// Wrong hashes at checkpoints should fail:
BOOST_CHECK(!Checkpoints::CheckBlock(11111, p134444));
BOOST_CHECK(!Checkpoints::CheckBlock(134444, p11111));
// ... but any hash not at a checkpoint should succeed:
BOOST_CHECK(Checkpoints::CheckBlock(11111+1, p134444));
BOOST_CHECK(Checkpoints::CheckBlock(134444+1, p11111));
BOOST_CHECK(Checkpoints::GetTotalBlocksEstimate() >= 134444);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
new file mode 100644
index 000000000..565b02ae6
--- /dev/null
+++ b/src/test/arith_uint256_tests.cpp
@@ -0,0 +1,566 @@
+// Copyright (c) 2011-2013 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 <boost/test/unit_test.hpp>
+#include <stdint.h>
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <cmath>
+#include "uint256.h"
+#include "arith_uint256.h"
+#include <string>
+#include "version.h"
+
+BOOST_AUTO_TEST_SUITE(arith_uint256_tests)
+
+/// Convert vector to arith_uint256, via uint256 blob
+inline arith_uint256 arith_uint256V(const std::vector<unsigned char>& vch)
+{
+ return UintToArith256(uint256(vch));
+}
+
+const unsigned char R1Array[] =
+ "\x9c\x52\x4a\xdb\xcf\x56\x11\x12\x2b\x29\x12\x5e\x5d\x35\xd2\xd2"
+ "\x22\x81\xaa\xb5\x33\xf0\x08\x32\xd5\x56\xb1\xf9\xea\xe5\x1d\x7d";
+const char R1ArrayHex[] = "7D1DE5EAF9B156D53208F033B5AA8122D2d2355d5e12292b121156cfdb4a529c";
+const double R1Ldouble = 0.4887374590559308955; // R1L equals roughly R1Ldouble * 2^256
+const arith_uint256 R1L = arith_uint256V(std::vector<unsigned char>(R1Array,R1Array+32));
+const uint64_t R1LLow64 = 0x121156cfdb4a529cULL;
+
+const unsigned char R2Array[] =
+ "\x70\x32\x1d\x7c\x47\xa5\x6b\x40\x26\x7e\x0a\xc3\xa6\x9c\xb6\xbf"
+ "\x13\x30\x47\xa3\x19\x2d\xda\x71\x49\x13\x72\xf0\xb4\xca\x81\xd7";
+const arith_uint256 R2L = arith_uint256V(std::vector<unsigned char>(R2Array,R2Array+32));
+
+const char R1LplusR2L[] = "549FB09FEA236A1EA3E31D4D58F1B1369288D204211CA751527CFC175767850C";
+
+const unsigned char ZeroArray[] =
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+const arith_uint256 ZeroL = arith_uint256V(std::vector<unsigned char>(ZeroArray,ZeroArray+32));
+
+const unsigned char OneArray[] =
+ "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+const arith_uint256 OneL = arith_uint256V(std::vector<unsigned char>(OneArray,OneArray+32));
+
+const unsigned char MaxArray[] =
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+const arith_uint256 MaxL = arith_uint256V(std::vector<unsigned char>(MaxArray,MaxArray+32));
+
+const arith_uint256 HalfL = (OneL << 255);
+std::string ArrayToString(const unsigned char A[], unsigned int width)
+{
+ std::stringstream Stream;
+ Stream << std::hex;
+ for (unsigned int i = 0; i < width; ++i)
+ {
+ Stream<<std::setw(2)<<std::setfill('0')<<(unsigned int)A[width-i-1];
+ }
+ return Stream.str();
+}
+
+BOOST_AUTO_TEST_CASE( basics ) // constructors, equality, inequality
+{
+ BOOST_CHECK(1 == 0+1);
+ // constructor arith_uint256(vector<char>):
+ BOOST_CHECK(R1L.ToString() == ArrayToString(R1Array,32));
+ BOOST_CHECK(R2L.ToString() == ArrayToString(R2Array,32));
+ BOOST_CHECK(ZeroL.ToString() == ArrayToString(ZeroArray,32));
+ BOOST_CHECK(OneL.ToString() == ArrayToString(OneArray,32));
+ BOOST_CHECK(MaxL.ToString() == ArrayToString(MaxArray,32));
+ BOOST_CHECK(OneL.ToString() != ArrayToString(ZeroArray,32));
+
+ // == and !=
+ BOOST_CHECK(R1L != R2L);
+ BOOST_CHECK(ZeroL != OneL);
+ BOOST_CHECK(OneL != ZeroL);
+ BOOST_CHECK(MaxL != ZeroL);
+ BOOST_CHECK(~MaxL == ZeroL);
+ BOOST_CHECK( ((R1L ^ R2L) ^ R1L) == R2L);
+
+ uint64_t Tmp64 = 0xc4dab720d9c7acaaULL;
+ for (unsigned int i = 0; i < 256; ++i)
+ {
+ BOOST_CHECK(ZeroL != (OneL << i));
+ BOOST_CHECK((OneL << i) != ZeroL);
+ BOOST_CHECK(R1L != (R1L ^ (OneL << i)));
+ BOOST_CHECK(((arith_uint256(Tmp64) ^ (OneL << i) ) != Tmp64 ));
+ }
+ BOOST_CHECK(ZeroL == (OneL << 256));
+
+ // String Constructor and Copy Constructor
+ BOOST_CHECK(arith_uint256("0x"+R1L.ToString()) == R1L);
+ BOOST_CHECK(arith_uint256("0x"+R2L.ToString()) == R2L);
+ BOOST_CHECK(arith_uint256("0x"+ZeroL.ToString()) == ZeroL);
+ BOOST_CHECK(arith_uint256("0x"+OneL.ToString()) == OneL);
+ BOOST_CHECK(arith_uint256("0x"+MaxL.ToString()) == MaxL);
+ BOOST_CHECK(arith_uint256(R1L.ToString()) == R1L);
+ BOOST_CHECK(arith_uint256(" 0x"+R1L.ToString()+" ") == R1L);
+ BOOST_CHECK(arith_uint256("") == ZeroL);
+ BOOST_CHECK(R1L == arith_uint256(R1ArrayHex));
+ BOOST_CHECK(arith_uint256(R1L) == R1L);
+ BOOST_CHECK((arith_uint256(R1L^R2L)^R2L) == R1L);
+ BOOST_CHECK(arith_uint256(ZeroL) == ZeroL);
+ BOOST_CHECK(arith_uint256(OneL) == OneL);
+
+ // uint64_t constructor
+ BOOST_CHECK( (R1L & arith_uint256("0xffffffffffffffff")) == arith_uint256(R1LLow64));
+ BOOST_CHECK(ZeroL == arith_uint256(0));
+ BOOST_CHECK(OneL == arith_uint256(1));
+ BOOST_CHECK(arith_uint256("0xffffffffffffffff") = arith_uint256(0xffffffffffffffffULL));
+
+ // Assignment (from base_uint)
+ arith_uint256 tmpL = ~ZeroL; BOOST_CHECK(tmpL == ~ZeroL);
+ tmpL = ~OneL; BOOST_CHECK(tmpL == ~OneL);
+ tmpL = ~R1L; BOOST_CHECK(tmpL == ~R1L);
+ tmpL = ~R2L; BOOST_CHECK(tmpL == ~R2L);
+ tmpL = ~MaxL; BOOST_CHECK(tmpL == ~MaxL);
+}
+
+void shiftArrayRight(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
+{
+ for (unsigned int T=0; T < arrayLength; ++T)
+ {
+ unsigned int F = (T+bitsToShift/8);
+ if (F < arrayLength)
+ to[T] = from[F] >> (bitsToShift%8);
+ else
+ to[T] = 0;
+ if (F + 1 < arrayLength)
+ to[T] |= from[(F+1)] << (8-bitsToShift%8);
+ }
+}
+
+void shiftArrayLeft(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
+{
+ for (unsigned int T=0; T < arrayLength; ++T)
+ {
+ if (T >= bitsToShift/8)
+ {
+ unsigned int F = T-bitsToShift/8;
+ to[T] = from[F] << (bitsToShift%8);
+ if (T >= bitsToShift/8+1)
+ to[T] |= from[F-1] >> (8-bitsToShift%8);
+ }
+ else {
+ to[T] = 0;
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( shifts ) { // "<<" ">>" "<<=" ">>="
+ unsigned char TmpArray[32];
+ arith_uint256 TmpL;
+ for (unsigned int i = 0; i < 256; ++i)
+ {
+ shiftArrayLeft(TmpArray, OneArray, 32, i);
+ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (OneL << i));
+ TmpL = OneL; TmpL <<= i;
+ BOOST_CHECK(TmpL == (OneL << i));
+ BOOST_CHECK((HalfL >> (255-i)) == (OneL << i));
+ TmpL = HalfL; TmpL >>= (255-i);
+ BOOST_CHECK(TmpL == (OneL << i));
+
+ shiftArrayLeft(TmpArray, R1Array, 32, i);
+ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (R1L << i));
+ TmpL = R1L; TmpL <<= i;
+ BOOST_CHECK(TmpL == (R1L << i));
+
+ shiftArrayRight(TmpArray, R1Array, 32, i);
+ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (R1L >> i));
+ TmpL = R1L; TmpL >>= i;
+ BOOST_CHECK(TmpL == (R1L >> i));
+
+ shiftArrayLeft(TmpArray, MaxArray, 32, i);
+ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (MaxL << i));
+ TmpL = MaxL; TmpL <<= i;
+ BOOST_CHECK(TmpL == (MaxL << i));
+
+ shiftArrayRight(TmpArray, MaxArray, 32, i);
+ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (MaxL >> i));
+ TmpL = MaxL; TmpL >>= i;
+ BOOST_CHECK(TmpL == (MaxL >> i));
+ }
+ arith_uint256 c1L = arith_uint256(0x0123456789abcdefULL);
+ arith_uint256 c2L = c1L << 128;
+ for (unsigned int i = 0; i < 128; ++i) {
+ BOOST_CHECK((c1L << i) == (c2L >> (128-i)));
+ }
+ for (unsigned int i = 128; i < 256; ++i) {
+ BOOST_CHECK((c1L << i) == (c2L << (i-128)));
+ }
+}
+
+BOOST_AUTO_TEST_CASE( unaryOperators ) // ! ~ -
+{
+ BOOST_CHECK(!ZeroL);
+ BOOST_CHECK(!(!OneL));
+ for (unsigned int i = 0; i < 256; ++i)
+ BOOST_CHECK(!(!(OneL<<i)));
+ BOOST_CHECK(!(!R1L));
+ BOOST_CHECK(!(!MaxL));
+
+ BOOST_CHECK(~ZeroL == MaxL);
+
+ unsigned char TmpArray[32];
+ for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = ~R1Array[i]; }
+ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (~R1L));
+
+ BOOST_CHECK(-ZeroL == ZeroL);
+ BOOST_CHECK(-R1L == (~R1L)+1);
+ for (unsigned int i = 0; i < 256; ++i)
+ BOOST_CHECK(-(OneL<<i) == (MaxL << i));
+}
+
+
+// Check if doing _A_ _OP_ _B_ results in the same as applying _OP_ onto each
+// element of Aarray and Barray, and then converting the result into a arith_uint256.
+#define CHECKBITWISEOPERATOR(_A_,_B_,_OP_) \
+ for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = _A_##Array[i] _OP_ _B_##Array[i]; } \
+ BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (_A_##L _OP_ _B_##L));
+
+#define CHECKASSIGNMENTOPERATOR(_A_,_B_,_OP_) \
+ TmpL = _A_##L; TmpL _OP_##= _B_##L; BOOST_CHECK(TmpL == (_A_##L _OP_ _B_##L));
+
+BOOST_AUTO_TEST_CASE( bitwiseOperators )
+{
+ unsigned char TmpArray[32];
+
+ CHECKBITWISEOPERATOR(R1,R2,|)
+ CHECKBITWISEOPERATOR(R1,R2,^)
+ CHECKBITWISEOPERATOR(R1,R2,&)
+ CHECKBITWISEOPERATOR(R1,Zero,|)
+ CHECKBITWISEOPERATOR(R1,Zero,^)
+ CHECKBITWISEOPERATOR(R1,Zero,&)
+ CHECKBITWISEOPERATOR(R1,Max,|)
+ CHECKBITWISEOPERATOR(R1,Max,^)
+ CHECKBITWISEOPERATOR(R1,Max,&)
+ CHECKBITWISEOPERATOR(Zero,R1,|)
+ CHECKBITWISEOPERATOR(Zero,R1,^)
+ CHECKBITWISEOPERATOR(Zero,R1,&)
+ CHECKBITWISEOPERATOR(Max,R1,|)
+ CHECKBITWISEOPERATOR(Max,R1,^)
+ CHECKBITWISEOPERATOR(Max,R1,&)
+
+ arith_uint256 TmpL;
+ CHECKASSIGNMENTOPERATOR(R1,R2,|)
+ CHECKASSIGNMENTOPERATOR(R1,R2,^)
+ CHECKASSIGNMENTOPERATOR(R1,R2,&)
+ CHECKASSIGNMENTOPERATOR(R1,Zero,|)
+ CHECKASSIGNMENTOPERATOR(R1,Zero,^)
+ CHECKASSIGNMENTOPERATOR(R1,Zero,&)
+ CHECKASSIGNMENTOPERATOR(R1,Max,|)
+ CHECKASSIGNMENTOPERATOR(R1,Max,^)
+ CHECKASSIGNMENTOPERATOR(R1,Max,&)
+ CHECKASSIGNMENTOPERATOR(Zero,R1,|)
+ CHECKASSIGNMENTOPERATOR(Zero,R1,^)
+ CHECKASSIGNMENTOPERATOR(Zero,R1,&)
+ CHECKASSIGNMENTOPERATOR(Max,R1,|)
+ CHECKASSIGNMENTOPERATOR(Max,R1,^)
+ CHECKASSIGNMENTOPERATOR(Max,R1,&)
+
+ uint64_t Tmp64 = 0xe1db685c9a0b47a2ULL;
+ TmpL = R1L; TmpL |= Tmp64; BOOST_CHECK(TmpL == (R1L | arith_uint256(Tmp64)));
+ TmpL = R1L; TmpL |= 0; BOOST_CHECK(TmpL == R1L);
+ TmpL ^= 0; BOOST_CHECK(TmpL == R1L);
+ TmpL ^= Tmp64; BOOST_CHECK(TmpL == (R1L ^ arith_uint256(Tmp64)));
+}
+
+BOOST_AUTO_TEST_CASE( comparison ) // <= >= < >
+{
+ arith_uint256 TmpL;
+ for (unsigned int i = 0; i < 256; ++i) {
+ TmpL= OneL<< i;
+ BOOST_CHECK( TmpL >= ZeroL && TmpL > ZeroL && ZeroL < TmpL && ZeroL <= TmpL);
+ BOOST_CHECK( TmpL >= 0 && TmpL > 0 && 0 < TmpL && 0 <= TmpL);
+ TmpL |= R1L;
+ BOOST_CHECK( TmpL >= R1L ); BOOST_CHECK( (TmpL == R1L) != (TmpL > R1L)); BOOST_CHECK( (TmpL == R1L) || !( TmpL <= R1L));
+ BOOST_CHECK( R1L <= TmpL ); BOOST_CHECK( (R1L == TmpL) != (R1L < TmpL)); BOOST_CHECK( (TmpL == R1L) || !( R1L >= TmpL));
+ BOOST_CHECK(! (TmpL < R1L)); BOOST_CHECK(! (R1L > TmpL));
+ }
+}
+
+BOOST_AUTO_TEST_CASE( plusMinus )
+{
+ arith_uint256 TmpL = 0;
+ BOOST_CHECK(R1L+R2L == arith_uint256(R1LplusR2L));
+ TmpL += R1L;
+ BOOST_CHECK(TmpL == R1L);
+ TmpL += R2L;
+ BOOST_CHECK(TmpL == R1L + R2L);
+ BOOST_CHECK(OneL+MaxL == ZeroL);
+ BOOST_CHECK(MaxL+OneL == ZeroL);
+ for (unsigned int i = 1; i < 256; ++i) {
+ BOOST_CHECK( (MaxL >> i) + OneL == (HalfL >> (i-1)) );
+ BOOST_CHECK( OneL + (MaxL >> i) == (HalfL >> (i-1)) );
+ TmpL = (MaxL>>i); TmpL += OneL;
+ BOOST_CHECK( TmpL == (HalfL >> (i-1)) );
+ TmpL = (MaxL>>i); TmpL += 1;
+ BOOST_CHECK( TmpL == (HalfL >> (i-1)) );
+ TmpL = (MaxL>>i);
+ BOOST_CHECK( TmpL++ == (MaxL>>i) );
+ BOOST_CHECK( TmpL == (HalfL >> (i-1)));
+ }
+ BOOST_CHECK(arith_uint256(0xbedc77e27940a7ULL) + 0xee8d836fce66fbULL == arith_uint256(0xbedc77e27940a7ULL + 0xee8d836fce66fbULL));
+ TmpL = arith_uint256(0xbedc77e27940a7ULL); TmpL += 0xee8d836fce66fbULL;
+ BOOST_CHECK(TmpL == arith_uint256(0xbedc77e27940a7ULL+0xee8d836fce66fbULL));
+ TmpL -= 0xee8d836fce66fbULL; BOOST_CHECK(TmpL == 0xbedc77e27940a7ULL);
+ TmpL = R1L;
+ BOOST_CHECK(++TmpL == R1L+1);
+
+ BOOST_CHECK(R1L -(-R2L) == R1L+R2L);
+ BOOST_CHECK(R1L -(-OneL) == R1L+OneL);
+ BOOST_CHECK(R1L - OneL == R1L+(-OneL));
+ for (unsigned int i = 1; i < 256; ++i) {
+ BOOST_CHECK((MaxL>>i) - (-OneL) == (HalfL >> (i-1)));
+ BOOST_CHECK((HalfL >> (i-1)) - OneL == (MaxL>>i));
+ TmpL = (HalfL >> (i-1));
+ BOOST_CHECK(TmpL-- == (HalfL >> (i-1)));
+ BOOST_CHECK(TmpL == (MaxL >> i));
+ TmpL = (HalfL >> (i-1));
+ BOOST_CHECK(--TmpL == (MaxL >> i));
+ }
+ TmpL = R1L;
+ BOOST_CHECK(--TmpL == R1L-1);
+}
+
+BOOST_AUTO_TEST_CASE( multiply )
+{
+ BOOST_CHECK((R1L * R1L).ToString() == "62a38c0486f01e45879d7910a7761bf30d5237e9873f9bff3642a732c4d84f10");
+ BOOST_CHECK((R1L * R2L).ToString() == "de37805e9986996cfba76ff6ba51c008df851987d9dd323f0e5de07760529c40");
+ BOOST_CHECK((R1L * ZeroL) == ZeroL);
+ BOOST_CHECK((R1L * OneL) == R1L);
+ BOOST_CHECK((R1L * MaxL) == -R1L);
+ BOOST_CHECK((R2L * R1L) == (R1L * R2L));
+ BOOST_CHECK((R2L * R2L).ToString() == "ac8c010096767d3cae5005dec28bb2b45a1d85ab7996ccd3e102a650f74ff100");
+ BOOST_CHECK((R2L * ZeroL) == ZeroL);
+ BOOST_CHECK((R2L * OneL) == R2L);
+ BOOST_CHECK((R2L * MaxL) == -R2L);
+
+ BOOST_CHECK(MaxL * MaxL == OneL);
+
+ BOOST_CHECK((R1L * 0) == 0);
+ BOOST_CHECK((R1L * 1) == R1L);
+ BOOST_CHECK((R1L * 3).ToString() == "7759b1c0ed14047f961ad09b20ff83687876a0181a367b813634046f91def7d4");
+ BOOST_CHECK((R2L * 0x87654321UL).ToString() == "23f7816e30c4ae2017257b7a0fa64d60402f5234d46e746b61c960d09a26d070");
+}
+
+BOOST_AUTO_TEST_CASE( divide )
+{
+ arith_uint256 D1L("AD7133AC1977FA2B7");
+ arith_uint256 D2L("ECD751716");
+ BOOST_CHECK((R1L / D1L).ToString() == "00000000000000000b8ac01106981635d9ed112290f8895545a7654dde28fb3a");
+ BOOST_CHECK((R1L / D2L).ToString() == "000000000873ce8efec5b67150bad3aa8c5fcb70e947586153bf2cec7c37c57a");
+ BOOST_CHECK(R1L / OneL == R1L);
+ BOOST_CHECK(R1L / MaxL == ZeroL);
+ BOOST_CHECK(MaxL / R1L == 2);
+ BOOST_CHECK_THROW(R1L / ZeroL, uint_error);
+ BOOST_CHECK((R2L / D1L).ToString() == "000000000000000013e1665895a1cc981de6d93670105a6b3ec3b73141b3a3c5");
+ BOOST_CHECK((R2L / D2L).ToString() == "000000000e8f0abe753bb0afe2e9437ee85d280be60882cf0bd1aaf7fa3cc2c4");
+ BOOST_CHECK(R2L / OneL == R2L);
+ BOOST_CHECK(R2L / MaxL == ZeroL);
+ BOOST_CHECK(MaxL / R2L == 1);
+ BOOST_CHECK_THROW(R2L / ZeroL, uint_error);
+}
+
+
+bool almostEqual(double d1, double d2)
+{
+ return fabs(d1-d2) <= 4*fabs(d1)*std::numeric_limits<double>::epsilon();
+}
+
+BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex size() GetLow64 GetSerializeSize, Serialize, Unserialize
+{
+ BOOST_CHECK(R1L.GetHex() == R1L.ToString());
+ BOOST_CHECK(R2L.GetHex() == R2L.ToString());
+ BOOST_CHECK(OneL.GetHex() == OneL.ToString());
+ BOOST_CHECK(MaxL.GetHex() == MaxL.ToString());
+ arith_uint256 TmpL(R1L);
+ BOOST_CHECK(TmpL == R1L);
+ TmpL.SetHex(R2L.ToString()); BOOST_CHECK(TmpL == R2L);
+ TmpL.SetHex(ZeroL.ToString()); BOOST_CHECK(TmpL == 0);
+ TmpL.SetHex(HalfL.ToString()); BOOST_CHECK(TmpL == HalfL);
+
+ TmpL.SetHex(R1L.ToString());
+ BOOST_CHECK(R1L.size() == 32);
+ BOOST_CHECK(R2L.size() == 32);
+ BOOST_CHECK(ZeroL.size() == 32);
+ BOOST_CHECK(MaxL.size() == 32);
+ BOOST_CHECK(R1L.GetLow64() == R1LLow64);
+ BOOST_CHECK(HalfL.GetLow64() ==0x0000000000000000ULL);
+ BOOST_CHECK(OneL.GetLow64() ==0x0000000000000001ULL);
+
+ for (unsigned int i = 0; i < 255; ++i)
+ {
+ BOOST_CHECK((OneL << i).getdouble() == ldexp(1.0,i));
+ }
+ BOOST_CHECK(ZeroL.getdouble() == 0.0);
+ for (int i = 256; i > 53; --i)
+ BOOST_CHECK(almostEqual((R1L>>(256-i)).getdouble(), ldexp(R1Ldouble,i)));
+ uint64_t R1L64part = (R1L>>192).GetLow64();
+ for (int i = 53; i > 0; --i) // doubles can store all integers in {0,...,2^54-1} exactly
+ {
+ BOOST_CHECK((R1L>>(256-i)).getdouble() == (double)(R1L64part >> (64-i)));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(bignum_SetCompact)
+{
+ arith_uint256 num;
+ bool fNegative;
+ bool fOverflow;
+ num.SetCompact(0, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x00123456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x01003456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x02000056, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x03000000, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x04000000, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x00923456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x01803456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x02800056, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x03800000, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x04800000, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x01123456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000012");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x01120000U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ // Make sure that we don't generate compacts with the 0x00800000 bit set
+ num = 0x80;
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000U);
+
+ num.SetCompact(0x01fedcba, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "000000000000000000000000000000000000000000000000000000000000007e");
+ BOOST_CHECK_EQUAL(num.GetCompact(true), 0x01fe0000U);
+ BOOST_CHECK_EQUAL(fNegative, true);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x02123456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000001234");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x03123456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000123456");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x04123456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000012345600");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x04923456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000012345600");
+ BOOST_CHECK_EQUAL(num.GetCompact(true), 0x04923456U);
+ BOOST_CHECK_EQUAL(fNegative, true);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x05009234, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000092340000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0x20123456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(num.GetHex(), "1234560000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456U);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, false);
+
+ num.SetCompact(0xff123456, &fNegative, &fOverflow);
+ BOOST_CHECK_EQUAL(fNegative, false);
+ BOOST_CHECK_EQUAL(fOverflow, true);
+}
+
+
+BOOST_AUTO_TEST_CASE( getmaxcoverage ) // some more tests just to get 100% coverage
+{
+ // ~R1L give a base_uint<256>
+ BOOST_CHECK((~~R1L >> 10) == (R1L >> 10));
+ BOOST_CHECK((~~R1L << 10) == (R1L << 10));
+ BOOST_CHECK(!(~~R1L < R1L));
+ BOOST_CHECK(~~R1L <= R1L);
+ BOOST_CHECK(!(~~R1L > R1L));
+ BOOST_CHECK(~~R1L >= R1L);
+ BOOST_CHECK(!(R1L < ~~R1L));
+ BOOST_CHECK(R1L <= ~~R1L);
+ BOOST_CHECK(!(R1L > ~~R1L));
+ BOOST_CHECK(R1L >= ~~R1L);
+
+ BOOST_CHECK(~~R1L + R2L == R1L + ~~R2L);
+ BOOST_CHECK(~~R1L - R2L == R1L - ~~R2L);
+ BOOST_CHECK(~R1L != R1L); BOOST_CHECK(R1L != ~R1L);
+ unsigned char TmpArray[32];
+ CHECKBITWISEOPERATOR(~R1,R2,|)
+ CHECKBITWISEOPERATOR(~R1,R2,^)
+ CHECKBITWISEOPERATOR(~R1,R2,&)
+ CHECKBITWISEOPERATOR(R1,~R2,|)
+ CHECKBITWISEOPERATOR(R1,~R2,^)
+ CHECKBITWISEOPERATOR(R1,~R2,&)
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index c32098efa..f9cbcb0d9 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -1,462 +1,462 @@
// Copyright (c) 2012-2013 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 "bloom.h"
#include "base58.h"
#include "clientversion.h"
#include "key.h"
#include "merkleblock.h"
#include "serialize.h"
#include "streams.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
#include <vector>
#include <boost/test/unit_test.hpp>
#include <boost/tuple/tuple.hpp>
using namespace std;
using namespace boost::tuples;
BOOST_AUTO_TEST_SUITE(bloom_tests)
BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
{
CBloomFilter filter(3, 0.01, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
// One bit different in first byte
BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
vector<unsigned char> vch = ParseHex("03614e9b050000000000000001");
vector<char> expected(vch.size());
for (unsigned int i = 0; i < vch.size(); i++)
expected[i] = (char)vch[i];
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
filter.clear();
BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter should be empty!");
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
{
// Same test as bloom_create_insert_serialize, but we add a nTweak of 100
CBloomFilter filter(3, 0.01, 2147483649UL, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
// One bit different in first byte
BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
vector<unsigned char> vch = ParseHex("03ce4299050000000100008001");
vector<char> expected(vch.size());
for (unsigned int i = 0; i < vch.size(); i++)
expected[i] = (char)vch[i];
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
{
string strSecret = string("5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C");
CBitcoinSecret vchSecret;
BOOST_CHECK(vchSecret.SetString(strSecret));
CKey key = vchSecret.GetKey();
CPubKey pubkey = key.GetPubKey();
vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end());
CBloomFilter filter(2, 0.001, 0, BLOOM_UPDATE_ALL);
filter.insert(vchPubKey);
uint160 hash = pubkey.GetID();
filter.insert(vector<unsigned char>(hash.begin(), hash.end()));
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
vector<unsigned char> vch = ParseHex("038fc16b080000000000000001");
vector<char> expected(vch.size());
for (unsigned int i = 0; i < vch.size(); i++)
expected[i] = (char)vch[i];
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_match)
{
// Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b)
CTransaction tx;
CDataStream stream(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"), SER_DISK, CLIENT_VERSION);
stream >> tx;
// and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
CDataStream spendStream(vch, SER_DISK, CLIENT_VERSION);
CTransaction spendingTx;
spendStream >> spendingTx;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(uint256("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
+ filter.insert(uint256S("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// byte-reversed tx hash
filter.insert(ParseHex("6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input signature");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input pub key");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address");
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(spendingTx), "Simple Bloom filter didn't add output");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("a266436d2965547608b9e15d9032a7b9d64fa431"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
+ filter.insert(COutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match COutPoint");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- COutPoint prevOutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0);
+ COutPoint prevOutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0);
{
vector<unsigned char> data(32 + sizeof(unsigned int));
memcpy(&data[0], prevOutPoint.hash.begin(), 32);
memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int));
filter.insert(data);
}
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(uint256("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"));
+ filter.insert(uint256S("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("0000006d2965547608b9e15d9032a7b9d64fa431"));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random address");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1));
+ filter.insert(COutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
- filter.insert(COutPoint(uint256("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
+ filter.insert(COutPoint(uint256S("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about");
}
BOOST_AUTO_TEST_CASE(merkle_block_1)
{
// Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
// With 9 txes
CBlock block;
CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the last transaction
- filter.insert(uint256("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+ filter.insert(uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 8);
vector<uint256> vMatched;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
// Also match the 8th transaction
- filter.insert(uint256("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
+ filter.insert(uint256S("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2);
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 7);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
BOOST_AUTO_TEST_CASE(merkle_block_2)
{
// Random real block (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6)
// With 4 txes
CBlock block;
CDataStream stream(ParseHex("0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"), SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the first transaction
- filter.insert(uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ filter.insert(uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
vector<uint256> vMatched;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
// Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
// This should match the third transaction because it spends the output matched
// It also matches the fourth transaction, which spends to the pubkey again
filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 4);
BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
- BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256S("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
- BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256("0x6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df21bea5f4e27e2"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256S("0x6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df21bea5f4e27e2"));
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 2);
- BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
BOOST_CHECK(merkleBlock.vMatchedTxn[3].first == 3);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
{
// Random real block (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6)
// With 4 txes
CBlock block;
CDataStream stream(ParseHex("0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"), SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE);
// Match the first transaction
- filter.insert(uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ filter.insert(uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
vector<uint256> vMatched;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
// Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
// This should not match the third transaction though it spends the output matched
// It will match the fourth transaction, which has another pay-to-pubkey output to the same address
filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 3);
BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
- BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256S("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
- BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 3);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize)
{
// Random real block (000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45)
// With one tx
CBlock block;
CDataStream stream(ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020a02ffffffff0100f2052a01000000434104ecd3229b0571c3be876feaac0442a9f13c5a572742927af1dc623353ecf8c202225f64868137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5672bb15ad5d4cac00000000"), SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the only transaction
- filter.insert(uint256("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
+ filter.insert(uint256S("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
vector<uint256> vMatched;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
CDataStream merkleStream(SER_NETWORK, PROTOCOL_VERSION);
merkleStream << merkleBlock;
vector<unsigned char> vch = ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101");
vector<char> expected(vch.size());
for (unsigned int i = 0; i < vch.size(); i++)
expected[i] = (char)vch[i];
BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), merkleStream.begin(), merkleStream.end());
}
BOOST_AUTO_TEST_CASE(merkle_block_4)
{
// Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
// With 7 txes
CBlock block;
CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the last transaction
- filter.insert(uint256("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
+ filter.insert(uint256S("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 6);
vector<uint256> vMatched;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
// Also match the 4th transaction
- filter.insert(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
+ filter.insert(uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2);
- BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 3);
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (unsigned int i = 0; i < vMatched.size(); i++)
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
BOOST_AUTO_TEST_CASE(merkle_block_4_test_p2pubkey_only)
{
// Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
// With 7 txes
CBlock block;
CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_P2PUBKEY_ONLY);
// Match the generation pubkey
filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91"));
// ...and the output address of the 4th transaction
filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
// We should match the generation outpoint
- BOOST_CHECK(filter.contains(COutPoint(uint256("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
+ BOOST_CHECK(filter.contains(COutPoint(uint256S("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
// ... but not the 4th transaction's output (its not pay-2-pubkey)
- BOOST_CHECK(!filter.contains(COutPoint(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
+ BOOST_CHECK(!filter.contains(COutPoint(uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
}
BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none)
{
// Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
// With 7 txes
CBlock block;
CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE);
// Match the generation pubkey
filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91"));
// ...and the output address of the 4th transaction
filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
// We shouldn't match any outpoints (UPDATE_NONE)
- BOOST_CHECK(!filter.contains(COutPoint(uint256("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
- BOOST_CHECK(!filter.contains(COutPoint(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
+ BOOST_CHECK(!filter.contains(COutPoint(uint256S("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
+ BOOST_CHECK(!filter.contains(COutPoint(uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index 56e054166..372cf3b30 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -1,118 +1,124 @@
// Copyright (c) 2012-2013 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 "merkleblock.h"
#include "serialize.h"
#include "streams.h"
#include "uint256.h"
+#include "arith_uint256.h"
#include "version.h"
#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/test/unit_test.hpp>
using namespace std;
class CPartialMerkleTreeTester : public CPartialMerkleTree
{
public:
// flip one bit in one of the hashes - this should break the authentication
void Damage() {
unsigned int n = rand() % vHash.size();
int bit = rand() % 256;
- uint256 &hash = vHash[n];
- hash ^= ((uint256)1 << bit);
+ *(vHash[n].begin() + (bit>>3)) ^= 1<<(bit&7);
}
};
BOOST_AUTO_TEST_SUITE(pmt_tests)
BOOST_AUTO_TEST_CASE(pmt_test1)
{
static const unsigned int nTxCounts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095};
for (int n = 0; n < 12; n++) {
unsigned int nTx = nTxCounts[n];
// build a block with some dummy transactions
CBlock block;
for (unsigned int j=0; j<nTx; j++) {
CMutableTransaction tx;
tx.nLockTime = rand(); // actual transaction data doesn't matter; just make the nLockTime's unique
block.vtx.push_back(CTransaction(tx));
}
// calculate actual merkle root and height
uint256 merkleRoot1 = block.BuildMerkleTree();
- std::vector<uint256> vTxid(nTx, 0);
+ std::vector<uint256> vTxid(nTx, uint256());
for (unsigned int j=0; j<nTx; j++)
vTxid[j] = block.vtx[j].GetHash();
int nHeight = 1, nTx_ = nTx;
while (nTx_ > 1) {
nTx_ = (nTx_+1)/2;
nHeight++;
}
// check with random subsets with inclusion chances 1, 1/2, 1/4, ..., 1/128
for (int att = 1; att < 15; att++) {
// build random subset of txid's
std::vector<bool> vMatch(nTx, false);
std::vector<uint256> vMatchTxid1;
for (unsigned int j=0; j<nTx; j++) {
bool fInclude = (rand() & ((1 << (att/2)) - 1)) == 0;
vMatch[j] = fInclude;
if (fInclude)
vMatchTxid1.push_back(vTxid[j]);
}
// build the partial merkle tree
CPartialMerkleTree pmt1(vTxid, vMatch);
// serialize
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pmt1;
// verify CPartialMerkleTree's size guarantees
unsigned int n = std::min<unsigned int>(nTx, 1 + vMatchTxid1.size()*nHeight);
BOOST_CHECK(ss.size() <= 10 + (258*n+7)/8);
// deserialize into a tester copy
CPartialMerkleTreeTester pmt2;
ss >> pmt2;
// extract merkle root and matched txids from copy
std::vector<uint256> vMatchTxid2;
uint256 merkleRoot2 = pmt2.ExtractMatches(vMatchTxid2);
// check that it has the same merkle root as the original, and a valid one
BOOST_CHECK(merkleRoot1 == merkleRoot2);
- BOOST_CHECK(merkleRoot2 != 0);
+ BOOST_CHECK(!merkleRoot2.IsNull());
// check that it contains the matched transactions (in the same order!)
BOOST_CHECK(vMatchTxid1 == vMatchTxid2);
// check that random bit flips break the authentication
for (int j=0; j<4; j++) {
CPartialMerkleTreeTester pmt3(pmt2);
pmt3.Damage();
std::vector<uint256> vMatchTxid3;
uint256 merkleRoot3 = pmt3.ExtractMatches(vMatchTxid3);
BOOST_CHECK(merkleRoot3 != merkleRoot1);
}
}
}
}
BOOST_AUTO_TEST_CASE(pmt_malleability)
{
- std::vector<uint256> vTxid = boost::assign::list_of(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(9)(10);
+ std::vector<uint256> vTxid = boost::assign::list_of
+ (ArithToUint256(1))(ArithToUint256(2))
+ (ArithToUint256(3))(ArithToUint256(4))
+ (ArithToUint256(5))(ArithToUint256(6))
+ (ArithToUint256(7))(ArithToUint256(8))
+ (ArithToUint256(9))(ArithToUint256(10))
+ (ArithToUint256(9))(ArithToUint256(10));
std::vector<bool> vMatch = boost::assign::list_of(false)(false)(false)(false)(false)(false)(false)(false)(false)(true)(true)(false);
CPartialMerkleTree tree(vTxid, vMatch);
std::vector<uint256> vTxid2;
- BOOST_CHECK(tree.ExtractMatches(vTxid) == 0);
+ BOOST_CHECK(tree.ExtractMatches(vTxid).IsNull());
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index 63553556e..77e70022d 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -1,381 +1,381 @@
// Copyright (c) 2012-2013 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 "key.h"
#include "keystore.h"
#include "main.h"
#include "script/script.h"
#include "script/script_error.h"
#include "script/sign.h"
#ifdef ENABLE_WALLET
#include "wallet_ismine.h"
#endif
#include <vector>
#include <boost/test/unit_test.hpp>
using namespace std;
// Helpers:
static std::vector<unsigned char>
Serialize(const CScript& s)
{
std::vector<unsigned char> sSerialized(s);
return sSerialized;
}
static bool
Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err)
{
// Create dummy to/from transactions:
CMutableTransaction txFrom;
txFrom.vout.resize(1);
txFrom.vout[0].scriptPubKey = scriptPubKey;
CMutableTransaction txTo;
txTo.vin.resize(1);
txTo.vout.resize(1);
txTo.vin[0].prevout.n = 0;
txTo.vin[0].prevout.hash = txFrom.GetHash();
txTo.vin[0].scriptSig = scriptSig;
txTo.vout[0].nValue = 1;
return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, SignatureChecker(txTo, 0), &err);
}
BOOST_AUTO_TEST_SUITE(script_P2SH_tests)
BOOST_AUTO_TEST_CASE(sign)
{
LOCK(cs_main);
// Pay-to-script-hash looks like this:
// scriptSig: <sig> <sig...> <serialized_script>
// scriptPubKey: HASH160 <hash> EQUAL
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
CBasicKeyStore keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
keystore.AddKey(key[i]);
}
// 8 Scripts: checking all combinations of
// different keys, straight/P2SH, pubkey/pubkeyhash
CScript standardScripts[4];
standardScripts[0] << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
standardScripts[1] = GetScriptForDestination(key[1].GetPubKey().GetID());
standardScripts[2] << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
standardScripts[3] = GetScriptForDestination(key[2].GetPubKey().GetID());
CScript evalScripts[4];
for (int i = 0; i < 4; i++)
{
keystore.AddCScript(standardScripts[i]);
evalScripts[i] = GetScriptForDestination(CScriptID(standardScripts[i]));
}
CMutableTransaction txFrom; // Funding transaction:
string reason;
txFrom.vout.resize(8);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = evalScripts[i];
txFrom.vout[i].nValue = COIN;
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
txFrom.vout[i+4].nValue = COIN;
}
BOOST_CHECK(IsStandardTx(txFrom, reason));
CMutableTransaction txTo[8]; // Spending transactions
for (int i = 0; i < 8; i++)
{
txTo[i].vin.resize(1);
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
#ifdef ENABLE_WALLET
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
#endif
}
for (int i = 0; i < 8; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
}
// All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig:
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false)();
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
txTo[i].vin[0].scriptSig = sigSave;
}
}
BOOST_AUTO_TEST_CASE(norecurse)
{
ScriptError err;
// Make sure only the outer pay-to-script-hash does the
// extra-validation thing:
CScript invalidAsScript;
invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE;
CScript p2sh = GetScriptForDestination(CScriptID(invalidAsScript));
CScript scriptSig;
scriptSig << Serialize(invalidAsScript);
// Should not verify, because it will try to execute OP_INVALIDOPCODE
BOOST_CHECK(!Verify(scriptSig, p2sh, true, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_BAD_OPCODE, ScriptErrorString(err));
// Try to recur, and verification should succeed because
// the inner HASH160 <> EQUAL should only check the hash:
CScript p2sh2 = GetScriptForDestination(CScriptID(p2sh));
CScript scriptSig2;
scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh);
BOOST_CHECK(Verify(scriptSig2, p2sh2, true, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
BOOST_AUTO_TEST_CASE(set)
{
LOCK(cs_main);
// Test the CScript::Set* methods
CBasicKeyStore keystore;
CKey key[4];
std::vector<CPubKey> keys;
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
keystore.AddKey(key[i]);
keys.push_back(key[i].GetPubKey());
}
CScript inner[4];
inner[0] = GetScriptForDestination(key[0].GetPubKey().GetID());
inner[1] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
inner[2] = GetScriptForMultisig(1, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
inner[3] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+3));
CScript outer[4];
for (int i = 0; i < 4; i++)
{
outer[i] = GetScriptForDestination(CScriptID(inner[i]));
keystore.AddCScript(inner[i]);
}
CMutableTransaction txFrom; // Funding transaction:
string reason;
txFrom.vout.resize(4);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = outer[i];
txFrom.vout[i].nValue = CENT;
}
BOOST_CHECK(IsStandardTx(txFrom, reason));
CMutableTransaction txTo[4]; // Spending transactions
for (int i = 0; i < 4; i++)
{
txTo[i].vin.resize(1);
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1*CENT;
txTo[i].vout[0].scriptPubKey = inner[i];
#ifdef ENABLE_WALLET
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
#endif
}
for (int i = 0; i < 4; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i));
}
}
BOOST_AUTO_TEST_CASE(is)
{
// Test CScript::IsPayToScriptHash()
- uint160 dummy(0);
+ uint160 dummy;
CScript p2sh;
p2sh << OP_HASH160 << ToByteVector(dummy) << OP_EQUAL;
BOOST_CHECK(p2sh.IsPayToScriptHash());
// Not considered pay-to-script-hash if using one of the OP_PUSHDATA opcodes:
static const unsigned char direct[] = { 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(direct, direct+sizeof(direct)).IsPayToScriptHash());
static const unsigned char pushdata1[] = { OP_HASH160, OP_PUSHDATA1, 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(pushdata1, pushdata1+sizeof(pushdata1)).IsPayToScriptHash());
static const unsigned char pushdata2[] = { OP_HASH160, OP_PUSHDATA2, 20,0, 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(pushdata2, pushdata2+sizeof(pushdata2)).IsPayToScriptHash());
static const unsigned char pushdata4[] = { OP_HASH160, OP_PUSHDATA4, 20,0,0,0, 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(pushdata4, pushdata4+sizeof(pushdata4)).IsPayToScriptHash());
CScript not_p2sh;
BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
not_p2sh.clear(); not_p2sh << OP_HASH160 << ToByteVector(dummy) << ToByteVector(dummy) << OP_EQUAL;
BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
not_p2sh.clear(); not_p2sh << OP_NOP << ToByteVector(dummy) << OP_EQUAL;
BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
not_p2sh.clear(); not_p2sh << OP_HASH160 << ToByteVector(dummy) << OP_CHECKSIG;
BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
}
BOOST_AUTO_TEST_CASE(switchover)
{
// Test switch over code
CScript notValid;
ScriptError err;
notValid << OP_11 << OP_12 << OP_EQUALVERIFY;
CScript scriptSig;
scriptSig << Serialize(notValid);
CScript fund = GetScriptForDestination(CScriptID(notValid));
// Validation should succeed under old rules (hash is correct):
BOOST_CHECK(Verify(scriptSig, fund, false, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
// Fail under new:
BOOST_CHECK(!Verify(scriptSig, fund, true, err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EQUALVERIFY, ScriptErrorString(err));
}
BOOST_AUTO_TEST_CASE(AreInputsStandard)
{
LOCK(cs_main);
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
CBasicKeyStore keystore;
CKey key[6];
vector<CPubKey> keys;
for (int i = 0; i < 6; i++)
{
key[i].MakeNewKey(true);
keystore.AddKey(key[i]);
}
for (int i = 0; i < 3; i++)
keys.push_back(key[i].GetPubKey());
CMutableTransaction txFrom;
txFrom.vout.resize(7);
// First three are standard:
CScript pay1 = GetScriptForDestination(key[0].GetPubKey().GetID());
keystore.AddCScript(pay1);
CScript pay1of3 = GetScriptForMultisig(1, keys);
txFrom.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(pay1)); // P2SH (OP_CHECKSIG)
txFrom.vout[0].nValue = 1000;
txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG
txFrom.vout[1].nValue = 2000;
txFrom.vout[2].scriptPubKey = pay1of3; // ordinary OP_CHECKMULTISIG
txFrom.vout[2].nValue = 3000;
// vout[3] is complicated 1-of-3 AND 2-of-3
// ... that is OK if wrapped in P2SH:
CScript oneAndTwo;
oneAndTwo << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey());
oneAndTwo << OP_3 << OP_CHECKMULTISIGVERIFY;
oneAndTwo << OP_2 << ToByteVector(key[3].GetPubKey()) << ToByteVector(key[4].GetPubKey()) << ToByteVector(key[5].GetPubKey());
oneAndTwo << OP_3 << OP_CHECKMULTISIG;
keystore.AddCScript(oneAndTwo);
txFrom.vout[3].scriptPubKey = GetScriptForDestination(CScriptID(oneAndTwo));
txFrom.vout[3].nValue = 4000;
// vout[4] is max sigops:
CScript fifteenSigops; fifteenSigops << OP_1;
for (unsigned i = 0; i < MAX_P2SH_SIGOPS; i++)
fifteenSigops << ToByteVector(key[i%3].GetPubKey());
fifteenSigops << OP_15 << OP_CHECKMULTISIG;
keystore.AddCScript(fifteenSigops);
txFrom.vout[4].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
txFrom.vout[4].nValue = 5000;
// vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS
CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG;
keystore.AddCScript(sixteenSigops);
txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
txFrom.vout[5].nValue = 5000;
CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
keystore.AddCScript(twentySigops);
txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops));
txFrom.vout[6].nValue = 6000;
coins.ModifyCoins(txFrom.GetHash())->FromTx(txFrom, 0);
CMutableTransaction txTo;
txTo.vout.resize(1);
txTo.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
txTo.vin.resize(5);
for (int i = 0; i < 5; i++)
{
txTo.vin[i].prevout.n = i;
txTo.vin[i].prevout.hash = txFrom.GetHash();
}
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0));
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1));
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2));
// SignSignature doesn't know how to sign these. We're
// not testing validating signatures, so just create
// dummy signatures that DO include the correct P2SH scripts:
txTo.vin[3].scriptSig << OP_11 << OP_11 << static_cast<vector<unsigned char> >(oneAndTwo);
txTo.vin[4].scriptSig << static_cast<vector<unsigned char> >(fifteenSigops);
BOOST_CHECK(::AreInputsStandard(txTo, coins));
// 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4]
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 22U);
// Make sure adding crap to the scriptSigs makes them non-standard:
for (int i = 0; i < 3; i++)
{
CScript t = txTo.vin[i].scriptSig;
txTo.vin[i].scriptSig = (CScript() << 11) + t;
BOOST_CHECK(!::AreInputsStandard(txTo, coins));
txTo.vin[i].scriptSig = t;
}
CMutableTransaction txToNonStd1;
txToNonStd1.vout.resize(1);
txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
txToNonStd1.vout[0].nValue = 1000;
txToNonStd1.vin.resize(1);
txToNonStd1.vin[0].prevout.n = 5;
txToNonStd1.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd1.vin[0].scriptSig << static_cast<vector<unsigned char> >(sixteenSigops);
BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins));
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd1, coins), 16U);
CMutableTransaction txToNonStd2;
txToNonStd2.vout.resize(1);
txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID());
txToNonStd2.vout[0].nValue = 1000;
txToNonStd2.vin.resize(1);
txToNonStd2.vin[0].prevout.n = 6;
txToNonStd2.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd2.vin[0].scriptSig << static_cast<vector<unsigned char> >(twentySigops);
BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins));
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd2, coins), 20U);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index a9d2f9e8c..662f765a0 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -1,214 +1,215 @@
// Copyright (c) 2013 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 "data/sighash.json.h"
#include "main.h"
#include "random.h"
#include "serialize.h"
#include "script/script.h"
#include "script/interpreter.h"
#include "util.h"
#include "version.h"
#include <iostream>
#include <boost/test/unit_test.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
using namespace json_spirit;
extern Array read_json(const std::string& jsondata);
// Old script.cpp SignatureHash function
uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
+ static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
if (nIn >= txTo.vin.size())
{
printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
- return 1;
+ return one;
}
CMutableTransaction txTmp(txTo);
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
// Blank out other inputs' signatures
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
txTmp.vin[i].scriptSig = CScript();
txTmp.vin[nIn].scriptSig = scriptCode;
// Blank out some of the outputs
if ((nHashType & 0x1f) == SIGHASH_NONE)
{
// Wildcard payee
txTmp.vout.clear();
// Let the others update at will
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
if (i != nIn)
txTmp.vin[i].nSequence = 0;
}
else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
{
// Only lock-in the txout payee at same index as txin
unsigned int nOut = nIn;
if (nOut >= txTmp.vout.size())
{
printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut);
- return 1;
+ return one;
}
txTmp.vout.resize(nOut+1);
for (unsigned int i = 0; i < nOut; i++)
txTmp.vout[i].SetNull();
// Let the others update at will
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
if (i != nIn)
txTmp.vin[i].nSequence = 0;
}
// Blank out other inputs completely, not recommended for open transactions
if (nHashType & SIGHASH_ANYONECANPAY)
{
txTmp.vin[0] = txTmp.vin[nIn];
txTmp.vin.resize(1);
}
// Serialize and hash
CHashWriter ss(SER_GETHASH, 0);
ss << txTmp << nHashType;
return ss.GetHash();
}
void static RandomScript(CScript &script) {
static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR};
script = CScript();
int ops = (insecure_rand() % 10);
for (int i=0; i<ops; i++)
script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))];
}
void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
tx.nVersion = insecure_rand();
tx.vin.clear();
tx.vout.clear();
tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
int ins = (insecure_rand() % 4) + 1;
int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
for (int in = 0; in < ins; in++) {
tx.vin.push_back(CTxIn());
CTxIn &txin = tx.vin.back();
txin.prevout.hash = GetRandHash();
txin.prevout.n = insecure_rand() % 4;
RandomScript(txin.scriptSig);
txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1;
}
for (int out = 0; out < outs; out++) {
tx.vout.push_back(CTxOut());
CTxOut &txout = tx.vout.back();
txout.nValue = insecure_rand() % 100000000;
RandomScript(txout.scriptPubKey);
}
}
BOOST_AUTO_TEST_SUITE(sighash_tests)
BOOST_AUTO_TEST_CASE(sighash_test)
{
seed_insecure_rand(false);
#if defined(PRINT_SIGHASH_JSON)
std::cout << "[\n";
std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n";
#endif
int nRandomTests = 50000;
#if defined(PRINT_SIGHASH_JSON)
nRandomTests = 500;
#endif
for (int i=0; i<nRandomTests; i++) {
int nHashType = insecure_rand();
CMutableTransaction txTo;
RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
CScript scriptCode;
RandomScript(scriptCode);
int nIn = insecure_rand() % txTo.vin.size();
uint256 sh, sho;
sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
sh = SignatureHash(scriptCode, txTo, nIn, nHashType);
#if defined(PRINT_SIGHASH_JSON)
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << txTo;
std::cout << "\t[\"" ;
std::cout << HexStr(ss.begin(), ss.end()) << "\", \"";
std::cout << HexStr(scriptCode) << "\", ";
std::cout << nIn << ", ";
std::cout << nHashType << ", \"";
std::cout << sho.GetHex() << "\"]";
if (i+1 != nRandomTests) {
std::cout << ",";
}
std::cout << "\n";
#endif
BOOST_CHECK(sh == sho);
}
#if defined(PRINT_SIGHASH_JSON)
std::cout << "]\n";
#endif
}
// Goal: check that SignatureHash generates correct hash
BOOST_AUTO_TEST_CASE(sighash_from_data)
{
Array tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash)));
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
std::string strTest = write_string(tv, false);
if (test.size() < 1) // Allow for extra stuff (useful for comments)
{
BOOST_ERROR("Bad test: " << strTest);
continue;
}
if (test.size() == 1) continue; // comment
std::string raw_tx, raw_script, sigHashHex;
int nIn, nHashType;
uint256 sh;
CTransaction tx;
CScript scriptCode = CScript();
try {
// deserialize test data
raw_tx = test[0].get_str();
raw_script = test[1].get_str();
nIn = test[2].get_int();
nHashType = test[3].get_int();
sigHashHex = test[4].get_str();
uint256 sh;
CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION);
stream >> tx;
CValidationState state;
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
BOOST_CHECK(state.IsValid());
std::vector<unsigned char> raw = ParseHex(raw_script);
scriptCode.insert(scriptCode.end(), raw.begin(), raw.end());
} catch (...) {
BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest);
continue;
}
sh = SignatureHash(scriptCode, tx, nIn, nHashType);
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 332660149..3c8264d89 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -1,66 +1,66 @@
// Copyright (c) 2012-2013 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 "pubkey.h"
#include "key.h"
#include "script/script.h"
#include "script/standard.h"
#include "uint256.h"
#include <vector>
#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
using namespace std;
// Helpers:
static std::vector<unsigned char>
Serialize(const CScript& s)
{
std::vector<unsigned char> sSerialized(s);
return sSerialized;
}
BOOST_AUTO_TEST_SUITE(sigopcount_tests)
BOOST_AUTO_TEST_CASE(GetSigOpCount)
{
// Test CScript::GetSigOpCount()
CScript s1;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0U);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0U);
- uint160 dummy(0);
+ uint160 dummy;
s1 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 2U);
s1 << OP_IF << OP_CHECKSIG << OP_ENDIF;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U);
CScript p2sh = GetScriptForDestination(CScriptID(s1));
CScript scriptSig;
scriptSig << OP_0 << Serialize(s1);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U);
std::vector<CPubKey> keys;
for (int i = 0; i < 3; i++)
{
CKey k;
k.MakeNewKey(true);
keys.push_back(k.GetPubKey());
}
CScript s2 = GetScriptForMultisig(1, keys);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U);
p2sh = GetScriptForDestination(CScriptID(s2));
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U);
CScript scriptSig2;
scriptSig2 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << Serialize(s2);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig2), 3U);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 646c7e6ad..c75e21a2a 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -1,102 +1,102 @@
// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "main.h"
#include "random.h"
#include "util.h"
#include <vector>
#include <boost/test/unit_test.hpp>
#define SKIPLIST_LENGTH 300000
BOOST_AUTO_TEST_SUITE(skiplist_tests)
BOOST_AUTO_TEST_CASE(skiplist_test)
{
std::vector<CBlockIndex> vIndex(SKIPLIST_LENGTH);
for (int i=0; i<SKIPLIST_LENGTH; i++) {
vIndex[i].nHeight = i;
vIndex[i].pprev = (i == 0) ? NULL : &vIndex[i - 1];
vIndex[i].BuildSkip();
}
for (int i=0; i<SKIPLIST_LENGTH; i++) {
if (i > 0) {
BOOST_CHECK(vIndex[i].pskip == &vIndex[vIndex[i].pskip->nHeight]);
BOOST_CHECK(vIndex[i].pskip->nHeight < i);
} else {
BOOST_CHECK(vIndex[i].pskip == NULL);
}
}
for (int i=0; i < 1000; i++) {
int from = insecure_rand() % (SKIPLIST_LENGTH - 1);
int to = insecure_rand() % (from + 1);
BOOST_CHECK(vIndex[SKIPLIST_LENGTH - 1].GetAncestor(from) == &vIndex[from]);
BOOST_CHECK(vIndex[from].GetAncestor(to) == &vIndex[to]);
BOOST_CHECK(vIndex[from].GetAncestor(0) == &vIndex[0]);
}
}
BOOST_AUTO_TEST_CASE(getlocator_test)
{
// Build a main chain 100000 blocks long.
std::vector<uint256> vHashMain(100000);
std::vector<CBlockIndex> vBlocksMain(100000);
for (unsigned int i=0; i<vBlocksMain.size(); i++) {
- vHashMain[i] = i; // Set the hash equal to the height, so we can quickly check the distances.
+ vHashMain[i] = ArithToUint256(i); // Set the hash equal to the height, so we can quickly check the distances.
vBlocksMain[i].nHeight = i;
vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : NULL;
vBlocksMain[i].phashBlock = &vHashMain[i];
vBlocksMain[i].BuildSkip();
- BOOST_CHECK_EQUAL((int)vBlocksMain[i].GetBlockHash().GetLow64(), vBlocksMain[i].nHeight);
+ BOOST_CHECK_EQUAL((int)UintToArith256(vBlocksMain[i].GetBlockHash()).GetLow64(), vBlocksMain[i].nHeight);
BOOST_CHECK(vBlocksMain[i].pprev == NULL || vBlocksMain[i].nHeight == vBlocksMain[i].pprev->nHeight + 1);
}
// Build a branch that splits off at block 49999, 50000 blocks long.
std::vector<uint256> vHashSide(50000);
std::vector<CBlockIndex> vBlocksSide(50000);
for (unsigned int i=0; i<vBlocksSide.size(); i++) {
- vHashSide[i] = i + 50000 + (uint256(1) << 128); // Add 1<<128 to the hashes, so GetLow64() still returns the height.
+ vHashSide[i] = ArithToUint256(i + 50000 + (arith_uint256(1) << 128)); // Add 1<<128 to the hashes, so GetLow64() still returns the height.
vBlocksSide[i].nHeight = i + 50000;
vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : &vBlocksMain[49999];
vBlocksSide[i].phashBlock = &vHashSide[i];
vBlocksSide[i].BuildSkip();
- BOOST_CHECK_EQUAL((int)vBlocksSide[i].GetBlockHash().GetLow64(), vBlocksSide[i].nHeight);
+ BOOST_CHECK_EQUAL((int)UintToArith256(vBlocksSide[i].GetBlockHash()).GetLow64(), vBlocksSide[i].nHeight);
BOOST_CHECK(vBlocksSide[i].pprev == NULL || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1);
}
// Build a CChain for the main branch.
CChain chain;
chain.SetTip(&vBlocksMain.back());
// Test 100 random starting points for locators.
for (int n=0; n<100; n++) {
int r = insecure_rand() % 150000;
CBlockIndex* tip = (r < 100000) ? &vBlocksMain[r] : &vBlocksSide[r - 100000];
CBlockLocator locator = chain.GetLocator(tip);
// The first result must be the block itself, the last one must be genesis.
BOOST_CHECK(locator.vHave.front() == tip->GetBlockHash());
BOOST_CHECK(locator.vHave.back() == vBlocksMain[0].GetBlockHash());
// Entries 1 through 11 (inclusive) go back one step each.
for (unsigned int i = 1; i < 12 && i < locator.vHave.size() - 1; i++) {
- BOOST_CHECK_EQUAL(locator.vHave[i].GetLow64(), tip->nHeight - i);
+ BOOST_CHECK_EQUAL(UintToArith256(locator.vHave[i]).GetLow64(), tip->nHeight - i);
}
// The further ones (excluding the last one) go back with exponential steps.
unsigned int dist = 2;
for (unsigned int i = 12; i < locator.vHave.size() - 1; i++) {
- BOOST_CHECK_EQUAL(locator.vHave[i - 1].GetLow64() - locator.vHave[i].GetLow64(), dist);
+ BOOST_CHECK_EQUAL(UintToArith256(locator.vHave[i - 1]).GetLow64() - UintToArith256(locator.vHave[i]).GetLow64(), dist);
dist *= 2;
}
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 2594f89ac..efa2af917 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1,378 +1,378 @@
// Copyright (c) 2011-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "data/tx_invalid.json.h"
#include "data/tx_valid.json.h"
#include "clientversion.h"
#include "key.h"
#include "keystore.h"
#include "main.h"
#include "script/script.h"
#include "script/script_error.h"
#include "core_io.h"
#include <map>
#include <string>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_writer_template.h"
using namespace std;
using namespace json_spirit;
using namespace boost::algorithm;
// In script_tests.cpp
extern Array read_json(const std::string& jsondata);
static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of
(string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE)
(string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH)
(string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC)
(string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG)
(string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S)
(string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY)
(string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA)
(string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY)
(string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS);
unsigned int ParseScriptFlags(string strFlags)
{
if (strFlags.empty()) {
return 0;
}
unsigned int flags = 0;
vector<string> words;
split(words, strFlags, is_any_of(","));
BOOST_FOREACH(string word, words)
{
if (!mapFlagNames.count(word))
BOOST_ERROR("Bad test: unknown verification flag '" << word << "'");
flags |= mapFlagNames[word];
}
return flags;
}
string FormatScriptFlags(unsigned int flags)
{
if (flags == 0) {
return "";
}
string ret;
std::map<string, unsigned int>::const_iterator it = mapFlagNames.begin();
while (it != mapFlagNames.end()) {
if (flags & it->second) {
ret += it->first + ",";
}
it++;
}
return ret.substr(0, ret.size() - 1);
}
BOOST_AUTO_TEST_SUITE(transaction_tests)
BOOST_AUTO_TEST_CASE(tx_valid)
{
// Read tests from test/data/tx_valid.json
// Format is an array of arrays
// Inner arrays are either [ "comment" ]
// or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, verifyFlags
// ... where all scripts are stringified scripts.
//
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
ScriptError err;
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
string strTest = write_string(tv, false);
if (test[0].type() == array_type)
{
if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type)
{
BOOST_ERROR("Bad test: " << strTest);
continue;
}
map<COutPoint, CScript> mapprevOutScriptPubKeys;
Array inputs = test[0].get_array();
bool fValid = true;
BOOST_FOREACH(Value& input, inputs)
{
if (input.type() != array_type)
{
fValid = false;
break;
}
Array vinput = input.get_array();
if (vinput.size() != 3)
{
fValid = false;
break;
}
- mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
+ mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
}
if (!fValid)
{
BOOST_ERROR("Bad test: " << strTest);
continue;
}
string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx;
stream >> tx;
CValidationState state;
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
BOOST_CHECK(state.IsValid());
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
{
BOOST_ERROR("Bad test: " << strTest);
break;
}
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
verify_flags, SignatureChecker(tx, i), &err),
strTest);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
}
}
}
BOOST_AUTO_TEST_CASE(tx_invalid)
{
// Read tests from test/data/tx_invalid.json
// Format is an array of arrays
// Inner arrays are either [ "comment" ]
// or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, verifyFlags
// ... where all scripts are stringified scripts.
//
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
ScriptError err;
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
string strTest = write_string(tv, false);
if (test[0].type() == array_type)
{
if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type)
{
BOOST_ERROR("Bad test: " << strTest);
continue;
}
map<COutPoint, CScript> mapprevOutScriptPubKeys;
Array inputs = test[0].get_array();
bool fValid = true;
BOOST_FOREACH(Value& input, inputs)
{
if (input.type() != array_type)
{
fValid = false;
break;
}
Array vinput = input.get_array();
if (vinput.size() != 3)
{
fValid = false;
break;
}
- mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
+ mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
}
if (!fValid)
{
BOOST_ERROR("Bad test: " << strTest);
continue;
}
string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx;
stream >> tx;
CValidationState state;
fValid = CheckTransaction(tx, state) && state.IsValid();
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
{
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
{
BOOST_ERROR("Bad test: " << strTest);
break;
}
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
verify_flags, SignatureChecker(tx, i), &err);
}
BOOST_CHECK_MESSAGE(!fValid, strTest);
BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
}
}
}
BOOST_AUTO_TEST_CASE(basic_transaction_tests)
{
// Random real transaction (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
CDataStream stream(vch, SER_DISK, CLIENT_VERSION);
CMutableTransaction tx;
stream >> tx;
CValidationState state;
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid.");
// Check that duplicate txins fail
tx.vin.push_back(tx.vin[0]);
BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
}
//
// Helper: create two dummy transactions, each with
// two outputs. The first has 11 and 50 CENT outputs
// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs
// paid to a TX_PUBKEYHASH.
//
static std::vector<CMutableTransaction>
SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
{
std::vector<CMutableTransaction> dummyTransactions;
dummyTransactions.resize(2);
// Add some keys to the keystore:
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(i % 2);
keystoreRet.AddKey(key[i]);
}
// Create some dummy input transactions
dummyTransactions[0].vout.resize(2);
dummyTransactions[0].vout[0].nValue = 11*CENT;
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
dummyTransactions[0].vout[1].nValue = 50*CENT;
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21*CENT;
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
dummyTransactions[1].vout[1].nValue = 22*CENT;
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
return dummyTransactions;
}
BOOST_AUTO_TEST_CASE(test_Get)
{
CBasicKeyStore keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
CMutableTransaction t1;
t1.vin.resize(3);
t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
t1.vin[0].prevout.n = 1;
t1.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();
t1.vin[1].prevout.n = 0;
t1.vin[1].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();
t1.vin[2].prevout.n = 1;
t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
t1.vout.resize(2);
t1.vout[0].nValue = 90*CENT;
t1.vout[0].scriptPubKey << OP_1;
BOOST_CHECK(AreInputsStandard(t1, coins));
BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT);
// Adding extra junk to the scriptSig should make it non-standard:
t1.vin[0].scriptSig << OP_11;
BOOST_CHECK(!AreInputsStandard(t1, coins));
// ... as should not having enough:
t1.vin[0].scriptSig = CScript();
BOOST_CHECK(!AreInputsStandard(t1, coins));
}
BOOST_AUTO_TEST_CASE(test_IsStandard)
{
LOCK(cs_main);
CBasicKeyStore keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
CMutableTransaction t;
t.vin.resize(1);
t.vin[0].prevout.hash = dummyTransactions[0].GetHash();
t.vin[0].prevout.n = 1;
t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
t.vout.resize(1);
t.vout[0].nValue = 90*CENT;
CKey key;
key.MakeNewKey(true);
t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
string reason;
BOOST_CHECK(IsStandardTx(t, reason));
t.vout[0].nValue = 501; // dust
BOOST_CHECK(!IsStandardTx(t, reason));
t.vout[0].nValue = 601; // not dust
BOOST_CHECK(IsStandardTx(t, reason));
t.vout[0].scriptPubKey = CScript() << OP_1;
BOOST_CHECK(!IsStandardTx(t, reason));
// 40-byte TX_NULL_DATA (standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
BOOST_CHECK(IsStandardTx(t, reason));
// 41-byte TX_NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
BOOST_CHECK(!IsStandardTx(t, reason));
// TX_NULL_DATA w/o PUSHDATA
t.vout.resize(1);
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
BOOST_CHECK(IsStandardTx(t, reason));
// Only one TX_NULL_DATA permitted in all cases
t.vout.resize(2);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
BOOST_CHECK(!IsStandardTx(t, reason));
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
BOOST_CHECK(!IsStandardTx(t, reason));
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
BOOST_CHECK(!IsStandardTx(t, reason));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index d05b28956..5b33846ba 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -1,837 +1,268 @@
// Copyright (c) 2011-2013 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 "arith_uint256.h"
+#include "uint256.h"
+#include "version.h"
#include <boost/test/unit_test.hpp>
#include <stdint.h>
#include <sstream>
#include <iomanip>
#include <limits>
#include <cmath>
-#include "uint256.h"
#include <string>
-#include "version.h"
+#include <stdio.h>
BOOST_AUTO_TEST_SUITE(uint256_tests)
-
-const unsigned char R1Array[] =
+
+const unsigned char R1Array[] =
"\x9c\x52\x4a\xdb\xcf\x56\x11\x12\x2b\x29\x12\x5e\x5d\x35\xd2\xd2"
"\x22\x81\xaa\xb5\x33\xf0\x08\x32\xd5\x56\xb1\xf9\xea\xe5\x1d\x7d";
const char R1ArrayHex[] = "7D1DE5EAF9B156D53208F033B5AA8122D2d2355d5e12292b121156cfdb4a529c";
-const double R1Ldouble = 0.4887374590559308955; // R1L equals roughly R1Ldouble * 2^256
-const double R1Sdouble = 0.7096329412477836074;
const uint256 R1L = uint256(std::vector<unsigned char>(R1Array,R1Array+32));
const uint160 R1S = uint160(std::vector<unsigned char>(R1Array,R1Array+20));
-const uint64_t R1LLow64 = 0x121156cfdb4a529cULL;
-const unsigned char R2Array[] =
+const unsigned char R2Array[] =
"\x70\x32\x1d\x7c\x47\xa5\x6b\x40\x26\x7e\x0a\xc3\xa6\x9c\xb6\xbf"
"\x13\x30\x47\xa3\x19\x2d\xda\x71\x49\x13\x72\xf0\xb4\xca\x81\xd7";
const uint256 R2L = uint256(std::vector<unsigned char>(R2Array,R2Array+32));
const uint160 R2S = uint160(std::vector<unsigned char>(R2Array,R2Array+20));
-const char R1LplusR2L[] = "549FB09FEA236A1EA3E31D4D58F1B1369288D204211CA751527CFC175767850C";
-
-const unsigned char ZeroArray[] =
+const unsigned char ZeroArray[] =
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
const uint256 ZeroL = uint256(std::vector<unsigned char>(ZeroArray,ZeroArray+32));
const uint160 ZeroS = uint160(std::vector<unsigned char>(ZeroArray,ZeroArray+20));
-
-const unsigned char OneArray[] =
+
+const unsigned char OneArray[] =
"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
const uint256 OneL = uint256(std::vector<unsigned char>(OneArray,OneArray+32));
const uint160 OneS = uint160(std::vector<unsigned char>(OneArray,OneArray+20));
-const unsigned char MaxArray[] =
+const unsigned char MaxArray[] =
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
const uint256 MaxL = uint256(std::vector<unsigned char>(MaxArray,MaxArray+32));
const uint160 MaxS = uint160(std::vector<unsigned char>(MaxArray,MaxArray+20));
-const uint256 HalfL = (OneL << 255);
-const uint160 HalfS = (OneS << 159);
std::string ArrayToString(const unsigned char A[], unsigned int width)
{
std::stringstream Stream;
Stream << std::hex;
- for (unsigned int i = 0; i < width; ++i)
+ for (unsigned int i = 0; i < width; ++i)
{
Stream<<std::setw(2)<<std::setfill('0')<<(unsigned int)A[width-i-1];
- }
+ }
return Stream.str();
}
+inline uint160 uint160S(const char *str)
+{
+ uint160 rv;
+ rv.SetHex(str);
+ return rv;
+}
+inline uint160 uint160S(const std::string& str)
+{
+ uint160 rv;
+ rv.SetHex(str);
+ return rv;
+}
+
BOOST_AUTO_TEST_CASE( basics ) // constructors, equality, inequality
{
BOOST_CHECK(1 == 0+1);
// constructor uint256(vector<char>):
BOOST_CHECK(R1L.ToString() == ArrayToString(R1Array,32));
BOOST_CHECK(R1S.ToString() == ArrayToString(R1Array,20));
BOOST_CHECK(R2L.ToString() == ArrayToString(R2Array,32));
BOOST_CHECK(R2S.ToString() == ArrayToString(R2Array,20));
BOOST_CHECK(ZeroL.ToString() == ArrayToString(ZeroArray,32));
BOOST_CHECK(ZeroS.ToString() == ArrayToString(ZeroArray,20));
BOOST_CHECK(OneL.ToString() == ArrayToString(OneArray,32));
BOOST_CHECK(OneS.ToString() == ArrayToString(OneArray,20));
BOOST_CHECK(MaxL.ToString() == ArrayToString(MaxArray,32));
BOOST_CHECK(MaxS.ToString() == ArrayToString(MaxArray,20));
BOOST_CHECK(OneL.ToString() != ArrayToString(ZeroArray,32));
BOOST_CHECK(OneS.ToString() != ArrayToString(ZeroArray,20));
// == and !=
BOOST_CHECK(R1L != R2L && R1S != R2S);
BOOST_CHECK(ZeroL != OneL && ZeroS != OneS);
BOOST_CHECK(OneL != ZeroL && OneS != ZeroS);
BOOST_CHECK(MaxL != ZeroL && MaxS != ZeroS);
- BOOST_CHECK(~MaxL == ZeroL && ~MaxS == ZeroS);
- BOOST_CHECK( ((R1L ^ R2L) ^ R1L) == R2L);
- BOOST_CHECK( ((R1S ^ R2S) ^ R1S) == R2S);
-
- uint64_t Tmp64 = 0xc4dab720d9c7acaaULL;
- for (unsigned int i = 0; i < 256; ++i)
- {
- BOOST_CHECK(ZeroL != (OneL << i));
- BOOST_CHECK((OneL << i) != ZeroL);
- BOOST_CHECK(R1L != (R1L ^ (OneL << i)));
- BOOST_CHECK(((uint256(Tmp64) ^ (OneL << i) ) != Tmp64 ));
- }
- BOOST_CHECK(ZeroL == (OneL << 256));
-
- for (unsigned int i = 0; i < 160; ++i)
- {
- BOOST_CHECK(ZeroS != (OneS << i));
- BOOST_CHECK((OneS << i) != ZeroS);
- BOOST_CHECK(R1S != (R1S ^ (OneS << i)));
- BOOST_CHECK(((uint160(Tmp64) ^ (OneS << i) ) != Tmp64 ));
- }
- BOOST_CHECK(ZeroS == (OneS << 256));
// String Constructor and Copy Constructor
- BOOST_CHECK(uint256("0x"+R1L.ToString()) == R1L);
- BOOST_CHECK(uint256("0x"+R2L.ToString()) == R2L);
- BOOST_CHECK(uint256("0x"+ZeroL.ToString()) == ZeroL);
- BOOST_CHECK(uint256("0x"+OneL.ToString()) == OneL);
- BOOST_CHECK(uint256("0x"+MaxL.ToString()) == MaxL);
- BOOST_CHECK(uint256(R1L.ToString()) == R1L);
- BOOST_CHECK(uint256(" 0x"+R1L.ToString()+" ") == R1L);
- BOOST_CHECK(uint256("") == ZeroL);
- BOOST_CHECK(R1L == uint256(R1ArrayHex));
+ BOOST_CHECK(uint256S("0x"+R1L.ToString()) == R1L);
+ BOOST_CHECK(uint256S("0x"+R2L.ToString()) == R2L);
+ BOOST_CHECK(uint256S("0x"+ZeroL.ToString()) == ZeroL);
+ BOOST_CHECK(uint256S("0x"+OneL.ToString()) == OneL);
+ BOOST_CHECK(uint256S("0x"+MaxL.ToString()) == MaxL);
+ BOOST_CHECK(uint256S(R1L.ToString()) == R1L);
+ BOOST_CHECK(uint256S(" 0x"+R1L.ToString()+" ") == R1L);
+ BOOST_CHECK(uint256S("") == ZeroL);
+ BOOST_CHECK(R1L == uint256S(R1ArrayHex));
BOOST_CHECK(uint256(R1L) == R1L);
- BOOST_CHECK((uint256(R1L^R2L)^R2L) == R1L);
BOOST_CHECK(uint256(ZeroL) == ZeroL);
BOOST_CHECK(uint256(OneL) == OneL);
- BOOST_CHECK(uint160("0x"+R1S.ToString()) == R1S);
- BOOST_CHECK(uint160("0x"+R2S.ToString()) == R2S);
- BOOST_CHECK(uint160("0x"+ZeroS.ToString()) == ZeroS);
- BOOST_CHECK(uint160("0x"+OneS.ToString()) == OneS);
- BOOST_CHECK(uint160("0x"+MaxS.ToString()) == MaxS);
- BOOST_CHECK(uint160(R1S.ToString()) == R1S);
- BOOST_CHECK(uint160(" 0x"+R1S.ToString()+" ") == R1S);
- BOOST_CHECK(uint160("") == ZeroS);
- BOOST_CHECK(R1S == uint160(R1ArrayHex));
+ BOOST_CHECK(uint160S("0x"+R1S.ToString()) == R1S);
+ BOOST_CHECK(uint160S("0x"+R2S.ToString()) == R2S);
+ BOOST_CHECK(uint160S("0x"+ZeroS.ToString()) == ZeroS);
+ BOOST_CHECK(uint160S("0x"+OneS.ToString()) == OneS);
+ BOOST_CHECK(uint160S("0x"+MaxS.ToString()) == MaxS);
+ BOOST_CHECK(uint160S(R1S.ToString()) == R1S);
+ BOOST_CHECK(uint160S(" 0x"+R1S.ToString()+" ") == R1S);
+ BOOST_CHECK(uint160S("") == ZeroS);
+ BOOST_CHECK(R1S == uint160S(R1ArrayHex));
BOOST_CHECK(uint160(R1S) == R1S);
- BOOST_CHECK((uint160(R1S^R2S)^R2S) == R1S);
BOOST_CHECK(uint160(ZeroS) == ZeroS);
BOOST_CHECK(uint160(OneS) == OneS);
-
- // uint64_t constructor
- BOOST_CHECK( (R1L & uint256("0xffffffffffffffff")) == uint256(R1LLow64));
- BOOST_CHECK(ZeroL == uint256(0));
- BOOST_CHECK(OneL == uint256(1));
- BOOST_CHECK(uint256("0xffffffffffffffff") = uint256(0xffffffffffffffffULL));
- BOOST_CHECK( (R1S & uint160("0xffffffffffffffff")) == uint160(R1LLow64));
- BOOST_CHECK(ZeroS == uint160(0));
- BOOST_CHECK(OneS == uint160(1));
- BOOST_CHECK(uint160("0xffffffffffffffff") = uint160(0xffffffffffffffffULL));
-
- // Assignment (from base_uint)
- uint256 tmpL = ~ZeroL; BOOST_CHECK(tmpL == ~ZeroL);
- tmpL = ~OneL; BOOST_CHECK(tmpL == ~OneL);
- tmpL = ~R1L; BOOST_CHECK(tmpL == ~R1L);
- tmpL = ~R2L; BOOST_CHECK(tmpL == ~R2L);
- tmpL = ~MaxL; BOOST_CHECK(tmpL == ~MaxL);
- uint160 tmpS = ~ZeroS; BOOST_CHECK(tmpS == ~ZeroS);
- tmpS = ~OneS; BOOST_CHECK(tmpS == ~OneS);
- tmpS = ~R1S; BOOST_CHECK(tmpS == ~R1S);
- tmpS = ~R2S; BOOST_CHECK(tmpS == ~R2S);
- tmpS = ~MaxS; BOOST_CHECK(tmpS == ~MaxS);
-
- // Wrong length must throw exception.
- BOOST_CHECK_THROW(uint256(std::vector<unsigned char>(OneArray,OneArray+31)), uint_error);
- BOOST_CHECK_THROW(uint256(std::vector<unsigned char>(OneArray,OneArray+20)), uint_error);
- BOOST_CHECK_THROW(uint160(std::vector<unsigned char>(OneArray,OneArray+32)), uint_error);
- BOOST_CHECK_THROW(uint160(std::vector<unsigned char>(OneArray,OneArray+19)), uint_error);
-}
-
-void shiftArrayRight(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
-{
- for (unsigned int T=0; T < arrayLength; ++T)
- {
- unsigned int F = (T+bitsToShift/8);
- if (F < arrayLength)
- to[T] = from[F] >> (bitsToShift%8);
- else
- to[T] = 0;
- if (F + 1 < arrayLength)
- to[T] |= from[(F+1)] << (8-bitsToShift%8);
- }
-}
-
-void shiftArrayLeft(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
-{
- for (unsigned int T=0; T < arrayLength; ++T)
- {
- if (T >= bitsToShift/8)
- {
- unsigned int F = T-bitsToShift/8;
- to[T] = from[F] << (bitsToShift%8);
- if (T >= bitsToShift/8+1)
- to[T] |= from[F-1] >> (8-bitsToShift%8);
- }
- else {
- to[T] = 0;
- }
- }
-}
-
-BOOST_AUTO_TEST_CASE( shifts ) { // "<<" ">>" "<<=" ">>="
- unsigned char TmpArray[32];
- uint256 TmpL;
- for (unsigned int i = 0; i < 256; ++i)
- {
- shiftArrayLeft(TmpArray, OneArray, 32, i);
- BOOST_CHECK(uint256(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (OneL << i));
- TmpL = OneL; TmpL <<= i;
- BOOST_CHECK(TmpL == (OneL << i));
- BOOST_CHECK((HalfL >> (255-i)) == (OneL << i));
- TmpL = HalfL; TmpL >>= (255-i);
- BOOST_CHECK(TmpL == (OneL << i));
-
- shiftArrayLeft(TmpArray, R1Array, 32, i);
- BOOST_CHECK(uint256(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (R1L << i));
- TmpL = R1L; TmpL <<= i;
- BOOST_CHECK(TmpL == (R1L << i));
-
- shiftArrayRight(TmpArray, R1Array, 32, i);
- BOOST_CHECK(uint256(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (R1L >> i));
- TmpL = R1L; TmpL >>= i;
- BOOST_CHECK(TmpL == (R1L >> i));
-
- shiftArrayLeft(TmpArray, MaxArray, 32, i);
- BOOST_CHECK(uint256(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (MaxL << i));
- TmpL = MaxL; TmpL <<= i;
- BOOST_CHECK(TmpL == (MaxL << i));
-
- shiftArrayRight(TmpArray, MaxArray, 32, i);
- BOOST_CHECK(uint256(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (MaxL >> i));
- TmpL = MaxL; TmpL >>= i;
- BOOST_CHECK(TmpL == (MaxL >> i));
- }
- uint256 c1L = uint256(0x0123456789abcdefULL);
- uint256 c2L = c1L << 128;
- for (unsigned int i = 0; i < 128; ++i) {
- BOOST_CHECK((c1L << i) == (c2L >> (128-i)));
- }
- for (unsigned int i = 128; i < 256; ++i) {
- BOOST_CHECK((c1L << i) == (c2L << (i-128)));
- }
-
- uint160 TmpS;
- for (unsigned int i = 0; i < 160; ++i)
- {
- shiftArrayLeft(TmpArray, OneArray, 20, i);
- BOOST_CHECK(uint160(std::vector<unsigned char>(TmpArray,TmpArray+20)) == (OneS << i));
- TmpS = OneS; TmpS <<= i;
- BOOST_CHECK(TmpS == (OneS << i));
- BOOST_CHECK((HalfS >> (159-i)) == (OneS << i));
- TmpS = HalfS; TmpS >>= (159-i);
- BOOST_CHECK(TmpS == (OneS << i));
-
- shiftArrayLeft(TmpArray, R1Array, 20, i);
- BOOST_CHECK(uint160(std::vector<unsigned char>(TmpArray,TmpArray+20)) == (R1S << i));
- TmpS = R1S; TmpS <<= i;
- BOOST_CHECK(TmpS == (R1S << i));
-
- shiftArrayRight(TmpArray, R1Array, 20, i);
- BOOST_CHECK(uint160(std::vector<unsigned char>(TmpArray,TmpArray+20)) == (R1S >> i));
- TmpS = R1S; TmpS >>= i;
- BOOST_CHECK(TmpS == (R1S >> i));
-
- shiftArrayLeft(TmpArray, MaxArray, 20, i);
- BOOST_CHECK(uint160(std::vector<unsigned char>(TmpArray,TmpArray+20)) == (MaxS << i));
- TmpS = MaxS; TmpS <<= i;
- BOOST_CHECK(TmpS == (MaxS << i));
-
- shiftArrayRight(TmpArray, MaxArray, 20, i);
- BOOST_CHECK(uint160(std::vector<unsigned char>(TmpArray,TmpArray+20)) == (MaxS >> i));
- TmpS = MaxS; TmpS >>= i;
- BOOST_CHECK(TmpS == (MaxS >> i));
- }
- uint160 c1S = uint160(0x0123456789abcdefULL);
- uint160 c2S = c1S << 80;
- for (unsigned int i = 0; i < 80; ++i) {
- BOOST_CHECK((c1S << i) == (c2S >> (80-i)));
- }
- for (unsigned int i = 80; i < 160; ++i) {
- BOOST_CHECK((c1S << i) == (c2S << (i-80)));
- }
-}
-
-BOOST_AUTO_TEST_CASE( unaryOperators ) // ! ~ -
-{
- BOOST_CHECK(!ZeroL); BOOST_CHECK(!ZeroS);
- BOOST_CHECK(!(!OneL));BOOST_CHECK(!(!OneS));
- for (unsigned int i = 0; i < 256; ++i)
- BOOST_CHECK(!(!(OneL<<i)));
- for (unsigned int i = 0; i < 160; ++i)
- BOOST_CHECK(!(!(OneS<<i)));
- BOOST_CHECK(!(!R1L));BOOST_CHECK(!(!R1S));
- BOOST_CHECK(!(!R2S));BOOST_CHECK(!(!R2S));
- BOOST_CHECK(!(!MaxL));BOOST_CHECK(!(!MaxS));
-
- BOOST_CHECK(~ZeroL == MaxL); BOOST_CHECK(~ZeroS == MaxS);
-
- unsigned char TmpArray[32];
- for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = ~R1Array[i]; }
- BOOST_CHECK(uint256(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (~R1L));
- BOOST_CHECK(uint160(std::vector<unsigned char>(TmpArray,TmpArray+20)) == (~R1S));
-
- BOOST_CHECK(-ZeroL == ZeroL); BOOST_CHECK(-ZeroS == ZeroS);
- BOOST_CHECK(-R1L == (~R1L)+1);
- BOOST_CHECK(-R1S == (~R1S)+1);
- for (unsigned int i = 0; i < 256; ++i)
- BOOST_CHECK(-(OneL<<i) == (MaxL << i));
- for (unsigned int i = 0; i < 160; ++i)
- BOOST_CHECK(-(OneS<<i) == (MaxS << i));
-}
-
-
-// Check if doing _A_ _OP_ _B_ results in the same as applying _OP_ onto each
-// element of Aarray and Barray, and then converting the result into a uint256.
-#define CHECKBITWISEOPERATOR(_A_,_B_,_OP_) \
- for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = _A_##Array[i] _OP_ _B_##Array[i]; } \
- BOOST_CHECK(uint256(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (_A_##L _OP_ _B_##L)); \
- for (unsigned int i = 0; i < 20; ++i) { TmpArray[i] = _A_##Array[i] _OP_ _B_##Array[i]; } \
- BOOST_CHECK(uint160(std::vector<unsigned char>(TmpArray,TmpArray+20)) == (_A_##S _OP_ _B_##S));
-
-#define CHECKASSIGNMENTOPERATOR(_A_,_B_,_OP_) \
- TmpL = _A_##L; TmpL _OP_##= _B_##L; BOOST_CHECK(TmpL == (_A_##L _OP_ _B_##L)); \
- TmpS = _A_##S; TmpS _OP_##= _B_##S; BOOST_CHECK(TmpS == (_A_##S _OP_ _B_##S));
-
-BOOST_AUTO_TEST_CASE( bitwiseOperators )
-{
- unsigned char TmpArray[32];
-
- CHECKBITWISEOPERATOR(R1,R2,|)
- CHECKBITWISEOPERATOR(R1,R2,^)
- CHECKBITWISEOPERATOR(R1,R2,&)
- CHECKBITWISEOPERATOR(R1,Zero,|)
- CHECKBITWISEOPERATOR(R1,Zero,^)
- CHECKBITWISEOPERATOR(R1,Zero,&)
- CHECKBITWISEOPERATOR(R1,Max,|)
- CHECKBITWISEOPERATOR(R1,Max,^)
- CHECKBITWISEOPERATOR(R1,Max,&)
- CHECKBITWISEOPERATOR(Zero,R1,|)
- CHECKBITWISEOPERATOR(Zero,R1,^)
- CHECKBITWISEOPERATOR(Zero,R1,&)
- CHECKBITWISEOPERATOR(Max,R1,|)
- CHECKBITWISEOPERATOR(Max,R1,^)
- CHECKBITWISEOPERATOR(Max,R1,&)
-
- uint256 TmpL;
- uint160 TmpS;
- CHECKASSIGNMENTOPERATOR(R1,R2,|)
- CHECKASSIGNMENTOPERATOR(R1,R2,^)
- CHECKASSIGNMENTOPERATOR(R1,R2,&)
- CHECKASSIGNMENTOPERATOR(R1,Zero,|)
- CHECKASSIGNMENTOPERATOR(R1,Zero,^)
- CHECKASSIGNMENTOPERATOR(R1,Zero,&)
- CHECKASSIGNMENTOPERATOR(R1,Max,|)
- CHECKASSIGNMENTOPERATOR(R1,Max,^)
- CHECKASSIGNMENTOPERATOR(R1,Max,&)
- CHECKASSIGNMENTOPERATOR(Zero,R1,|)
- CHECKASSIGNMENTOPERATOR(Zero,R1,^)
- CHECKASSIGNMENTOPERATOR(Zero,R1,&)
- CHECKASSIGNMENTOPERATOR(Max,R1,|)
- CHECKASSIGNMENTOPERATOR(Max,R1,^)
- CHECKASSIGNMENTOPERATOR(Max,R1,&)
-
- uint64_t Tmp64 = 0xe1db685c9a0b47a2ULL;
- TmpL = R1L; TmpL |= Tmp64; BOOST_CHECK(TmpL == (R1L | uint256(Tmp64)));
- TmpS = R1S; TmpS |= Tmp64; BOOST_CHECK(TmpS == (R1S | uint160(Tmp64)));
- TmpL = R1L; TmpL |= 0; BOOST_CHECK(TmpL == R1L);
- TmpS = R1S; TmpS |= 0; BOOST_CHECK(TmpS == R1S);
- TmpL ^= 0; BOOST_CHECK(TmpL == R1L);
- TmpS ^= 0; BOOST_CHECK(TmpS == R1S);
- TmpL ^= Tmp64; BOOST_CHECK(TmpL == (R1L ^ uint256(Tmp64)));
- TmpS ^= Tmp64; BOOST_CHECK(TmpS == (R1S ^ uint160(Tmp64)));
}
BOOST_AUTO_TEST_CASE( comparison ) // <= >= < >
{
- uint256 TmpL;
- for (unsigned int i = 0; i < 256; ++i) {
- TmpL= OneL<< i;
- BOOST_CHECK( TmpL >= ZeroL && TmpL > ZeroL && ZeroL < TmpL && ZeroL <= TmpL);
- BOOST_CHECK( TmpL >= 0 && TmpL > 0 && 0 < TmpL && 0 <= TmpL);
- TmpL |= R1L;
- BOOST_CHECK( TmpL >= R1L ); BOOST_CHECK( (TmpL == R1L) != (TmpL > R1L)); BOOST_CHECK( (TmpL == R1L) || !( TmpL <= R1L));
- BOOST_CHECK( R1L <= TmpL ); BOOST_CHECK( (R1L == TmpL) != (R1L < TmpL)); BOOST_CHECK( (TmpL == R1L) || !( R1L >= TmpL));
- BOOST_CHECK(! (TmpL < R1L)); BOOST_CHECK(! (R1L > TmpL));
- }
- uint160 TmpS;
- for (unsigned int i = 0; i < 160; ++i) {
- TmpS= OneS<< i;
- BOOST_CHECK( TmpS >= ZeroS && TmpS > ZeroS && ZeroS < TmpS && ZeroS <= TmpS);
- BOOST_CHECK( TmpS >= 0 && TmpS > 0 && 0 < TmpS && 0 <= TmpS);
- TmpS |= R1S;
- BOOST_CHECK( TmpS >= R1S ); BOOST_CHECK( (TmpS == R1S) != (TmpS > R1S)); BOOST_CHECK( (TmpS == R1S) || !( TmpS <= R1S));
- BOOST_CHECK( R1S <= TmpS ); BOOST_CHECK( (R1S == TmpS) != (R1S < TmpS)); BOOST_CHECK( (TmpS == R1S) || !( R1S >= TmpS));
- BOOST_CHECK(! (TmpS < R1S)); BOOST_CHECK(! (R1S > TmpS));
+ uint256 LastL;
+ for (int i = 255; i >= 0; --i) {
+ uint256 TmpL;
+ *(TmpL.begin() + (i>>3)) |= 1<<(7-(i&7));
+ BOOST_CHECK( LastL < TmpL );
+ LastL = TmpL;
}
-}
-BOOST_AUTO_TEST_CASE( plusMinus )
-{
- uint256 TmpL = 0;
- BOOST_CHECK(R1L+R2L == uint256(R1LplusR2L));
- TmpL += R1L;
- BOOST_CHECK(TmpL == R1L);
- TmpL += R2L;
- BOOST_CHECK(TmpL == R1L + R2L);
- BOOST_CHECK(OneL+MaxL == ZeroL);
- BOOST_CHECK(MaxL+OneL == ZeroL);
- for (unsigned int i = 1; i < 256; ++i) {
- BOOST_CHECK( (MaxL >> i) + OneL == (HalfL >> (i-1)) );
- BOOST_CHECK( OneL + (MaxL >> i) == (HalfL >> (i-1)) );
- TmpL = (MaxL>>i); TmpL += OneL;
- BOOST_CHECK( TmpL == (HalfL >> (i-1)) );
- TmpL = (MaxL>>i); TmpL += 1;
- BOOST_CHECK( TmpL == (HalfL >> (i-1)) );
- TmpL = (MaxL>>i);
- BOOST_CHECK( TmpL++ == (MaxL>>i) );
- BOOST_CHECK( TmpL == (HalfL >> (i-1)));
+ BOOST_CHECK( ZeroL < R1L );
+ BOOST_CHECK( R2L < R1L );
+ BOOST_CHECK( ZeroL < OneL );
+ BOOST_CHECK( OneL < MaxL );
+ BOOST_CHECK( R1L < MaxL );
+ BOOST_CHECK( R2L < MaxL );
+
+ uint160 LastS;
+ for (int i = 159; i >= 0; --i) {
+ uint160 TmpS;
+ *(TmpS.begin() + (i>>3)) |= 1<<(7-(i&7));
+ BOOST_CHECK( LastS < TmpS );
+ LastS = TmpS;
}
- BOOST_CHECK(uint256(0xbedc77e27940a7ULL) + 0xee8d836fce66fbULL == uint256(0xbedc77e27940a7ULL + 0xee8d836fce66fbULL));
- TmpL = uint256(0xbedc77e27940a7ULL); TmpL += 0xee8d836fce66fbULL;
- BOOST_CHECK(TmpL == uint256(0xbedc77e27940a7ULL+0xee8d836fce66fbULL));
- TmpL -= 0xee8d836fce66fbULL; BOOST_CHECK(TmpL == 0xbedc77e27940a7ULL);
- TmpL = R1L;
- BOOST_CHECK(++TmpL == R1L+1);
-
- BOOST_CHECK(R1L -(-R2L) == R1L+R2L);
- BOOST_CHECK(R1L -(-OneL) == R1L+OneL);
- BOOST_CHECK(R1L - OneL == R1L+(-OneL));
- for (unsigned int i = 1; i < 256; ++i) {
- BOOST_CHECK((MaxL>>i) - (-OneL) == (HalfL >> (i-1)));
- BOOST_CHECK((HalfL >> (i-1)) - OneL == (MaxL>>i));
- TmpL = (HalfL >> (i-1));
- BOOST_CHECK(TmpL-- == (HalfL >> (i-1)));
- BOOST_CHECK(TmpL == (MaxL >> i));
- TmpL = (HalfL >> (i-1));
- BOOST_CHECK(--TmpL == (MaxL >> i));
- }
- TmpL = R1L;
- BOOST_CHECK(--TmpL == R1L-1);
-
- // 160-bit; copy-pasted
- uint160 TmpS = 0;
- BOOST_CHECK(R1S+R2S == uint160(R1LplusR2L));
- TmpS += R1S;
- BOOST_CHECK(TmpS == R1S);
- TmpS += R2S;
- BOOST_CHECK(TmpS == R1S + R2S);
- BOOST_CHECK(OneS+MaxS == ZeroS);
- BOOST_CHECK(MaxS+OneS == ZeroS);
- for (unsigned int i = 1; i < 160; ++i) {
- BOOST_CHECK( (MaxS >> i) + OneS == (HalfS >> (i-1)) );
- BOOST_CHECK( OneS + (MaxS >> i) == (HalfS >> (i-1)) );
- TmpS = (MaxS>>i); TmpS += OneS;
- BOOST_CHECK( TmpS == (HalfS >> (i-1)) );
- TmpS = (MaxS>>i); TmpS += 1;
- BOOST_CHECK( TmpS == (HalfS >> (i-1)) );
- TmpS = (MaxS>>i);
- BOOST_CHECK( TmpS++ == (MaxS>>i) );
- BOOST_CHECK( TmpS == (HalfS >> (i-1)));
- }
- BOOST_CHECK(uint160(0xbedc77e27940a7ULL) + 0xee8d836fce66fbULL == uint160(0xbedc77e27940a7ULL + 0xee8d836fce66fbULL));
- TmpS = uint160(0xbedc77e27940a7ULL); TmpS += 0xee8d836fce66fbULL;
- BOOST_CHECK(TmpS == uint160(0xbedc77e27940a7ULL+0xee8d836fce66fbULL));
- TmpS -= 0xee8d836fce66fbULL; BOOST_CHECK(TmpS == 0xbedc77e27940a7ULL);
- TmpS = R1S;
- BOOST_CHECK(++TmpS == R1S+1);
-
- BOOST_CHECK(R1S -(-R2S) == R1S+R2S);
- BOOST_CHECK(R1S -(-OneS) == R1S+OneS);
- BOOST_CHECK(R1S - OneS == R1S+(-OneS));
- for (unsigned int i = 1; i < 160; ++i) {
- BOOST_CHECK((MaxS>>i) - (-OneS) == (HalfS >> (i-1)));
- BOOST_CHECK((HalfS >> (i-1)) - OneS == (MaxS>>i));
- TmpS = (HalfS >> (i-1));
- BOOST_CHECK(TmpS-- == (HalfS >> (i-1)));
- BOOST_CHECK(TmpS == (MaxS >> i));
- TmpS = (HalfS >> (i-1));
- BOOST_CHECK(--TmpS == (MaxS >> i));
- }
- TmpS = R1S;
- BOOST_CHECK(--TmpS == R1S-1);
-
-}
-
-BOOST_AUTO_TEST_CASE( multiply )
-{
- BOOST_CHECK((R1L * R1L).ToString() == "62a38c0486f01e45879d7910a7761bf30d5237e9873f9bff3642a732c4d84f10");
- BOOST_CHECK((R1L * R2L).ToString() == "de37805e9986996cfba76ff6ba51c008df851987d9dd323f0e5de07760529c40");
- BOOST_CHECK((R1L * ZeroL) == ZeroL);
- BOOST_CHECK((R1L * OneL) == R1L);
- BOOST_CHECK((R1L * MaxL) == -R1L);
- BOOST_CHECK((R2L * R1L) == (R1L * R2L));
- BOOST_CHECK((R2L * R2L).ToString() == "ac8c010096767d3cae5005dec28bb2b45a1d85ab7996ccd3e102a650f74ff100");
- BOOST_CHECK((R2L * ZeroL) == ZeroL);
- BOOST_CHECK((R2L * OneL) == R2L);
- BOOST_CHECK((R2L * MaxL) == -R2L);
-
- BOOST_CHECK((R1S * R1S).ToString() == "a7761bf30d5237e9873f9bff3642a732c4d84f10");
- BOOST_CHECK((R1S * R2S).ToString() == "ba51c008df851987d9dd323f0e5de07760529c40");
- BOOST_CHECK((R1S * ZeroS) == ZeroS);
- BOOST_CHECK((R1S * OneS) == R1S);
- BOOST_CHECK((R1S * MaxS) == -R1S);
- BOOST_CHECK((R2S * R1S) == (R1S * R2S));
- BOOST_CHECK((R2S * R2S).ToString() == "c28bb2b45a1d85ab7996ccd3e102a650f74ff100");
- BOOST_CHECK((R2S * ZeroS) == ZeroS);
- BOOST_CHECK((R2S * OneS) == R2S);
- BOOST_CHECK((R2S * MaxS) == -R2S);
-
- BOOST_CHECK(MaxL * MaxL == OneL);
- BOOST_CHECK(MaxS * MaxS == OneS);
-
- BOOST_CHECK((R1L * 0) == 0);
- BOOST_CHECK((R1L * 1) == R1L);
- BOOST_CHECK((R1L * 3).ToString() == "7759b1c0ed14047f961ad09b20ff83687876a0181a367b813634046f91def7d4");
- BOOST_CHECK((R2L * 0x87654321UL).ToString() == "23f7816e30c4ae2017257b7a0fa64d60402f5234d46e746b61c960d09a26d070");
- BOOST_CHECK((R1S * 0) == 0);
- BOOST_CHECK((R1S * 1) == R1S);
- BOOST_CHECK((R1S * 7).ToString() == "f7a987f3c3bf758d927f202d7e795faeff084244");
- BOOST_CHECK((R2S * 0xFFFFFFFFUL).ToString() == "1c6f6c930353e17f7d6127213bb18d2883e2cd90");
-}
-
-BOOST_AUTO_TEST_CASE( divide )
-{
- uint256 D1L("AD7133AC1977FA2B7");
- uint256 D2L("ECD751716");
- BOOST_CHECK((R1L / D1L).ToString() == "00000000000000000b8ac01106981635d9ed112290f8895545a7654dde28fb3a");
- BOOST_CHECK((R1L / D2L).ToString() == "000000000873ce8efec5b67150bad3aa8c5fcb70e947586153bf2cec7c37c57a");
- BOOST_CHECK(R1L / OneL == R1L);
- BOOST_CHECK(R1L / MaxL == ZeroL);
- BOOST_CHECK(MaxL / R1L == 2);
- BOOST_CHECK_THROW(R1L / ZeroL, uint_error);
- BOOST_CHECK((R2L / D1L).ToString() == "000000000000000013e1665895a1cc981de6d93670105a6b3ec3b73141b3a3c5");
- BOOST_CHECK((R2L / D2L).ToString() == "000000000e8f0abe753bb0afe2e9437ee85d280be60882cf0bd1aaf7fa3cc2c4");
- BOOST_CHECK(R2L / OneL == R2L);
- BOOST_CHECK(R2L / MaxL == ZeroL);
- BOOST_CHECK(MaxL / R2L == 1);
- BOOST_CHECK_THROW(R2L / ZeroL, uint_error);
-
- uint160 D1S("D3C5EDCDEA54EB92679F0A4B4");
- uint160 D2S("13037");
- BOOST_CHECK((R1S / D1S).ToString() == "0000000000000000000000000db9af3beade6c02");
- BOOST_CHECK((R1S / D2S).ToString() == "000098dfb6cc40ca592bf74366794f298ada205c");
- BOOST_CHECK(R1S / OneS == R1S);
- BOOST_CHECK(R1S / MaxS == ZeroS);
- BOOST_CHECK(MaxS / R1S == 1);
- BOOST_CHECK_THROW(R1S / ZeroS, uint_error);
- BOOST_CHECK((R2S / D1S).ToString() == "0000000000000000000000000c5608e781182047");
- BOOST_CHECK((R2S / D2S).ToString() == "00008966751b7187c3c67c1fda5cea7db2c1c069");
- BOOST_CHECK(R2S / OneS == R2S);
- BOOST_CHECK(R2S / MaxS == ZeroS);
- BOOST_CHECK(MaxS / R2S == 1);
- BOOST_CHECK_THROW(R2S / ZeroS, uint_error);
-}
-
-
-bool almostEqual(double d1, double d2)
-{
- return fabs(d1-d2) <= 4*fabs(d1)*std::numeric_limits<double>::epsilon();
+ BOOST_CHECK( ZeroS < R1S );
+ BOOST_CHECK( R2S < R1S );
+ BOOST_CHECK( ZeroS < OneS );
+ BOOST_CHECK( OneS < MaxS );
+ BOOST_CHECK( R1S < MaxS );
+ BOOST_CHECK( R2S < MaxS );
}
BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 GetSerializeSize, Serialize, Unserialize
{
BOOST_CHECK(R1L.GetHex() == R1L.ToString());
BOOST_CHECK(R2L.GetHex() == R2L.ToString());
BOOST_CHECK(OneL.GetHex() == OneL.ToString());
BOOST_CHECK(MaxL.GetHex() == MaxL.ToString());
uint256 TmpL(R1L);
BOOST_CHECK(TmpL == R1L);
TmpL.SetHex(R2L.ToString()); BOOST_CHECK(TmpL == R2L);
- TmpL.SetHex(ZeroL.ToString()); BOOST_CHECK(TmpL == 0);
- TmpL.SetHex(HalfL.ToString()); BOOST_CHECK(TmpL == HalfL);
+ TmpL.SetHex(ZeroL.ToString()); BOOST_CHECK(TmpL == uint256());
TmpL.SetHex(R1L.ToString());
BOOST_CHECK(memcmp(R1L.begin(), R1Array, 32)==0);
BOOST_CHECK(memcmp(TmpL.begin(), R1Array, 32)==0);
BOOST_CHECK(memcmp(R2L.begin(), R2Array, 32)==0);
BOOST_CHECK(memcmp(ZeroL.begin(), ZeroArray, 32)==0);
BOOST_CHECK(memcmp(OneL.begin(), OneArray, 32)==0);
+ BOOST_CHECK(R1L.size() == sizeof(R1L));
+ BOOST_CHECK(sizeof(R1L) == 32);
BOOST_CHECK(R1L.size() == 32);
BOOST_CHECK(R2L.size() == 32);
BOOST_CHECK(ZeroL.size() == 32);
BOOST_CHECK(MaxL.size() == 32);
BOOST_CHECK(R1L.begin() + 32 == R1L.end());
BOOST_CHECK(R2L.begin() + 32 == R2L.end());
BOOST_CHECK(OneL.begin() + 32 == OneL.end());
BOOST_CHECK(MaxL.begin() + 32 == MaxL.end());
BOOST_CHECK(TmpL.begin() + 32 == TmpL.end());
- BOOST_CHECK(R1L.GetLow64() == R1LLow64);
- BOOST_CHECK(HalfL.GetLow64() ==0x0000000000000000ULL);
- BOOST_CHECK(OneL.GetLow64() ==0x0000000000000001ULL);
BOOST_CHECK(R1L.GetSerializeSize(0,PROTOCOL_VERSION) == 32);
BOOST_CHECK(ZeroL.GetSerializeSize(0,PROTOCOL_VERSION) == 32);
std::stringstream ss;
R1L.Serialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+32));
TmpL.Unserialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(R1L == TmpL);
ss.str("");
ZeroL.Serialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ss.str() == std::string(ZeroArray,ZeroArray+32));
TmpL.Unserialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ZeroL == TmpL);
ss.str("");
MaxL.Serialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ss.str() == std::string(MaxArray,MaxArray+32));
TmpL.Unserialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(MaxL == TmpL);
ss.str("");
BOOST_CHECK(R1S.GetHex() == R1S.ToString());
BOOST_CHECK(R2S.GetHex() == R2S.ToString());
BOOST_CHECK(OneS.GetHex() == OneS.ToString());
BOOST_CHECK(MaxS.GetHex() == MaxS.ToString());
uint160 TmpS(R1S);
BOOST_CHECK(TmpS == R1S);
TmpS.SetHex(R2S.ToString()); BOOST_CHECK(TmpS == R2S);
- TmpS.SetHex(ZeroS.ToString()); BOOST_CHECK(TmpS == 0);
- TmpS.SetHex(HalfS.ToString()); BOOST_CHECK(TmpS == HalfS);
+ TmpS.SetHex(ZeroS.ToString()); BOOST_CHECK(TmpS == uint160());
TmpS.SetHex(R1S.ToString());
BOOST_CHECK(memcmp(R1S.begin(), R1Array, 20)==0);
BOOST_CHECK(memcmp(TmpS.begin(), R1Array, 20)==0);
BOOST_CHECK(memcmp(R2S.begin(), R2Array, 20)==0);
BOOST_CHECK(memcmp(ZeroS.begin(), ZeroArray, 20)==0);
BOOST_CHECK(memcmp(OneS.begin(), OneArray, 20)==0);
+ BOOST_CHECK(R1S.size() == sizeof(R1S));
+ BOOST_CHECK(sizeof(R1S) == 20);
BOOST_CHECK(R1S.size() == 20);
BOOST_CHECK(R2S.size() == 20);
BOOST_CHECK(ZeroS.size() == 20);
BOOST_CHECK(MaxS.size() == 20);
BOOST_CHECK(R1S.begin() + 20 == R1S.end());
BOOST_CHECK(R2S.begin() + 20 == R2S.end());
BOOST_CHECK(OneS.begin() + 20 == OneS.end());
BOOST_CHECK(MaxS.begin() + 20 == MaxS.end());
BOOST_CHECK(TmpS.begin() + 20 == TmpS.end());
- BOOST_CHECK(R1S.GetLow64() == R1LLow64);
- BOOST_CHECK(HalfS.GetLow64() ==0x0000000000000000ULL);
- BOOST_CHECK(OneS.GetLow64() ==0x0000000000000001ULL);
BOOST_CHECK(R1S.GetSerializeSize(0,PROTOCOL_VERSION) == 20);
BOOST_CHECK(ZeroS.GetSerializeSize(0,PROTOCOL_VERSION) == 20);
R1S.Serialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+20));
TmpS.Unserialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(R1S == TmpS);
ss.str("");
ZeroS.Serialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ss.str() == std::string(ZeroArray,ZeroArray+20));
TmpS.Unserialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ZeroS == TmpS);
ss.str("");
MaxS.Serialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(ss.str() == std::string(MaxArray,MaxArray+20));
TmpS.Unserialize(ss,0,PROTOCOL_VERSION);
BOOST_CHECK(MaxS == TmpS);
ss.str("");
-
- for (unsigned int i = 0; i < 255; ++i)
- {
- BOOST_CHECK((OneL << i).getdouble() == ldexp(1.0,i));
- if (i < 160) BOOST_CHECK((OneS << i).getdouble() == ldexp(1.0,i));
- }
- BOOST_CHECK(ZeroL.getdouble() == 0.0);
- BOOST_CHECK(ZeroS.getdouble() == 0.0);
- for (int i = 256; i > 53; --i)
- BOOST_CHECK(almostEqual((R1L>>(256-i)).getdouble(), ldexp(R1Ldouble,i)));
- for (int i = 160; i > 53; --i)
- BOOST_CHECK(almostEqual((R1S>>(160-i)).getdouble(), ldexp(R1Sdouble,i)));
- uint64_t R1L64part = (R1L>>192).GetLow64();
- uint64_t R1S64part = (R1S>>96).GetLow64();
- for (int i = 53; i > 0; --i) // doubles can store all integers in {0,...,2^54-1} exactly
- {
- BOOST_CHECK((R1L>>(256-i)).getdouble() == (double)(R1L64part >> (64-i)));
- BOOST_CHECK((R1S>>(160-i)).getdouble() == (double)(R1S64part >> (64-i)));
- }
}
-BOOST_AUTO_TEST_CASE(bignum_SetCompact)
+BOOST_AUTO_TEST_CASE( conversion )
{
- uint256 num;
- bool fNegative;
- bool fOverflow;
- num.SetCompact(0, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x00123456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x01003456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x02000056, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x03000000, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x04000000, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x00923456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x01803456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x02800056, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x03800000, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x04800000, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x01123456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000000012");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0x01120000U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- // Make sure that we don't generate compacts with the 0x00800000 bit set
- num = 0x80;
- BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000U);
-
- num.SetCompact(0x01fedcba, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "000000000000000000000000000000000000000000000000000000000000007e");
- BOOST_CHECK_EQUAL(num.GetCompact(true), 0x01fe0000U);
- BOOST_CHECK_EQUAL(fNegative, true);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x02123456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000001234");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x03123456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000000123456");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x04123456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000012345600");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x04923456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000012345600");
- BOOST_CHECK_EQUAL(num.GetCompact(true), 0x04923456U);
- BOOST_CHECK_EQUAL(fNegative, true);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x05009234, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "0000000000000000000000000000000000000000000000000000000092340000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0x20123456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(num.GetHex(), "1234560000000000000000000000000000000000000000000000000000000000");
- BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456U);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, false);
-
- num.SetCompact(0xff123456, &fNegative, &fOverflow);
- BOOST_CHECK_EQUAL(fNegative, false);
- BOOST_CHECK_EQUAL(fOverflow, true);
-}
-
-
-BOOST_AUTO_TEST_CASE( getmaxcoverage ) // some more tests just to get 100% coverage
-{
- // ~R1L give a base_uint<256>
- BOOST_CHECK((~~R1L >> 10) == (R1L >> 10)); BOOST_CHECK((~~R1S >> 10) == (R1S >> 10));
- BOOST_CHECK((~~R1L << 10) == (R1L << 10)); BOOST_CHECK((~~R1S << 10) == (R1S << 10));
- BOOST_CHECK(!(~~R1L < R1L)); BOOST_CHECK(!(~~R1S < R1S));
- BOOST_CHECK(~~R1L <= R1L); BOOST_CHECK(~~R1S <= R1S);
- BOOST_CHECK(!(~~R1L > R1L)); BOOST_CHECK(!(~~R1S > R1S));
- BOOST_CHECK(~~R1L >= R1L); BOOST_CHECK(~~R1S >= R1S);
- BOOST_CHECK(!(R1L < ~~R1L)); BOOST_CHECK(!(R1S < ~~R1S));
- BOOST_CHECK(R1L <= ~~R1L); BOOST_CHECK(R1S <= ~~R1S);
- BOOST_CHECK(!(R1L > ~~R1L)); BOOST_CHECK(!(R1S > ~~R1S));
- BOOST_CHECK(R1L >= ~~R1L); BOOST_CHECK(R1S >= ~~R1S);
-
- BOOST_CHECK(~~R1L + R2L == R1L + ~~R2L);
- BOOST_CHECK(~~R1S + R2S == R1S + ~~R2S);
- BOOST_CHECK(~~R1L - R2L == R1L - ~~R2L);
- BOOST_CHECK(~~R1S - R2S == R1S - ~~R2S);
- BOOST_CHECK(~R1L != R1L); BOOST_CHECK(R1L != ~R1L);
- BOOST_CHECK(~R1S != R1S); BOOST_CHECK(R1S != ~R1S);
- unsigned char TmpArray[32];
- CHECKBITWISEOPERATOR(~R1,R2,|)
- CHECKBITWISEOPERATOR(~R1,R2,^)
- CHECKBITWISEOPERATOR(~R1,R2,&)
- CHECKBITWISEOPERATOR(R1,~R2,|)
- CHECKBITWISEOPERATOR(R1,~R2,^)
- CHECKBITWISEOPERATOR(R1,~R2,&)
+ BOOST_CHECK(ArithToUint256(UintToArith256(ZeroL)) == ZeroL);
+ BOOST_CHECK(ArithToUint256(UintToArith256(OneL)) == OneL);
+ BOOST_CHECK(ArithToUint256(UintToArith256(R1L)) == R1L);
+ BOOST_CHECK(ArithToUint256(UintToArith256(R2L)) == R2L);
+ BOOST_CHECK(UintToArith256(ZeroL) == 0);
+ BOOST_CHECK(UintToArith256(OneL) == 1);
+ BOOST_CHECK(ArithToUint256(0) == ZeroL);
+ BOOST_CHECK(ArithToUint256(1) == OneL);
+ BOOST_CHECK(arith_uint256(R1L.GetHex()) == UintToArith256(R1L));
+ BOOST_CHECK(arith_uint256(R2L.GetHex()) == UintToArith256(R2L));
+ BOOST_CHECK(R1L.GetHex() == UintToArith256(R1L).GetHex());
+ BOOST_CHECK(R2L.GetHex() == UintToArith256(R2L).GetHex());
}
BOOST_AUTO_TEST_SUITE_END()
-
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 5096ab19a..4467cdce7 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -1,227 +1,227 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "txdb.h"
#include "pow.h"
#include "uint256.h"
#include <stdint.h>
#include <boost/thread.hpp>
using namespace std;
void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) {
if (coins.IsPruned())
batch.Erase(make_pair('c', hash));
else
batch.Write(make_pair('c', hash), coins);
}
void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) {
batch.Write('B', hash);
}
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) {
}
bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const {
return db.Read(make_pair('c', txid), coins);
}
bool CCoinsViewDB::HaveCoins(const uint256 &txid) const {
return db.Exists(make_pair('c', txid));
}
uint256 CCoinsViewDB::GetBestBlock() const {
uint256 hashBestChain;
if (!db.Read('B', hashBestChain))
- return uint256(0);
+ return uint256();
return hashBestChain;
}
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
CLevelDBBatch batch;
size_t count = 0;
size_t changed = 0;
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
BatchWriteCoins(batch, it->first, it->second.coins);
changed++;
}
count++;
CCoinsMap::iterator itOld = it++;
mapCoins.erase(itOld);
}
- if (hashBlock != uint256(0))
+ if (!hashBlock.IsNull())
BatchWriteHashBestChain(batch, hashBlock);
LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
return db.WriteBatch(batch);
}
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
}
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
return Read(make_pair('f', nFile), info);
}
bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
if (fReindexing)
return Write('R', '1');
else
return Erase('R');
}
bool CBlockTreeDB::ReadReindexing(bool &fReindexing) {
fReindexing = Exists('R');
return true;
}
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
return Read('l', nFile);
}
bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
/* It seems that there are no "const iterators" for LevelDB. Since we
only need read operations on it, use a const-cast to get around
that restriction. */
boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
pcursor->SeekToFirst();
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = GetBestBlock();
ss << stats.hashBlock;
CAmount nTotalAmount = 0;
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;
ssKey >> chType;
if (chType == 'c') {
leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CCoins coins;
ssValue >> coins;
uint256 txhash;
ssKey >> txhash;
ss << txhash;
ss << VARINT(coins.nVersion);
ss << (coins.fCoinBase ? 'c' : 'n');
ss << VARINT(coins.nHeight);
stats.nTransactions++;
for (unsigned int i=0; i<coins.vout.size(); i++) {
const CTxOut &out = coins.vout[i];
if (!out.IsNull()) {
stats.nTransactionOutputs++;
ss << VARINT(i+1);
ss << out;
nTotalAmount += out.nValue;
}
}
stats.nSerializedSize += 32 + slValue.size();
ss << VARINT(0);
}
pcursor->Next();
} catch (const std::exception& e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
}
stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight;
stats.hashSerialized = ss.GetHash();
stats.nTotalAmount = nTotalAmount;
return true;
}
bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
CLevelDBBatch batch;
for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
batch.Write(make_pair('f', it->first), *it->second);
}
batch.Write('l', nLastFile);
for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
batch.Write(make_pair('b', (*it)->GetBlockHash()), CDiskBlockIndex(*it));
}
return WriteBatch(batch, true);
}
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
return Read(make_pair('t', txid), pos);
}
bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) {
CLevelDBBatch batch;
for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
batch.Write(make_pair('t', it->first), it->second);
return WriteBatch(batch);
}
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
return Write(std::make_pair('F', name), fValue ? '1' : '0');
}
bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
char ch;
if (!Read(std::make_pair('F', name), ch))
return false;
fValue = ch == '1';
return true;
}
bool CBlockTreeDB::LoadBlockIndexGuts()
{
boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
- ssKeySet << make_pair('b', uint256(0));
+ ssKeySet << make_pair('b', uint256());
pcursor->Seek(ssKeySet.str());
// Load mapBlockIndex
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;
ssKey >> chType;
if (chType == 'b') {
leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CDiskBlockIndex diskindex;
ssValue >> diskindex;
// Construct block index object
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
pindexNew->nHeight = diskindex.nHeight;
pindexNew->nFile = diskindex.nFile;
pindexNew->nDataPos = diskindex.nDataPos;
pindexNew->nUndoPos = diskindex.nUndoPos;
pindexNew->nVersion = diskindex.nVersion;
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->nTime = diskindex.nTime;
pindexNew->nBits = diskindex.nBits;
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits))
return error("LoadBlockIndex() : CheckProofOfWork failed: %s", pindexNew->ToString());
pcursor->Next();
} else {
break; // if shutdown requested or finished loading block index
}
} catch (const std::exception& e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
}
return true;
}
diff --git a/src/uint256.cpp b/src/uint256.cpp
index 918c1a7cd..3b1334a03 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -1,357 +1,146 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "uint256.h"
#include "utilstrencodings.h"
#include <stdio.h>
#include <string.h>
template <unsigned int BITS>
-base_uint<BITS>::base_uint(const std::string& str)
+base_blob<BITS>::base_blob(const std::vector<unsigned char>& vch)
{
- SetHex(str);
+ assert(vch.size() == sizeof(data));
+ memcpy(data, &vch[0], sizeof(data));
}
template <unsigned int BITS>
-base_uint<BITS>::base_uint(const std::vector<unsigned char>& vch)
+std::string base_blob<BITS>::GetHex() const
{
- if (vch.size() != sizeof(pn))
- throw uint_error("Converting vector of wrong size to base_uint");
- memcpy(pn, &vch[0], sizeof(pn));
+ char psz[sizeof(data) * 2 + 1];
+ for (unsigned int i = 0; i < sizeof(data); i++)
+ sprintf(psz + i * 2, "%02x", data[sizeof(data) - i - 1]);
+ return std::string(psz, psz + sizeof(data) * 2);
}
template <unsigned int BITS>
-base_uint<BITS>& base_uint<BITS>::operator<<=(unsigned int shift)
+void base_blob<BITS>::SetHex(const char* psz)
{
- base_uint<BITS> a(*this);
- for (int i = 0; i < WIDTH; i++)
- pn[i] = 0;
- int k = shift / 32;
- shift = shift % 32;
- for (int i = 0; i < WIDTH; i++) {
- if (i + k + 1 < WIDTH && shift != 0)
- pn[i + k + 1] |= (a.pn[i] >> (32 - shift));
- if (i + k < WIDTH)
- pn[i + k] |= (a.pn[i] << shift);
- }
- return *this;
-}
-
-template <unsigned int BITS>
-base_uint<BITS>& base_uint<BITS>::operator>>=(unsigned int shift)
-{
- base_uint<BITS> a(*this);
- for (int i = 0; i < WIDTH; i++)
- pn[i] = 0;
- int k = shift / 32;
- shift = shift % 32;
- for (int i = 0; i < WIDTH; i++) {
- if (i - k - 1 >= 0 && shift != 0)
- pn[i - k - 1] |= (a.pn[i] << (32 - shift));
- if (i - k >= 0)
- pn[i - k] |= (a.pn[i] >> shift);
- }
- return *this;
-}
-
-template <unsigned int BITS>
-base_uint<BITS>& base_uint<BITS>::operator*=(uint32_t b32)
-{
- uint64_t carry = 0;
- for (int i = 0; i < WIDTH; i++) {
- uint64_t n = carry + (uint64_t)b32 * pn[i];
- pn[i] = n & 0xffffffff;
- carry = n >> 32;
- }
- return *this;
-}
-
-template <unsigned int BITS>
-base_uint<BITS>& base_uint<BITS>::operator*=(const base_uint& b)
-{
- base_uint<BITS> a = *this;
- *this = 0;
- for (int j = 0; j < WIDTH; j++) {
- uint64_t carry = 0;
- for (int i = 0; i + j < WIDTH; i++) {
- uint64_t n = carry + pn[i + j] + (uint64_t)a.pn[j] * b.pn[i];
- pn[i + j] = n & 0xffffffff;
- carry = n >> 32;
- }
- }
- return *this;
-}
-
-template <unsigned int BITS>
-base_uint<BITS>& base_uint<BITS>::operator/=(const base_uint& b)
-{
- base_uint<BITS> div = b; // make a copy, so we can shift.
- base_uint<BITS> num = *this; // make a copy, so we can subtract.
- *this = 0; // the quotient.
- int num_bits = num.bits();
- int div_bits = div.bits();
- if (div_bits == 0)
- throw uint_error("Division by zero");
- if (div_bits > num_bits) // the result is certainly 0.
- return *this;
- int shift = num_bits - div_bits;
- div <<= shift; // shift so that div and num align.
- while (shift >= 0) {
- if (num >= div) {
- num -= div;
- pn[shift / 32] |= (1 << (shift & 31)); // set a bit of the result.
- }
- div >>= 1; // shift back.
- shift--;
- }
- // num now contains the remainder of the division.
- return *this;
-}
-
-template <unsigned int BITS>
-int base_uint<BITS>::CompareTo(const base_uint<BITS>& b) const
-{
- for (int i = WIDTH - 1; i >= 0; i--) {
- if (pn[i] < b.pn[i])
- return -1;
- if (pn[i] > b.pn[i])
- return 1;
- }
- return 0;
-}
-
-template <unsigned int BITS>
-bool base_uint<BITS>::EqualTo(uint64_t b) const
-{
- for (int i = WIDTH - 1; i >= 2; i--) {
- if (pn[i])
- return false;
- }
- if (pn[1] != (b >> 32))
- return false;
- if (pn[0] != (b & 0xfffffffful))
- return false;
- return true;
-}
-
-template <unsigned int BITS>
-double base_uint<BITS>::getdouble() const
-{
- double ret = 0.0;
- double fact = 1.0;
- for (int i = 0; i < WIDTH; i++) {
- ret += fact * pn[i];
- fact *= 4294967296.0;
- }
- return ret;
-}
-
-template <unsigned int BITS>
-std::string base_uint<BITS>::GetHex() const
-{
- char psz[sizeof(pn) * 2 + 1];
- for (unsigned int i = 0; i < sizeof(pn); i++)
- sprintf(psz + i * 2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]);
- return std::string(psz, psz + sizeof(pn) * 2);
-}
-
-template <unsigned int BITS>
-void base_uint<BITS>::SetHex(const char* psz)
-{
- memset(pn, 0, sizeof(pn));
+ memset(data, 0, sizeof(data));
// skip leading spaces
while (isspace(*psz))
psz++;
// skip 0x
if (psz[0] == '0' && tolower(psz[1]) == 'x')
psz += 2;
// hex string to uint
const char* pbegin = psz;
while (::HexDigit(*psz) != -1)
psz++;
psz--;
- unsigned char* p1 = (unsigned char*)pn;
+ unsigned char* p1 = (unsigned char*)data;
unsigned char* pend = p1 + WIDTH * 4;
while (psz >= pbegin && p1 < pend) {
*p1 = ::HexDigit(*psz--);
if (psz >= pbegin) {
*p1 |= ((unsigned char)::HexDigit(*psz--) << 4);
p1++;
}
}
}
template <unsigned int BITS>
-void base_uint<BITS>::SetHex(const std::string& str)
+void base_blob<BITS>::SetHex(const std::string& str)
{
SetHex(str.c_str());
}
template <unsigned int BITS>
-std::string base_uint<BITS>::ToString() const
+std::string base_blob<BITS>::ToString() const
{
return (GetHex());
}
-template <unsigned int BITS>
-unsigned int base_uint<BITS>::bits() const
-{
- for (int pos = WIDTH - 1; pos >= 0; pos--) {
- if (pn[pos]) {
- for (int bits = 31; bits > 0; bits--) {
- if (pn[pos] & 1 << bits)
- return 32 * pos + bits + 1;
- }
- return 32 * pos + 1;
- }
- }
- return 0;
-}
-
-// Explicit instantiations for base_uint<160>
-template base_uint<160>::base_uint(const std::string&);
-template base_uint<160>::base_uint(const std::vector<unsigned char>&);
-template base_uint<160>& base_uint<160>::operator<<=(unsigned int);
-template base_uint<160>& base_uint<160>::operator>>=(unsigned int);
-template base_uint<160>& base_uint<160>::operator*=(uint32_t b32);
-template base_uint<160>& base_uint<160>::operator*=(const base_uint<160>& b);
-template base_uint<160>& base_uint<160>::operator/=(const base_uint<160>& b);
-template int base_uint<160>::CompareTo(const base_uint<160>&) const;
-template bool base_uint<160>::EqualTo(uint64_t) const;
-template double base_uint<160>::getdouble() const;
-template std::string base_uint<160>::GetHex() const;
-template std::string base_uint<160>::ToString() const;
-template void base_uint<160>::SetHex(const char*);
-template void base_uint<160>::SetHex(const std::string&);
-template unsigned int base_uint<160>::bits() const;
+// Explicit instantiations for base_blob<160>
+template base_blob<160>::base_blob(const std::vector<unsigned char>&);
+template std::string base_blob<160>::GetHex() const;
+template std::string base_blob<160>::ToString() const;
+template void base_blob<160>::SetHex(const char*);
+template void base_blob<160>::SetHex(const std::string&);
-// Explicit instantiations for base_uint<256>
-template base_uint<256>::base_uint(const std::string&);
-template base_uint<256>::base_uint(const std::vector<unsigned char>&);
-template base_uint<256>& base_uint<256>::operator<<=(unsigned int);
-template base_uint<256>& base_uint<256>::operator>>=(unsigned int);
-template base_uint<256>& base_uint<256>::operator*=(uint32_t b32);
-template base_uint<256>& base_uint<256>::operator*=(const base_uint<256>& b);
-template base_uint<256>& base_uint<256>::operator/=(const base_uint<256>& b);
-template int base_uint<256>::CompareTo(const base_uint<256>&) const;
-template bool base_uint<256>::EqualTo(uint64_t) const;
-template double base_uint<256>::getdouble() const;
-template std::string base_uint<256>::GetHex() const;
-template std::string base_uint<256>::ToString() const;
-template void base_uint<256>::SetHex(const char*);
-template void base_uint<256>::SetHex(const std::string&);
-template unsigned int base_uint<256>::bits() const;
-
-// This implementation directly uses shifts instead of going
-// through an intermediate MPI representation.
-uint256& uint256::SetCompact(uint32_t nCompact, bool* pfNegative, bool* pfOverflow)
-{
- int nSize = nCompact >> 24;
- uint32_t nWord = nCompact & 0x007fffff;
- if (nSize <= 3) {
- nWord >>= 8 * (3 - nSize);
- *this = nWord;
- } else {
- *this = nWord;
- *this <<= 8 * (nSize - 3);
- }
- if (pfNegative)
- *pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0;
- if (pfOverflow)
- *pfOverflow = nWord != 0 && ((nSize > 34) ||
- (nWord > 0xff && nSize > 33) ||
- (nWord > 0xffff && nSize > 32));
- return *this;
-}
-
-uint32_t uint256::GetCompact(bool fNegative) const
-{
- int nSize = (bits() + 7) / 8;
- uint32_t nCompact = 0;
- if (nSize <= 3) {
- nCompact = GetLow64() << 8 * (3 - nSize);
- } else {
- uint256 bn = *this >> 8 * (nSize - 3);
- nCompact = bn.GetLow64();
- }
- // The 0x00800000 bit denotes the sign.
- // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
- if (nCompact & 0x00800000) {
- nCompact >>= 8;
- nSize++;
- }
- assert((nCompact & ~0x007fffff) == 0);
- assert(nSize < 256);
- nCompact |= nSize << 24;
- nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0);
- return nCompact;
-}
+// Explicit instantiations for base_blob<256>
+template base_blob<256>::base_blob(const std::vector<unsigned char>&);
+template std::string base_blob<256>::GetHex() const;
+template std::string base_blob<256>::ToString() const;
+template void base_blob<256>::SetHex(const char*);
+template void base_blob<256>::SetHex(const std::string&);
static void inline HashMix(uint32_t& a, uint32_t& b, uint32_t& c)
{
// Taken from lookup3, by Bob Jenkins.
a -= c;
a ^= ((c << 4) | (c >> 28));
c += b;
b -= a;
b ^= ((a << 6) | (a >> 26));
a += c;
c -= b;
c ^= ((b << 8) | (b >> 24));
b += a;
a -= c;
a ^= ((c << 16) | (c >> 16));
c += b;
b -= a;
b ^= ((a << 19) | (a >> 13));
a += c;
c -= b;
c ^= ((b << 4) | (b >> 28));
b += a;
}
static void inline HashFinal(uint32_t& a, uint32_t& b, uint32_t& c)
{
// Taken from lookup3, by Bob Jenkins.
c ^= b;
c -= ((b << 14) | (b >> 18));
a ^= c;
a -= ((c << 11) | (c >> 21));
b ^= a;
b -= ((a << 25) | (a >> 7));
c ^= b;
c -= ((b << 16) | (b >> 16));
a ^= c;
a -= ((c << 4) | (c >> 28));
b ^= a;
b -= ((a << 14) | (a >> 18));
c ^= b;
c -= ((b << 24) | (b >> 8));
}
uint64_t uint256::GetHash(const uint256& salt) const
{
uint32_t a, b, c;
+ const uint32_t *pn = (const uint32_t*)data;
+ const uint32_t *salt_pn = (const uint32_t*)salt.data;
a = b = c = 0xdeadbeef + (WIDTH << 2);
- a += pn[0] ^ salt.pn[0];
- b += pn[1] ^ salt.pn[1];
- c += pn[2] ^ salt.pn[2];
+ a += pn[0] ^ salt_pn[0];
+ b += pn[1] ^ salt_pn[1];
+ c += pn[2] ^ salt_pn[2];
HashMix(a, b, c);
- a += pn[3] ^ salt.pn[3];
- b += pn[4] ^ salt.pn[4];
- c += pn[5] ^ salt.pn[5];
+ a += pn[3] ^ salt_pn[3];
+ b += pn[4] ^ salt_pn[4];
+ c += pn[5] ^ salt_pn[5];
HashMix(a, b, c);
- a += pn[6] ^ salt.pn[6];
- b += pn[7] ^ salt.pn[7];
+ a += pn[6] ^ salt_pn[6];
+ b += pn[7] ^ salt_pn[7];
HashFinal(a, b, c);
return ((((uint64_t)b) << 32) | c);
}
diff --git a/src/uint256.h b/src/uint256.h
index 139270ac4..6d016ab16 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -1,333 +1,158 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UINT256_H
#define BITCOIN_UINT256_H
#include <assert.h>
#include <cstring>
#include <stdexcept>
#include <stdint.h>
#include <string>
#include <vector>
-class uint_error : public std::runtime_error {
-public:
- explicit uint_error(const std::string& str) : std::runtime_error(str) {}
-};
-
-/** Template base class for unsigned big integers. */
+/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS>
-class base_uint
+class base_blob
{
protected:
- enum { WIDTH=BITS/32 };
- uint32_t pn[WIDTH];
+ enum { WIDTH=BITS/8 };
+ uint8_t data[WIDTH];
public:
-
- base_uint()
+ base_blob()
{
- for (int i = 0; i < WIDTH; i++)
- pn[i] = 0;
+ memset(data, 0, sizeof(data));
}
- base_uint(const base_uint& b)
- {
- for (int i = 0; i < WIDTH; i++)
- pn[i] = b.pn[i];
- }
+ explicit base_blob(const std::vector<unsigned char>& vch);
- base_uint& operator=(const base_uint& b)
+ bool IsNull() const
{
for (int i = 0; i < WIDTH; i++)
- pn[i] = b.pn[i];
- return *this;
- }
-
- base_uint(uint64_t b)
- {
- pn[0] = (unsigned int)b;
- pn[1] = (unsigned int)(b >> 32);
- for (int i = 2; i < WIDTH; i++)
- pn[i] = 0;
- }
-
- explicit base_uint(const std::string& str);
- explicit base_uint(const std::vector<unsigned char>& vch);
-
- bool operator!() const
- {
- for (int i = 0; i < WIDTH; i++)
- if (pn[i] != 0)
+ if (data[i] != 0)
return false;
return true;
}
- const base_uint operator~() const
- {
- base_uint ret;
- for (int i = 0; i < WIDTH; i++)
- ret.pn[i] = ~pn[i];
- return ret;
- }
-
- const base_uint operator-() const
- {
- base_uint ret;
- for (int i = 0; i < WIDTH; i++)
- ret.pn[i] = ~pn[i];
- ret++;
- return ret;
- }
-
- double getdouble() const;
-
- base_uint& operator=(uint64_t b)
- {
- pn[0] = (unsigned int)b;
- pn[1] = (unsigned int)(b >> 32);
- for (int i = 2; i < WIDTH; i++)
- pn[i] = 0;
- return *this;
- }
-
- base_uint& operator^=(const base_uint& b)
- {
- for (int i = 0; i < WIDTH; i++)
- pn[i] ^= b.pn[i];
- return *this;
- }
-
- base_uint& operator&=(const base_uint& b)
- {
- for (int i = 0; i < WIDTH; i++)
- pn[i] &= b.pn[i];
- return *this;
- }
-
- base_uint& operator|=(const base_uint& b)
- {
- for (int i = 0; i < WIDTH; i++)
- pn[i] |= b.pn[i];
- return *this;
- }
-
- base_uint& operator^=(uint64_t b)
- {
- pn[0] ^= (unsigned int)b;
- pn[1] ^= (unsigned int)(b >> 32);
- return *this;
- }
-
- base_uint& operator|=(uint64_t b)
+ void SetNull()
{
- pn[0] |= (unsigned int)b;
- pn[1] |= (unsigned int)(b >> 32);
- return *this;
+ memset(data, 0, sizeof(data));
}
- base_uint& operator<<=(unsigned int shift);
- base_uint& operator>>=(unsigned int shift);
-
- base_uint& operator+=(const base_uint& b)
- {
- uint64_t carry = 0;
- for (int i = 0; i < WIDTH; i++)
- {
- uint64_t n = carry + pn[i] + b.pn[i];
- pn[i] = n & 0xffffffff;
- carry = n >> 32;
- }
- return *this;
- }
-
- base_uint& operator-=(const base_uint& b)
- {
- *this += -b;
- return *this;
- }
-
- base_uint& operator+=(uint64_t b64)
- {
- base_uint b;
- b = b64;
- *this += b;
- return *this;
- }
-
- base_uint& operator-=(uint64_t b64)
- {
- base_uint b;
- b = b64;
- *this += -b;
- return *this;
- }
-
- base_uint& operator*=(uint32_t b32);
- base_uint& operator*=(const base_uint& b);
- base_uint& operator/=(const base_uint& b);
-
- base_uint& operator++()
- {
- // prefix operator
- int i = 0;
- while (++pn[i] == 0 && i < WIDTH-1)
- i++;
- return *this;
- }
-
- const base_uint operator++(int)
- {
- // postfix operator
- const base_uint ret = *this;
- ++(*this);
- return ret;
- }
-
- base_uint& operator--()
- {
- // prefix operator
- int i = 0;
- while (--pn[i] == (uint32_t)-1 && i < WIDTH-1)
- i++;
- return *this;
- }
-
- const base_uint operator--(int)
- {
- // postfix operator
- const base_uint ret = *this;
- --(*this);
- return ret;
- }
-
- int CompareTo(const base_uint& b) const;
- bool EqualTo(uint64_t b) const;
-
- friend inline const base_uint operator+(const base_uint& a, const base_uint& b) { return base_uint(a) += b; }
- friend inline const base_uint operator-(const base_uint& a, const base_uint& b) { return base_uint(a) -= b; }
- friend inline const base_uint operator*(const base_uint& a, const base_uint& b) { return base_uint(a) *= b; }
- friend inline const base_uint operator/(const base_uint& a, const base_uint& b) { return base_uint(a) /= b; }
- friend inline const base_uint operator|(const base_uint& a, const base_uint& b) { return base_uint(a) |= b; }
- friend inline const base_uint operator&(const base_uint& a, const base_uint& b) { return base_uint(a) &= b; }
- friend inline const base_uint operator^(const base_uint& a, const base_uint& b) { return base_uint(a) ^= b; }
- friend inline const base_uint operator>>(const base_uint& a, int shift) { return base_uint(a) >>= shift; }
- friend inline const base_uint operator<<(const base_uint& a, int shift) { return base_uint(a) <<= shift; }
- friend inline const base_uint operator*(const base_uint& a, uint32_t b) { return base_uint(a) *= b; }
- friend inline bool operator==(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) == 0; }
- friend inline bool operator!=(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) != 0; }
- friend inline bool operator>(const base_uint& a, const base_uint& b) { return a.CompareTo(b) > 0; }
- friend inline bool operator<(const base_uint& a, const base_uint& b) { return a.CompareTo(b) < 0; }
- friend inline bool operator>=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) >= 0; }
- friend inline bool operator<=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) <= 0; }
- friend inline bool operator==(const base_uint& a, uint64_t b) { return a.EqualTo(b); }
- friend inline bool operator!=(const base_uint& a, uint64_t b) { return !a.EqualTo(b); }
+ friend inline bool operator==(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) == 0; }
+ friend inline bool operator!=(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) != 0; }
+ friend inline bool operator<(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) < 0; }
std::string GetHex() const;
void SetHex(const char* psz);
void SetHex(const std::string& str);
std::string ToString() const;
unsigned char* begin()
{
- return (unsigned char*)&pn[0];
+ return &data[0];
}
unsigned char* end()
{
- return (unsigned char*)&pn[WIDTH];
+ return &data[WIDTH];
}
const unsigned char* begin() const
{
- return (unsigned char*)&pn[0];
+ return &data[0];
}
const unsigned char* end() const
{
- return (unsigned char*)&pn[WIDTH];
+ return &data[WIDTH];
}
unsigned int size() const
{
- return sizeof(pn);
- }
-
- /**
- * Returns the position of the highest bit set plus one, or zero if the
- * value is zero.
- */
- unsigned int bits() const;
-
- uint64_t GetLow64() const
- {
- assert(WIDTH >= 2);
- return pn[0] | (uint64_t)pn[1] << 32;
+ return sizeof(data);
}
unsigned int GetSerializeSize(int nType, int nVersion) const
{
- return sizeof(pn);
+ return sizeof(data);
}
template<typename Stream>
void Serialize(Stream& s, int nType, int nVersion) const
{
- s.write((char*)pn, sizeof(pn));
+ s.write((char*)data, sizeof(data));
}
template<typename Stream>
void Unserialize(Stream& s, int nType, int nVersion)
{
- s.read((char*)pn, sizeof(pn));
+ s.read((char*)data, sizeof(data));
}
};
-/** 160-bit unsigned big integer. */
-class uint160 : public base_uint<160> {
+/** 160-bit opaque blob.
+ * @note This type is called uint160 for historical reasons only. It is an opaque
+ * blob of 160 bits and has no integer operations.
+ */
+class uint160 : public base_blob<160> {
public:
uint160() {}
- uint160(const base_uint<160>& b) : base_uint<160>(b) {}
- uint160(uint64_t b) : base_uint<160>(b) {}
- explicit uint160(const std::string& str) : base_uint<160>(str) {}
- explicit uint160(const std::vector<unsigned char>& vch) : base_uint<160>(vch) {}
+ uint160(const base_blob<160>& b) : base_blob<160>(b) {}
+ explicit uint160(const std::vector<unsigned char>& vch) : base_blob<160>(vch) {}
};
-/** 256-bit unsigned big integer. */
-class uint256 : public base_uint<256> {
+/** 256-bit opaque blob.
+ * @note This type is called uint256 for historical reasons only. It is an
+ * opaque blob of 256 bits and has no integer operations. Use arith_uint256 if
+ * those are required.
+ */
+class uint256 : public base_blob<256> {
public:
uint256() {}
- uint256(const base_uint<256>& b) : base_uint<256>(b) {}
- uint256(uint64_t b) : base_uint<256>(b) {}
- explicit uint256(const std::string& str) : base_uint<256>(str) {}
- explicit uint256(const std::vector<unsigned char>& vch) : base_uint<256>(vch) {}
-
- /**
- * The "compact" format is a representation of a whole
- * number N using an unsigned 32bit number similar to a
- * floating point format.
- * The most significant 8 bits are the unsigned exponent of base 256.
- * This exponent can be thought of as "number of bytes of N".
- * The lower 23 bits are the mantissa.
- * Bit number 24 (0x800000) represents the sign of N.
- * N = (-1^sign) * mantissa * 256^(exponent-3)
- *
- * Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().
- * MPI uses the most significant bit of the first byte as sign.
- * Thus 0x1234560000 is compact (0x05123456)
- * and 0xc0de000000 is compact (0x0600c0de)
- *
- * Bitcoin only uses this "compact" format for encoding difficulty
- * targets, which are unsigned 256bit quantities. Thus, all the
- * complexities of the sign bit and using base 256 are probably an
- * implementation accident.
+ uint256(const base_blob<256>& b) : base_blob<256>(b) {}
+ explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {}
+
+ /** A cheap hash function that just returns 64 bits from the result, it can be
+ * used when the contents are considered uniformly random. It is not appropriate
+ * when the value can easily be influenced from outside as e.g. a network adversary could
+ * provide values to trigger worst-case behavior.
+ * @note The result of this function is not stable between little and big endian.
*/
- uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL);
- uint32_t GetCompact(bool fNegative = false) const;
+ uint64_t GetCheapHash() const
+ {
+ uint64_t result;
+ memcpy((void*)&result, (void*)data, 8);
+ return result;
+ }
+ /** A more secure, salted hash function.
+ * @note This hash is not stable between little and big endian.
+ */
uint64_t GetHash(const uint256& salt) const;
};
+/* uint256 from const char *.
+ * This is a separate function because the constructor uint256(const char*) can result
+ * in dangerously catching uint256(0).
+ */
+inline uint256 uint256S(const char *str)
+{
+ uint256 rv;
+ rv.SetHex(str);
+ return rv;
+}
+/* uint256 from std::string.
+ * This is a separate function because the constructor uint256(const std::string &str) can result
+ * in dangerously catching uint256(0) via std::string(const char*).
+ */
+inline uint256 uint256S(const std::string& str)
+{
+ uint256 rv;
+ rv.SetHex(str);
+ return rv;
+}
+
#endif // BITCOIN_UINT256_H
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 4920cb21f..6c5af3bdc 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -1,2367 +1,2367 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "wallet.h"
#include "base58.h"
#include "checkpoints.h"
#include "coincontrol.h"
#include "net.h"
#include "script/script.h"
#include "script/sign.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
#include <assert.h>
#include <boost/algorithm/string/replace.hpp>
#include <boost/thread.hpp>
using namespace std;
/**
* Settings
*/
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
unsigned int nTxConfirmTarget = 1;
bool bSpendZeroConfChange = true;
bool fSendFreeTransactions = false;
bool fPayAtLeastCustomFee = true;
/**
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
* Override with -mintxfee
*/
CFeeRate CWallet::minTxFee = CFeeRate(1000);
/** @defgroup mapWallet
*
* @{
*/
struct CompareValueOnly
{
bool operator()(const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t1,
const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t2) const
{
return t1.first < t2.first;
}
};
std::string COutput::ToString() const
{
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue));
}
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
LOCK(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end())
return NULL;
return &(it->second);
}
CPubKey CWallet::GenerateNewKey()
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
CKey secret;
secret.MakeNewKey(fCompressed);
// Compressed public keys were introduced in version 0.6.0
if (fCompressed)
SetMinVersion(FEATURE_COMPRPUBKEY);
CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey));
// Create new metadata
int64_t nCreationTime = GetTime();
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
nTimeFirstKey = nCreationTime;
if (!AddKeyPubKey(secret, pubkey))
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
return pubkey;
}
bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey))
return false;
// check if we need to remove from watch-only
CScript script;
script = GetScriptForDestination(pubkey.GetID());
if (HaveWatchOnly(script))
RemoveWatchOnly(script);
if (!fFileBacked)
return true;
if (!IsCrypted()) {
return CWalletDB(strWalletFile).WriteKey(pubkey,
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
return true;
}
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const vector<unsigned char> &vchCryptedSecret)
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
if (!fFileBacked)
return true;
{
LOCK(cs_wallet);
if (pwalletdbEncryption)
return pwalletdbEncryption->WriteCryptedKey(vchPubKey,
vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]);
else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey,
vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]);
}
return false;
}
bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey))
nTimeFirstKey = meta.nCreateTime;
mapKeyMetadata[pubkey.GetID()] = meta;
return true;
}
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
}
bool CWallet::AddCScript(const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
bool CWallet::LoadCScript(const CScript& redeemScript)
{
/* A sanity check was added in pull #3843 to avoid adding redeemScripts
* that never can be redeemed. However, old wallets may still contain
* these. Do not add them to the wallet and warn. */
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
{
std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString();
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n",
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
return true;
}
return CCryptoKeyStore::AddCScript(redeemScript);
}
bool CWallet::AddWatchOnly(const CScript &dest)
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
return false;
nTimeFirstKey = 1; // No birthday information for watch-only keys.
NotifyWatchonlyChanged(true);
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).WriteWatchOnly(dest);
}
bool CWallet::RemoveWatchOnly(const CScript &dest)
{
AssertLockHeld(cs_wallet);
if (!CCryptoKeyStore::RemoveWatchOnly(dest))
return false;
if (!HaveWatchOnly())
NotifyWatchonlyChanged(false);
if (fFileBacked)
if (!CWalletDB(strWalletFile).EraseWatchOnly(dest))
return false;
return true;
}
bool CWallet::LoadWatchOnly(const CScript &dest)
{
return CCryptoKeyStore::AddWatchOnly(dest);
}
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
CCrypter crypter;
CKeyingMaterial vMasterKey;
{
LOCK(cs_wallet);
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{
if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
continue; // try another master key
if (CCryptoKeyStore::Unlock(vMasterKey))
return true;
}
}
return false;
}
bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
{
bool fWasLocked = IsLocked();
{
LOCK(cs_wallet);
Lock();
CCrypter crypter;
CKeyingMaterial vMasterKey;
BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{
if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false;
if (CCryptoKeyStore::Unlock(vMasterKey))
{
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
if (pMasterKey.second.nDeriveIterations < 25000)
pMasterKey.second.nDeriveIterations = 25000;
LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey))
return false;
CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
if (fWasLocked)
Lock();
return true;
}
}
}
return false;
}
void CWallet::SetBestChain(const CBlockLocator& loc)
{
CWalletDB walletdb(strWalletFile);
walletdb.WriteBestBlock(loc);
}
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
{
LOCK(cs_wallet); // nWalletVersion
if (nWalletVersion >= nVersion)
return true;
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
if (fExplicit && nVersion > nWalletMaxVersion)
nVersion = FEATURE_LATEST;
nWalletVersion = nVersion;
if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion;
if (fFileBacked)
{
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
if (nWalletVersion > 40000)
pwalletdb->WriteMinVersion(nWalletVersion);
if (!pwalletdbIn)
delete pwalletdb;
}
return true;
}
bool CWallet::SetMaxVersion(int nVersion)
{
LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion
// cannot downgrade below current version
if (nWalletVersion > nVersion)
return false;
nWalletMaxVersion = nVersion;
return true;
}
set<uint256> CWallet::GetConflicts(const uint256& txid) const
{
set<uint256> result;
AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid);
if (it == mapWallet.end())
return result;
const CWalletTx& wtx = it->second;
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
{
if (mapTxSpends.count(txin.prevout) <= 1)
continue; // No conflict if zero or one spends
range = mapTxSpends.equal_range(txin.prevout);
for (TxSpends::const_iterator it = range.first; it != range.second; ++it)
result.insert(it->second);
}
return result;
}
void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range)
{
// We want all the wallet transactions in range to have the same metadata as
// the oldest (smallest nOrderPos).
// So: find smallest nOrderPos:
int nMinOrderPos = std::numeric_limits<int>::max();
const CWalletTx* copyFrom = NULL;
for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
int n = mapWallet[hash].nOrderPos;
if (n < nMinOrderPos)
{
nMinOrderPos = n;
copyFrom = &mapWallet[hash];
}
}
// Now copy data from copyFrom to rest:
for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
CWalletTx* copyTo = &mapWallet[hash];
if (copyFrom == copyTo) continue;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// fTimeReceivedIsTxTime not copied on purpose
// nTimeReceived not copied on purpose
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
// nOrderPos not copied on purpose
// cached members not copied on purpose
}
}
/**
* Outpoint is spent if any non-conflicted transaction
* spends it:
*/
bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
{
const COutPoint outpoint(hash, n);
pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
range = mapTxSpends.equal_range(outpoint);
for (TxSpends::const_iterator it = range.first; it != range.second; ++it)
{
const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0)
return true; // Spent
}
return false;
}
void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
{
mapTxSpends.insert(make_pair(outpoint, wtxid));
pair<TxSpends::iterator, TxSpends::iterator> range;
range = mapTxSpends.equal_range(outpoint);
SyncMetaData(range);
}
void CWallet::AddToSpends(const uint256& wtxid)
{
assert(mapWallet.count(wtxid));
CWalletTx& thisTx = mapWallet[wtxid];
if (thisTx.IsCoinBase()) // Coinbases don't spend anything!
return;
BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
AddToSpends(txin.prevout, wtxid);
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
if (IsCrypted())
return false;
CKeyingMaterial vMasterKey;
RandAddSeedPerfmon();
vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
CMasterKey kMasterKey;
RandAddSeedPerfmon();
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
if (kMasterKey.nDeriveIterations < 25000)
kMasterKey.nDeriveIterations = 25000;
LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
return false;
if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
return false;
{
LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
if (fFileBacked)
{
assert(!pwalletdbEncryption);
pwalletdbEncryption = new CWalletDB(strWalletFile);
if (!pwalletdbEncryption->TxnBegin()) {
delete pwalletdbEncryption;
pwalletdbEncryption = NULL;
return false;
}
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
}
if (!EncryptKeys(vMasterKey))
{
if (fFileBacked) {
pwalletdbEncryption->TxnAbort();
delete pwalletdbEncryption;
}
// We now probably have half of our keys encrypted in memory, and half not...
// die and let the user reload their unencrypted wallet.
assert(false);
}
// Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
if (fFileBacked)
{
if (!pwalletdbEncryption->TxnCommit()) {
delete pwalletdbEncryption;
// We now have keys encrypted in memory, but not on disk...
// die to avoid confusion and let the user reload their unencrypted wallet.
assert(false);
}
delete pwalletdbEncryption;
pwalletdbEncryption = NULL;
}
Lock();
Unlock(strWalletPassphrase);
NewKeyPool();
Lock();
// Need to completely rewrite the wallet file; if we don't, bdb might keep
// bits of the unencrypted private key in slack space in the database file.
CDB::Rewrite(strWalletFile);
}
NotifyStatusChanged(this);
return true;
}
int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
{
AssertLockHeld(cs_wallet); // nOrderPosNext
int64_t nRet = nOrderPosNext++;
if (pwalletdb) {
pwalletdb->WriteOrderPosNext(nOrderPosNext);
} else {
CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext);
}
return nRet;
}
CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
{
AssertLockHeld(cs_wallet); // mapWallet
CWalletDB walletdb(strWalletFile);
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
TxItems txOrdered;
// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
// would make this much faster for applications that do this a lot.
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
CWalletTx* wtx = &((*it).second);
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
}
acentries.clear();
walletdb.ListAccountCreditDebit(strAccount, acentries);
BOOST_FOREACH(CAccountingEntry& entry, acentries)
{
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
}
return txOrdered;
}
void CWallet::MarkDirty()
{
{
LOCK(cs_wallet);
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
item.second.MarkDirty();
}
}
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
{
uint256 hash = wtxIn.GetHash();
if (fFromLoadWallet)
{
mapWallet[hash] = wtxIn;
mapWallet[hash].BindWallet(this);
AddToSpends(hash);
}
else
{
LOCK(cs_wallet);
// Inserts only if not already there, returns tx inserted or tx found
pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn));
CWalletTx& wtx = (*ret.first).second;
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew)
{
wtx.nTimeReceived = GetAdjustedTime();
wtx.nOrderPos = IncOrderPosNext();
wtx.nTimeSmart = wtx.nTimeReceived;
- if (wtxIn.hashBlock != 0)
+ if (!wtxIn.hashBlock.IsNull())
{
if (mapBlockIndex.count(wtxIn.hashBlock))
{
int64_t latestNow = wtx.nTimeReceived;
int64_t latestEntry = 0;
{
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
int64_t latestTolerated = latestNow + 300;
std::list<CAccountingEntry> acentries;
TxItems txOrdered = OrderedTxItems(acentries);
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx == &wtx)
continue;
CAccountingEntry *const pacentry = (*it).second.second;
int64_t nSmartTime;
if (pwtx)
{
nSmartTime = pwtx->nTimeSmart;
if (!nSmartTime)
nSmartTime = pwtx->nTimeReceived;
}
else
nSmartTime = pacentry->nTime;
if (nSmartTime <= latestTolerated)
{
latestEntry = nSmartTime;
if (nSmartTime > latestNow)
latestNow = nSmartTime;
break;
}
}
}
int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime();
wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
}
else
LogPrintf("AddToWallet() : found %s in block %s not in index\n",
wtxIn.GetHash().ToString(),
wtxIn.hashBlock.ToString());
}
AddToSpends(hash);
}
bool fUpdated = false;
if (!fInsertedNew)
{
// Merge
- if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock)
+ if (!wtxIn.hashBlock.IsNull() && wtxIn.hashBlock != wtx.hashBlock)
{
wtx.hashBlock = wtxIn.hashBlock;
fUpdated = true;
}
if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex))
{
wtx.vMerkleBranch = wtxIn.vMerkleBranch;
wtx.nIndex = wtxIn.nIndex;
fUpdated = true;
}
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
{
wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true;
}
}
//// debug print
LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
// Write to disk
if (fInsertedNew || fUpdated)
if (!wtx.WriteToDisk())
return false;
// Break debit/credit balance caches:
wtx.MarkDirty();
// Notify UI of new or updated transaction
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
// notify an external script when a wallet transaction comes in or is updated
std::string strCmd = GetArg("-walletnotify", "");
if ( !strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
}
return true;
}
/**
* Add a transaction to the wallet, or update it.
* pblock is optional, but should be provided if the transaction is known to be in a block.
* If fUpdate is true, existing transactions will be updated.
*/
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
{
{
AssertLockHeld(cs_wallet);
bool fExisted = mapWallet.count(tx.GetHash()) != 0;
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
CWalletTx wtx(this,tx);
// Get merkle branch if transaction was found in a block
if (pblock)
wtx.SetMerkleBranch(*pblock);
return AddToWallet(wtx);
}
}
return false;
}
void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock)
{
LOCK2(cs_main, cs_wallet);
if (!AddToWalletIfInvolvingMe(tx, pblock, true))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
if (mapWallet.count(txin.prevout.hash))
mapWallet[txin.prevout.hash].MarkDirty();
}
}
void CWallet::EraseFromWallet(const uint256 &hash)
{
if (!fFileBacked)
return;
{
LOCK(cs_wallet);
if (mapWallet.erase(hash))
CWalletDB(strWalletFile).EraseTx(hash);
}
return;
}
isminetype CWallet::IsMine(const CTxIn &txin) const
{
{
LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
return IsMine(prev.vout[txin.prevout.n]);
}
}
return ISMINE_NO;
}
CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{
{
LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]) & filter)
return prev.vout[txin.prevout.n].nValue;
}
}
return 0;
}
bool CWallet::IsChange(const CTxOut& txout) const
{
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a script that is ours, but is not in the address book
// is change. That assumption is likely to break when we implement multisignature
// wallets that return change back into a multi-signature-protected address;
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
if (::IsMine(*this, txout.scriptPubKey))
{
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address))
return true;
LOCK(cs_wallet);
if (!mapAddressBook.count(address))
return true;
}
return false;
}
int64_t CWalletTx::GetTxTime() const
{
int64_t n = nTimeSmart;
return n ? n : nTimeReceived;
}
int CWalletTx::GetRequestCount() const
{
// Returns -1 if it wasn't being tracked
int nRequests = -1;
{
LOCK(pwallet->cs_wallet);
if (IsCoinBase())
{
// Generated block
- if (hashBlock != 0)
+ if (!hashBlock.IsNull())
{
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
}
}
else
{
// Did anyone request this transaction?
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
if (mi != pwallet->mapRequestCount.end())
{
nRequests = (*mi).second;
// How about the block it's in?
- if (nRequests == 0 && hashBlock != 0)
+ if (nRequests == 0 && !hashBlock.IsNull())
{
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
else
nRequests = 1; // If it's in someone else's block it must have got out
}
}
}
}
return nRequests;
}
void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
list<COutputEntry>& listSent, CAmount& nFee, string& strSentAccount, const isminefilter& filter) const
{
nFee = 0;
listReceived.clear();
listSent.clear();
strSentAccount = strFromAccount;
// Compute fee:
CAmount nDebit = GetDebit(filter);
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
CAmount nValueOut = GetValueOut();
nFee = nDebit - nValueOut;
}
// Sent/received.
for (unsigned int i = 0; i < vout.size(); ++i)
{
const CTxOut& txout = vout[i];
isminetype fIsMine = pwallet->IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
// 2) the output is to us (received)
if (nDebit > 0)
{
// Don't report 'change' txouts
if (pwallet->IsChange(txout))
continue;
}
else if (!(fIsMine & filter))
continue;
// In either case, we need to get the destination address
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address))
{
LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString());
address = CNoDestination();
}
COutputEntry output = {address, txout.nValue, (int)i};
// If we are debited by the transaction, add the output as a "sent" entry
if (nDebit > 0)
listSent.push_back(output);
// If we are receiving the output, add it as a "received" entry
if (fIsMine & filter)
listReceived.push_back(output);
}
}
void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived,
CAmount& nSent, CAmount& nFee, const isminefilter& filter) const
{
nReceived = nSent = nFee = 0;
CAmount allFee;
string strSentAccount;
list<COutputEntry> listReceived;
list<COutputEntry> listSent;
GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (strAccount == strSentAccount)
{
BOOST_FOREACH(const COutputEntry& s, listSent)
nSent += s.amount;
nFee = allFee;
}
{
LOCK(pwallet->cs_wallet);
BOOST_FOREACH(const COutputEntry& r, listReceived)
{
if (pwallet->mapAddressBook.count(r.destination))
{
map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.destination);
if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount)
nReceived += r.amount;
}
else if (strAccount.empty())
{
nReceived += r.amount;
}
}
}
}
bool CWalletTx::WriteToDisk()
{
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
}
/**
* Scan the block chain (starting in pindexStart) for transactions
* from or to us. If fUpdate is true, found transactions that already
* exist in the wallet will be updated.
*/
int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
int ret = 0;
int64_t nNow = GetTime();
CBlockIndex* pindex = pindexStart;
{
LOCK2(cs_main, cs_wallet);
// no need to read and scan block, if block was created before
// our wallet birthday (as adjusted for block time variability)
while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200)))
pindex = chainActive.Next(pindex);
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
double dProgressStart = Checkpoints::GuessVerificationProgress(pindex, false);
double dProgressTip = Checkpoints::GuessVerificationProgress(chainActive.Tip(), false);
while (pindex)
{
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
CBlock block;
ReadBlockFromDisk(block, pindex);
BOOST_FOREACH(CTransaction& tx, block.vtx)
{
if (AddToWalletIfInvolvingMe(tx, &block, fUpdate))
ret++;
}
pindex = chainActive.Next(pindex);
if (GetTime() >= nNow + 60) {
nNow = GetTime();
LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(pindex));
}
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
}
return ret;
}
void CWallet::ReacceptWalletTransactions()
{
LOCK2(cs_main, cs_wallet);
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
const uint256& wtxid = item.first;
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
int nDepth = wtx.GetDepthInMainChain();
if (!wtx.IsCoinBase() && nDepth < 0)
{
// Try to add to memory pool
LOCK(mempool.cs);
wtx.AcceptToMemoryPool(false);
}
}
}
void CWalletTx::RelayWalletTransaction()
{
if (!IsCoinBase())
{
if (GetDepthInMainChain() == 0) {
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
RelayTransaction((CTransaction)*this);
}
}
}
set<uint256> CWalletTx::GetConflicts() const
{
set<uint256> result;
if (pwallet != NULL)
{
uint256 myHash = GetHash();
result = pwallet->GetConflicts(myHash);
result.erase(myHash);
}
return result;
}
void CWallet::ResendWalletTransactions()
{
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
if (GetTime() < nNextResend)
return;
bool fFirst = (nNextResend == 0);
nNextResend = GetTime() + GetRand(30 * 60);
if (fFirst)
return;
// Only do it if there's been a new block since last time
if (nTimeBestReceived < nLastResend)
return;
nLastResend = GetTime();
// Rebroadcast any of our txes that aren't in a block yet
LogPrintf("ResendWalletTransactions()\n");
{
LOCK(cs_wallet);
// Sort them in chronological order
multimap<unsigned int, CWalletTx*> mapSorted;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
// Don't rebroadcast until it's had plenty of time that
// it should have gotten in already by now.
if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60)
mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx));
}
BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
{
CWalletTx& wtx = *item.second;
wtx.RelayWalletTransaction();
}
}
}
/** @} */ // end of mapWallet
/** @defgroup Actions
*
* @{
*/
CAmount CWallet::GetBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableCredit();
}
}
return nTotal;
}
CAmount CWallet::GetUnconfirmedBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
nTotal += pcoin->GetAvailableCredit();
}
}
return nTotal;
}
CAmount CWallet::GetImmatureBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
nTotal += pcoin->GetImmatureCredit();
}
}
return nTotal;
}
CAmount CWallet::GetWatchOnlyBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsTrusted())
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}
return nTotal;
}
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}
return nTotal;
}
CAmount CWallet::GetImmatureWatchOnlyBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
nTotal += pcoin->GetImmatureWatchOnlyCredit();
}
}
return nTotal;
}
/**
* populate vCoins with vector of available COutputs.
*/
void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const
{
vCoins.clear();
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const uint256& wtxid = it->first;
const CWalletTx* pcoin = &(*it).second;
if (!IsFinalTx(*pcoin))
continue;
if (fOnlyConfirmed && !pcoin->IsTrusted())
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < 0)
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
isminetype mine = IsMine(pcoin->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
!IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 &&
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO));
}
}
}
}
static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
vector<char> vfIncluded;
vfBest.assign(vValue.size(), true);
nBest = nTotalLower;
seed_insecure_rand();
for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
{
vfIncluded.assign(vValue.size(), false);
CAmount nTotal = 0;
bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
{
for (unsigned int i = 0; i < vValue.size(); i++)
{
//The solver here uses a randomized algorithm,
//the randomness serves no real security purpose but is just
//needed to prevent degenerate behavior and it is important
//that the rng is fast. We do not use a constant random sequence,
//because there may be some privacy improvement by making
//the selection random.
if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i])
{
nTotal += vValue[i].first;
vfIncluded[i] = true;
if (nTotal >= nTargetValue)
{
fReachedTarget = true;
if (nTotal < nBest)
{
nBest = nTotal;
vfBest = vfIncluded;
}
nTotal -= vValue[i].first;
vfIncluded[i] = false;
}
}
}
}
}
}
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
{
setCoinsRet.clear();
nValueRet = 0;
// List of values less than target
pair<CAmount, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
coinLowestLarger.first = std::numeric_limits<CAmount>::max();
coinLowestLarger.second.first = NULL;
vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > > vValue;
CAmount nTotalLower = 0;
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
BOOST_FOREACH(const COutput &output, vCoins)
{
if (!output.fSpendable)
continue;
const CWalletTx *pcoin = output.tx;
if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
continue;
int i = output.i;
CAmount n = pcoin->vout[i].nValue;
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
if (n == nTargetValue)
{
setCoinsRet.insert(coin.second);
nValueRet += coin.first;
return true;
}
else if (n < nTargetValue + CENT)
{
vValue.push_back(coin);
nTotalLower += n;
}
else if (n < coinLowestLarger.first)
{
coinLowestLarger = coin;
}
}
if (nTotalLower == nTargetValue)
{
for (unsigned int i = 0; i < vValue.size(); ++i)
{
setCoinsRet.insert(vValue[i].second);
nValueRet += vValue[i].first;
}
return true;
}
if (nTotalLower < nTargetValue)
{
if (coinLowestLarger.second.first == NULL)
return false;
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
return true;
}
// Solve subset sum by stochastic approximation
sort(vValue.rbegin(), vValue.rend(), CompareValueOnly());
vector<char> vfBest;
CAmount nBest;
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000);
if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT)
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000);
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
// or the next bigger coin is closer), return the bigger coin
if (coinLowestLarger.second.first &&
((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest))
{
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
}
else {
for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i])
{
setCoinsRet.insert(vValue[i].second);
nValueRet += vValue[i].first;
}
LogPrint("selectcoins", "SelectCoins() best subset: ");
for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i])
LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first));
LogPrint("selectcoins", "total %s\n", FormatMoney(nBest));
}
return true;
}
bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
{
vector<COutput> vCoins;
AvailableCoins(vCoins, true, coinControl);
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
if (coinControl && coinControl->HasSelected())
{
BOOST_FOREACH(const COutput& out, vCoins)
{
if(!out.fSpendable)
continue;
nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
}
return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) ||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)));
}
bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl)
{
CAmount nValue = 0;
BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend)
{
if (nValue < 0)
{
strFailReason = _("Transaction amounts must be positive");
return false;
}
nValue += s.second;
}
if (vecSend.empty() || nValue < 0)
{
strFailReason = _("Transaction amounts must be positive");
return false;
}
wtxNew.fTimeReceivedIsTxTime = true;
wtxNew.BindWallet(this);
CMutableTransaction txNew;
// Discourage fee sniping.
//
// However because of a off-by-one-error in previous versions we need to
// neuter it by setting nLockTime to at least one less than nBestHeight.
// Secondly currently propagation of transactions created for block heights
// corresponding to blocks that were just mined may be iffy - transactions
// aren't re-accepted into the mempool - we additionally neuter the code by
// going ten blocks back. Doesn't yet do anything for sniping, but does act
// to shake out wallet bugs like not showing nLockTime'd transactions at
// all.
txNew.nLockTime = std::max(0, chainActive.Height() - 10);
// Secondly occasionally randomly pick a nLockTime even further back, so
// that transactions that are delayed after signing for whatever reason,
// e.g. high-latency mix networks and some CoinJoin implementations, have
// better privacy.
if (GetRandInt(10) == 0)
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));
assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
LOCK2(cs_main, cs_wallet);
{
nFeeRet = 0;
while (true)
{
txNew.vin.clear();
txNew.vout.clear();
wtxNew.fFromMe = true;
CAmount nTotalValue = nValue + nFeeRet;
double dPriority = 0;
// vouts to the payees
BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend)
{
CTxOut txout(s.second, s.first);
if (txout.IsDust(::minRelayTxFee))
{
strFailReason = _("Transaction amount too small");
return false;
}
txNew.vout.push_back(txout);
}
// Choose coins to use
set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl))
{
strFailReason = _("Insufficient funds");
return false;
}
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
//The priority after the next block (depth+1) is used instead of the current,
//reflecting an assumption the user would accept a bit more delay for
//a chance at a free transaction.
dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1);
}
CAmount nChange = nValueIn - nValue - nFeeRet;
if (nChange > 0)
{
// Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
CScript scriptChange;
// coin control: send change to custom address
if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))
scriptChange = GetScriptForDestination(coinControl->destChange);
// no coin control: send change to newly generated address
else
{
// Note: We use a new key here to keep it from being obvious which side is the change.
// The drawback is that by not reusing a previous key, the change may be lost if a
// backup is restored, if the backup doesn't have the new private key for the change.
// If we reused the old key, it would be possible to add code to look for and
// rediscover unknown transactions that were written with keys of ours to recover
// post-backup change.
// Reserve a new key pair from key pool
CPubKey vchPubKey;
bool ret;
ret = reservekey.GetReservedKey(vchPubKey);
assert(ret); // should never fail, as we just unlocked
scriptChange = GetScriptForDestination(vchPubKey.GetID());
}
CTxOut newTxOut(nChange, scriptChange);
// Never create dust outputs; if we would, just
// add the dust to the fee.
if (newTxOut.IsDust(::minRelayTxFee))
{
nFeeRet += nChange;
reservekey.ReturnKey();
}
else
{
// Insert change txn at random position:
vector<CTxOut>::iterator position = txNew.vout.begin()+GetRandInt(txNew.vout.size()+1);
txNew.vout.insert(position, newTxOut);
}
}
else
reservekey.ReturnKey();
// Fill vin
//
// Note how the sequence number is set to max()-1 so that the
// nLockTime set above actually works.
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
std::numeric_limits<unsigned int>::max()-1));
// Sign
int nIn = 0;
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
if (!SignSignature(*this, *coin.first, txNew, nIn++))
{
strFailReason = _("Signing transaction failed");
return false;
}
// Embed the constructed transaction data in wtxNew.
*static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
// Limit size
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_STANDARD_TX_SIZE)
{
strFailReason = _("Transaction too large");
return false;
}
dPriority = wtxNew.ComputePriority(dPriority, nBytes);
// Can we complete this as a free transaction?
if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
{
// Not enough fee: enough priority?
double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
// Not enough mempool history to estimate: use hard-coded AllowFree.
if (dPriorityNeeded <= 0 && AllowFree(dPriority))
break;
// Small enough, and priority high enough, to send for free
if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded)
break;
}
CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
// because we must be at the maximum allowed fee.
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
{
strFailReason = _("Transaction too large for fee policy");
return false;
}
if (nFeeRet >= nFeeNeeded)
break; // Done, enough fee included.
// Include more fee and try again.
nFeeRet = nFeeNeeded;
continue;
}
}
}
return true;
}
bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue,
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl)
{
vector< pair<CScript, CAmount> > vecSend;
vecSend.push_back(make_pair(scriptPubKey, nValue));
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl);
}
/**
* Call after CreateTransaction unless you want to abort
*/
bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
{
LOCK2(cs_main, cs_wallet);
LogPrintf("CommitTransaction:\n%s", wtxNew.ToString());
{
// This is only to keep the database open to defeat the auto-flush for the
// duration of this scope. This is the only place where this optimization
// maybe makes sense; please don't do it anywhere else.
CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL;
// Take key pair from key pool so it won't be used again
reservekey.KeepKey();
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
AddToWallet(wtxNew);
// Notify that old coins are spent
set<CWalletTx*> setCoins;
BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
coin.BindWallet(this);
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
}
if (fFileBacked)
delete pwalletdb;
}
// Track how many getdata requests our transaction gets
mapRequestCount[wtxNew.GetHash()] = 0;
// Broadcast
if (!wtxNew.AcceptToMemoryPool(false))
{
// This must not fail. The transaction has already been signed and recorded.
LogPrintf("CommitTransaction() : Error: Transaction not valid");
return false;
}
wtxNew.RelayWalletTransaction();
}
return true;
}
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
{
// payTxFee is user-set "I want to pay this much"
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
// user selected total at least (default=true)
if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK())
nFeeNeeded = payTxFee.GetFeePerK();
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0)
nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes);
// ... unless we don't have enough mempool data, in which case fall
// back to a hard-coded fee
if (nFeeNeeded == 0)
nFeeNeeded = minTxFee.GetFee(nTxBytes);
// prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee
if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes))
nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes);
// But always obey the maximum
if (nFeeNeeded > maxTxFee)
nFeeNeeded = maxTxFee;
return nFeeNeeded;
}
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
if (!fFileBacked)
return DB_LOAD_OK;
fFirstRunRet = false;
DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
if (nLoadWalletRet == DB_NEED_REWRITE)
{
if (CDB::Rewrite(strWalletFile, "\x04pool"))
{
LOCK(cs_wallet);
setKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// the requires a new key.
}
}
if (nLoadWalletRet != DB_LOAD_OK)
return nLoadWalletRet;
fFirstRunRet = !vchDefaultKey.IsValid();
uiInterface.LoadWallet(this);
return DB_LOAD_OK;
}
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
if (!fFileBacked)
return DB_LOAD_OK;
DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE)
{
if (CDB::Rewrite(strWalletFile, "\x04pool"))
{
LOCK(cs_wallet);
setKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
if (nZapWalletTxRet != DB_LOAD_OK)
return nZapWalletTxRet;
return DB_LOAD_OK;
}
bool CWallet::SetAddressBook(const CTxDestination& address, const string& strName, const string& strPurpose)
{
bool fUpdated = false;
{
LOCK(cs_wallet); // mapAddressBook
std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address);
fUpdated = mi != mapAddressBook.end();
mapAddressBook[address].name = strName;
if (!strPurpose.empty()) /* update purpose only if requested */
mapAddressBook[address].purpose = strPurpose;
}
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!fFileBacked)
return false;
if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
return false;
return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName);
}
bool CWallet::DelAddressBook(const CTxDestination& address)
{
{
LOCK(cs_wallet); // mapAddressBook
if(fFileBacked)
{
// Delete destdata tuples associated with address
std::string strAddress = CBitcoinAddress(address).ToString();
BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata)
{
CWalletDB(strWalletFile).EraseDestData(strAddress, item.first);
}
}
mapAddressBook.erase(address);
}
NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED);
if (!fFileBacked)
return false;
CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString());
return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
}
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
{
if (fFileBacked)
{
if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey))
return false;
}
vchDefaultKey = vchPubKey;
return true;
}
/**
* Mark old keypool keys as used,
* and generate all new keys
*/
bool CWallet::NewKeyPool()
{
{
LOCK(cs_wallet);
CWalletDB walletdb(strWalletFile);
BOOST_FOREACH(int64_t nIndex, setKeyPool)
walletdb.ErasePool(nIndex);
setKeyPool.clear();
if (IsLocked())
return false;
int64_t nKeys = max(GetArg("-keypool", 100), (int64_t)0);
for (int i = 0; i < nKeys; i++)
{
int64_t nIndex = i+1;
walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey()));
setKeyPool.insert(nIndex);
}
LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys);
}
return true;
}
bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
{
LOCK(cs_wallet);
if (IsLocked())
return false;
CWalletDB walletdb(strWalletFile);
// Top up key pool
unsigned int nTargetSize;
if (kpSize > 0)
nTargetSize = kpSize;
else
nTargetSize = max(GetArg("-keypool", 100), (int64_t) 0);
while (setKeyPool.size() < (nTargetSize + 1))
{
int64_t nEnd = 1;
if (!setKeyPool.empty())
nEnd = *(--setKeyPool.end()) + 1;
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey())))
throw runtime_error("TopUpKeyPool() : writing generated key failed");
setKeyPool.insert(nEnd);
LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size());
}
}
return true;
}
void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
{
nIndex = -1;
keypool.vchPubKey = CPubKey();
{
LOCK(cs_wallet);
if (!IsLocked())
TopUpKeyPool();
// Get the oldest key
if(setKeyPool.empty())
return;
CWalletDB walletdb(strWalletFile);
nIndex = *(setKeyPool.begin());
setKeyPool.erase(setKeyPool.begin());
if (!walletdb.ReadPool(nIndex, keypool))
throw runtime_error("ReserveKeyFromKeyPool() : read failed");
if (!HaveKey(keypool.vchPubKey.GetID()))
throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool");
assert(keypool.vchPubKey.IsValid());
LogPrintf("keypool reserve %d\n", nIndex);
}
}
void CWallet::KeepKey(int64_t nIndex)
{
// Remove from key pool
if (fFileBacked)
{
CWalletDB walletdb(strWalletFile);
walletdb.ErasePool(nIndex);
}
LogPrintf("keypool keep %d\n", nIndex);
}
void CWallet::ReturnKey(int64_t nIndex)
{
// Return to key pool
{
LOCK(cs_wallet);
setKeyPool.insert(nIndex);
}
LogPrintf("keypool return %d\n", nIndex);
}
bool CWallet::GetKeyFromPool(CPubKey& result)
{
int64_t nIndex = 0;
CKeyPool keypool;
{
LOCK(cs_wallet);
ReserveKeyFromKeyPool(nIndex, keypool);
if (nIndex == -1)
{
if (IsLocked()) return false;
result = GenerateNewKey();
return true;
}
KeepKey(nIndex);
result = keypool.vchPubKey;
}
return true;
}
int64_t CWallet::GetOldestKeyPoolTime()
{
int64_t nIndex = 0;
CKeyPool keypool;
ReserveKeyFromKeyPool(nIndex, keypool);
if (nIndex == -1)
return GetTime();
ReturnKey(nIndex);
return keypool.nTime;
}
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
{
map<CTxDestination, CAmount> balances;
{
LOCK(cs_wallet);
BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)
{
CWalletTx *pcoin = &walletEntry.second;
if (!IsFinalTx(*pcoin) || !pcoin->IsTrusted())
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
{
CTxDestination addr;
if (!IsMine(pcoin->vout[i]))
continue;
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
balances[addr] += n;
}
}
}
return balances;
}
set< set<CTxDestination> > CWallet::GetAddressGroupings()
{
AssertLockHeld(cs_wallet); // mapWallet
set< set<CTxDestination> > groupings;
set<CTxDestination> grouping;
BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)
{
CWalletTx *pcoin = &walletEntry.second;
if (pcoin->vin.size() > 0)
{
bool any_mine = false;
// group all input addresses with each other
BOOST_FOREACH(CTxIn txin, pcoin->vin)
{
CTxDestination address;
if(!IsMine(txin)) /* If this input isn't mine, ignore it */
continue;
if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address))
continue;
grouping.insert(address);
any_mine = true;
}
// group change with input addresses
if (any_mine)
{
BOOST_FOREACH(CTxOut txout, pcoin->vout)
if (IsChange(txout))
{
CTxDestination txoutAddr;
if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
continue;
grouping.insert(txoutAddr);
}
}
if (grouping.size() > 0)
{
groupings.insert(grouping);
grouping.clear();
}
}
// group lone addrs by themselves
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
if (IsMine(pcoin->vout[i]))
{
CTxDestination address;
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address))
continue;
grouping.insert(address);
groupings.insert(grouping);
grouping.clear();
}
}
set< set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
map< CTxDestination, set<CTxDestination>* > setmap; // map addresses to the unique group containing it
BOOST_FOREACH(set<CTxDestination> grouping, groupings)
{
// make a set of all the groups hit by this new group
set< set<CTxDestination>* > hits;
map< CTxDestination, set<CTxDestination>* >::iterator it;
BOOST_FOREACH(CTxDestination address, grouping)
if ((it = setmap.find(address)) != setmap.end())
hits.insert((*it).second);
// merge all hit groups into a new single group and delete old groups
set<CTxDestination>* merged = new set<CTxDestination>(grouping);
BOOST_FOREACH(set<CTxDestination>* hit, hits)
{
merged->insert(hit->begin(), hit->end());
uniqueGroupings.erase(hit);
delete hit;
}
uniqueGroupings.insert(merged);
// update setmap
BOOST_FOREACH(CTxDestination element, *merged)
setmap[element] = merged;
}
set< set<CTxDestination> > ret;
BOOST_FOREACH(set<CTxDestination>* uniqueGrouping, uniqueGroupings)
{
ret.insert(*uniqueGrouping);
delete uniqueGrouping;
}
return ret;
}
set<CTxDestination> CWallet::GetAccountAddresses(string strAccount) const
{
LOCK(cs_wallet);
set<CTxDestination> result;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, mapAddressBook)
{
const CTxDestination& address = item.first;
const string& strName = item.second.name;
if (strName == strAccount)
result.insert(address);
}
return result;
}
bool CReserveKey::GetReservedKey(CPubKey& pubkey)
{
if (nIndex == -1)
{
CKeyPool keypool;
pwallet->ReserveKeyFromKeyPool(nIndex, keypool);
if (nIndex != -1)
vchPubKey = keypool.vchPubKey;
else {
return false;
}
}
assert(vchPubKey.IsValid());
pubkey = vchPubKey;
return true;
}
void CReserveKey::KeepKey()
{
if (nIndex != -1)
pwallet->KeepKey(nIndex);
nIndex = -1;
vchPubKey = CPubKey();
}
void CReserveKey::ReturnKey()
{
if (nIndex != -1)
pwallet->ReturnKey(nIndex);
nIndex = -1;
vchPubKey = CPubKey();
}
void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress) const
{
setAddress.clear();
CWalletDB walletdb(strWalletFile);
LOCK2(cs_main, cs_wallet);
BOOST_FOREACH(const int64_t& id, setKeyPool)
{
CKeyPool keypool;
if (!walletdb.ReadPool(id, keypool))
throw runtime_error("GetAllReserveKeyHashes() : read failed");
assert(keypool.vchPubKey.IsValid());
CKeyID keyID = keypool.vchPubKey.GetID();
if (!HaveKey(keyID))
throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool");
setAddress.insert(keyID);
}
}
void CWallet::UpdatedTransaction(const uint256 &hashTx)
{
{
LOCK(cs_wallet);
// Only notify UI if this transaction is in this wallet
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
if (mi != mapWallet.end())
NotifyTransactionChanged(this, hashTx, CT_UPDATED);
}
}
void CWallet::LockCoin(COutPoint& output)
{
AssertLockHeld(cs_wallet); // setLockedCoins
setLockedCoins.insert(output);
}
void CWallet::UnlockCoin(COutPoint& output)
{
AssertLockHeld(cs_wallet); // setLockedCoins
setLockedCoins.erase(output);
}
void CWallet::UnlockAllCoins()
{
AssertLockHeld(cs_wallet); // setLockedCoins
setLockedCoins.clear();
}
bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
{
AssertLockHeld(cs_wallet); // setLockedCoins
COutPoint outpt(hash, n);
return (setLockedCoins.count(outpt) > 0);
}
void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts)
{
AssertLockHeld(cs_wallet); // setLockedCoins
for (std::set<COutPoint>::iterator it = setLockedCoins.begin();
it != setLockedCoins.end(); it++) {
COutPoint outpt = (*it);
vOutpts.push_back(outpt);
}
}
/** @} */ // end of Actions
class CAffectedKeysVisitor : public boost::static_visitor<void> {
private:
const CKeyStore &keystore;
std::vector<CKeyID> &vKeys;
public:
CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
void Process(const CScript &script) {
txnouttype type;
std::vector<CTxDestination> vDest;
int nRequired;
if (ExtractDestinations(script, type, vDest, nRequired)) {
BOOST_FOREACH(const CTxDestination &dest, vDest)
boost::apply_visitor(*this, dest);
}
}
void operator()(const CKeyID &keyId) {
if (keystore.HaveKey(keyId))
vKeys.push_back(keyId);
}
void operator()(const CScriptID &scriptId) {
CScript script;
if (keystore.GetCScript(scriptId, script))
Process(script);
}
void operator()(const CNoDestination &none) {}
};
void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
AssertLockHeld(cs_wallet); // mapKeyMetadata
mapKeyBirth.clear();
// get birth times for keys with metadata
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++)
if (it->second.nCreateTime)
mapKeyBirth[it->first] = it->second.nCreateTime;
// map in which we'll infer heights of other keys
CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin
std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock;
std::set<CKeyID> setKeys;
GetKeys(setKeys);
BOOST_FOREACH(const CKeyID &keyid, setKeys) {
if (mapKeyBirth.count(keyid) == 0)
mapKeyFirstBlock[keyid] = pindexMax;
}
setKeys.clear();
// if there are no such keys, we're done
if (mapKeyFirstBlock.empty())
return;
// find first block that affects those keys, if there are any left
std::vector<CKeyID> vAffected;
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) {
// iterate over all wallet transactions...
const CWalletTx &wtx = (*it).second;
BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);
if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) {
// ... which are already in a block
int nHeight = blit->second->nHeight;
BOOST_FOREACH(const CTxOut &txout, wtx.vout) {
// iterate over all their outputs
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
BOOST_FOREACH(const CKeyID &keyid, vAffected) {
// ... and all their affected keys
std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid);
if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
rit->second = blit->second;
}
vAffected.clear();
}
}
}
// Extract block timestamps for those keys
for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off
}
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
{
if (boost::get<CNoDestination>(&dest))
return false;
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
}
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
{
if (!mapAddressBook[dest].destdata.erase(key))
return false;
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key);
}
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
{
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
return true;
}
bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const
{
std::map<CTxDestination, CAddressBookData>::const_iterator i = mapAddressBook.find(dest);
if(i != mapAddressBook.end())
{
CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key);
if(j != i->second.destdata.end())
{
if(value)
*value = j->second;
return true;
}
}
return false;
}
CKeyPool::CKeyPool()
{
nTime = GetTime();
}
CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn)
{
nTime = GetTime();
vchPubKey = vchPubKeyIn;
}
CWalletKey::CWalletKey(int64_t nExpires)
{
nTimeCreated = (nExpires ? GetTime() : 0);
nTimeExpires = nExpires;
}
int CMerkleTx::SetMerkleBranch(const CBlock& block)
{
AssertLockHeld(cs_main);
CBlock blockTmp;
// Update the tx's hashBlock
hashBlock = block.GetHash();
// Locate the transaction
for (nIndex = 0; nIndex < (int)block.vtx.size(); nIndex++)
if (block.vtx[nIndex] == *(CTransaction*)this)
break;
if (nIndex == (int)block.vtx.size())
{
vMerkleBranch.clear();
nIndex = -1;
LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n");
return 0;
}
// Fill in merkle branch
vMerkleBranch = block.GetMerkleBranch(nIndex);
// Is the tx in a block that's in the main chain
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
const CBlockIndex* pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex))
return 0;
return chainActive.Height() - pindex->nHeight + 1;
}
int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const
{
- if (hashBlock == 0 || nIndex == -1)
+ if (hashBlock.IsNull() || nIndex == -1)
return 0;
AssertLockHeld(cs_main);
// Find the block it claims to be in
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex))
return 0;
// Make sure the merkle branch connects to this block
if (!fMerkleVerified)
{
if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot)
return 0;
fMerkleVerified = true;
}
pindexRet = pindex;
return chainActive.Height() - pindex->nHeight + 1;
}
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
{
AssertLockHeld(cs_main);
int nResult = GetDepthInMainChainINTERNAL(pindexRet);
if (nResult == 0 && !mempool.exists(GetHash()))
return -1; // Not in chain, not in mempool
return nResult;
}
int CMerkleTx::GetBlocksToMaturity() const
{
if (!IsCoinBase())
return 0;
return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
}
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee)
{
CValidationState state;
return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee);
}
diff --git a/src/wallet.h b/src/wallet.h
index 4d95d0ae2..1d0dc97c6 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -1,1081 +1,1081 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_H
#define BITCOIN_WALLET_H
#include "amount.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "crypter.h"
#include "key.h"
#include "keystore.h"
#include "main.h"
#include "ui_interface.h"
#include "wallet_ismine.h"
#include "walletdb.h"
#include <algorithm>
#include <map>
#include <set>
#include <stdexcept>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
/**
* Settings
*/
extern CFeeRate payTxFee;
extern CAmount maxTxFee;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
extern bool fPayAtLeastCustomFee;
//! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB
static const CAmount nHighTransactionFeeWarning = 0.01 * COIN;
//! -maxtxfee default
static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN;
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
static const CAmount nHighTransactionMaxFeeWarning = 100 * nHighTransactionFeeWarning;
//! Largest (in bytes) free transaction we're willing to create
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
class CAccountingEntry;
class CCoinControl;
class COutput;
class CReserveKey;
class CScript;
class CWalletTx;
/** (client) version numbers for particular wallet features */
enum WalletFeature
{
FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output)
FEATURE_WALLETCRYPT = 40000, // wallet encryption
FEATURE_COMPRPUBKEY = 60000, // compressed public keys
FEATURE_LATEST = 60000
};
/** A key pool entry */
class CKeyPool
{
public:
int64_t nTime;
CPubKey vchPubKey;
CKeyPool();
CKeyPool(const CPubKey& vchPubKeyIn);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(nTime);
READWRITE(vchPubKey);
}
};
/** Address book data */
class CAddressBookData
{
public:
std::string name;
std::string purpose;
CAddressBookData()
{
purpose = "unknown";
}
typedef std::map<std::string, std::string> StringMap;
StringMap destdata;
};
/**
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
*/
class CWallet : public CCryptoKeyStore, public CValidationInterface
{
private:
bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
CWalletDB *pwalletdbEncryption;
//! the current wallet version: clients below this version are not able to load the wallet
int nWalletVersion;
//! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
int nWalletMaxVersion;
int64_t nNextResend;
int64_t nLastResend;
/**
* Used to keep track of spent outpoints, and
* detect and report conflicts (double-spends or
* mutated transactions where the mutant gets mined).
*/
typedef std::multimap<COutPoint, uint256> TxSpends;
TxSpends mapTxSpends;
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
void AddToSpends(const uint256& wtxid);
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
public:
/*
* Main wallet lock.
* This lock protects all the fields added by CWallet
* except for:
* fFileBacked (immutable after instantiation)
* strWalletFile (immutable after instantiation)
*/
mutable CCriticalSection cs_wallet;
bool fFileBacked;
std::string strWalletFile;
std::set<int64_t> setKeyPool;
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
CWallet()
{
SetNull();
}
CWallet(std::string strWalletFileIn)
{
SetNull();
strWalletFile = strWalletFileIn;
fFileBacked = true;
}
~CWallet()
{
delete pwalletdbEncryption;
pwalletdbEncryption = NULL;
}
void SetNull()
{
nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
fFileBacked = false;
nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL;
nOrderPosNext = 0;
nNextResend = 0;
nLastResend = 0;
nTimeFirstKey = 0;
}
std::map<uint256, CWalletTx> mapWallet;
int64_t nOrderPosNext;
std::map<uint256, int> mapRequestCount;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
CPubKey vchDefaultKey;
std::set<COutPoint> setLockedCoins;
int64_t nTimeFirstKey;
const CWalletTx* GetWalletTx(const uint256& hash) const;
//! check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL) const;
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
bool IsLockedCoin(uint256 hash, unsigned int n) const;
void LockCoin(COutPoint& output);
void UnlockCoin(COutPoint& output);
void UnlockAllCoins();
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
/**
* keystore implementation
* Generate a new key
*/
CPubKey GenerateNewKey();
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
bool LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &metadata);
bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddCScript(const CScript& redeemScript);
bool LoadCScript(const CScript& redeemScript);
//! Adds a destination data tuple to the store, and saves it to disk
bool AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
//! Erases a destination data tuple in the store and on disk
bool EraseDestData(const CTxDestination &dest, const std::string &key);
//! Adds a destination data tuple to the store, without saving it to disk
bool LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
//! Look up a destination data tuple in the store, return true if found false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript &dest);
bool RemoveWatchOnly(const CScript &dest);
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const;
/**
* Increment the next transaction order id
* @return next transaction order id
*/
int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL);
typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
typedef std::multimap<int64_t, TxPair > TxItems;
/**
* Get the wallet's activity log
* @return multimap of ordered transactions and accounting entries
* @warning Returned pointers are *only* valid within the scope of passed acentries
*/
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet=false);
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
void EraseFromWallet(const uint256 &hash);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions();
CAmount GetBalance() const;
CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const;
CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
bool CreateTransaction(const std::vector<std::pair<CScript, CAmount> >& vecSend,
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL);
bool CreateTransaction(CScript scriptPubKey, const CAmount& nValue,
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
static CFeeRate minTxFee;
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
bool NewKeyPool();
bool TopUpKeyPool(unsigned int kpSize = 0);
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool);
void KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex);
bool GetKeyFromPool(CPubKey &key);
int64_t GetOldestKeyPoolTime();
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, CAmount> GetAddressBalances();
std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const;
isminetype IsMine(const CTxIn& txin) const;
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const
{
return ::IsMine(*this, txout.scriptPubKey);
}
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error("CWallet::GetCredit() : value out of range");
return ((IsMine(txout) & filter) ? txout.nValue : 0);
}
bool IsChange(const CTxOut& txout) const;
CAmount GetChange(const CTxOut& txout) const
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error("CWallet::GetChange() : value out of range");
return (IsChange(txout) ? txout.nValue : 0);
}
bool IsMine(const CTransaction& tx) const
{
BOOST_FOREACH(const CTxOut& txout, tx.vout)
if (IsMine(txout))
return true;
return false;
}
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const
{
return (GetDebit(tx, ISMINE_ALL) > 0);
}
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const
{
CAmount nDebit = 0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
nDebit += GetDebit(txin, filter);
if (!MoneyRange(nDebit))
throw std::runtime_error("CWallet::GetDebit() : value out of range");
}
return nDebit;
}
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const
{
CAmount nCredit = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nCredit += GetCredit(txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWallet::GetCredit() : value out of range");
}
return nCredit;
}
CAmount GetChange(const CTransaction& tx) const
{
CAmount nChange = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nChange += GetChange(txout);
if (!MoneyRange(nChange))
throw std::runtime_error("CWallet::GetChange() : value out of range");
}
return nChange;
}
void SetBestChain(const CBlockLocator& loc);
DBErrors LoadWallet(bool& fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);
bool DelAddressBook(const CTxDestination& address);
void UpdatedTransaction(const uint256 &hashTx);
void Inventory(const uint256 &hash)
{
{
LOCK(cs_wallet);
std::map<uint256, int>::iterator mi = mapRequestCount.find(hash);
if (mi != mapRequestCount.end())
(*mi).second++;
}
}
unsigned int GetKeyPoolSize()
{
AssertLockHeld(cs_wallet); // setKeyPool
return setKeyPool.size();
}
bool SetDefaultKey(const CPubKey &vchPubKey);
//! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower
bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false);
//! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format)
bool SetMaxVersion(int nVersion);
//! get the current wallet format (the oldest client version guaranteed to understand this wallet)
int GetVersion() { LOCK(cs_wallet); return nWalletVersion; }
//! Get wallet transactions that conflict with given transaction (spend same outputs)
std::set<uint256> GetConflicts(const uint256& txid) const;
/**
* Address book entry changed.
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void (CWallet *wallet, const CTxDestination
&address, const std::string &label, bool isMine,
const std::string &purpose,
ChangeType status)> NotifyAddressBookChanged;
/**
* Wallet transaction added, removed or updated.
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx,
ChangeType status)> NotifyTransactionChanged;
/** Show progress e.g. for rescan */
boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress;
/** Watch-only address added */
boost::signals2::signal<void (bool fHaveWatchOnly)> NotifyWatchonlyChanged;
};
/** A key allocated from the key pool. */
class CReserveKey
{
protected:
CWallet* pwallet;
int64_t nIndex;
CPubKey vchPubKey;
public:
CReserveKey(CWallet* pwalletIn)
{
nIndex = -1;
pwallet = pwalletIn;
}
~CReserveKey()
{
ReturnKey();
}
void ReturnKey();
bool GetReservedKey(CPubKey &pubkey);
void KeepKey();
};
typedef std::map<std::string, std::string> mapValue_t;
static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
{
if (!mapValue.count("n"))
{
nOrderPos = -1; // TODO: calculate elsewhere
return;
}
nOrderPos = atoi64(mapValue["n"].c_str());
}
static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
{
if (nOrderPos == -1)
return;
mapValue["n"] = i64tostr(nOrderPos);
}
struct COutputEntry
{
CTxDestination destination;
CAmount amount;
int vout;
};
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx : public CTransaction
{
private:
int GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const;
public:
uint256 hashBlock;
std::vector<uint256> vMerkleBranch;
int nIndex;
// memory only
mutable bool fMerkleVerified;
CMerkleTx()
{
Init();
}
CMerkleTx(const CTransaction& txIn) : CTransaction(txIn)
{
Init();
}
void Init()
{
- hashBlock = 0;
+ hashBlock = uint256();
nIndex = -1;
fMerkleVerified = false;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*(CTransaction*)this);
nVersion = this->nVersion;
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
}
int SetMerkleBranch(const CBlock& block);
/**
* Return depth of transaction in blockchain:
* -1 : not in blockchain, and not in memory pool (conflicted transaction)
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; }
int GetBlocksToMaturity() const;
bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectInsaneFee=true);
};
/**
* A transaction with a bunch of additional info that only the owner cares about.
* It includes any unrecorded transactions needed to link it back to the block chain.
*/
class CWalletTx : public CMerkleTx
{
private:
const CWallet* pwallet;
public:
mapValue_t mapValue;
std::vector<std::pair<std::string, std::string> > vOrderForm;
unsigned int fTimeReceivedIsTxTime;
unsigned int nTimeReceived; //! time received by this node
unsigned int nTimeSmart;
char fFromMe;
std::string strFromAccount;
int64_t nOrderPos; //! position in ordered transaction list
// memory only
mutable bool fDebitCached;
mutable bool fCreditCached;
mutable bool fImmatureCreditCached;
mutable bool fAvailableCreditCached;
mutable bool fWatchDebitCached;
mutable bool fWatchCreditCached;
mutable bool fImmatureWatchCreditCached;
mutable bool fAvailableWatchCreditCached;
mutable bool fChangeCached;
mutable CAmount nDebitCached;
mutable CAmount nCreditCached;
mutable CAmount nImmatureCreditCached;
mutable CAmount nAvailableCreditCached;
mutable CAmount nWatchDebitCached;
mutable CAmount nWatchCreditCached;
mutable CAmount nImmatureWatchCreditCached;
mutable CAmount nAvailableWatchCreditCached;
mutable CAmount nChangeCached;
CWalletTx()
{
Init(NULL);
}
CWalletTx(const CWallet* pwalletIn)
{
Init(pwalletIn);
}
CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn)
{
Init(pwalletIn);
}
CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn)
{
Init(pwalletIn);
}
void Init(const CWallet* pwalletIn)
{
pwallet = pwalletIn;
mapValue.clear();
vOrderForm.clear();
fTimeReceivedIsTxTime = false;
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
strFromAccount.clear();
fDebitCached = false;
fCreditCached = false;
fImmatureCreditCached = false;
fAvailableCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fChangeCached = false;
nDebitCached = 0;
nCreditCached = 0;
nImmatureCreditCached = 0;
nAvailableCreditCached = 0;
nWatchDebitCached = 0;
nWatchCreditCached = 0;
nAvailableWatchCreditCached = 0;
nImmatureWatchCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (ser_action.ForRead())
Init(NULL);
char fSpent = false;
if (!ser_action.ForRead())
{
mapValue["fromaccount"] = strFromAccount;
WriteOrderPos(nOrderPos, mapValue);
if (nTimeSmart)
mapValue["timesmart"] = strprintf("%u", nTimeSmart);
}
READWRITE(*(CMerkleTx*)this);
std::vector<CMerkleTx> vUnused; //! Used to be vtxPrev
READWRITE(vUnused);
READWRITE(mapValue);
READWRITE(vOrderForm);
READWRITE(fTimeReceivedIsTxTime);
READWRITE(nTimeReceived);
READWRITE(fFromMe);
READWRITE(fSpent);
if (ser_action.ForRead())
{
strFromAccount = mapValue["fromaccount"];
ReadOrderPos(nOrderPos, mapValue);
nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
}
mapValue.erase("fromaccount");
mapValue.erase("version");
mapValue.erase("spent");
mapValue.erase("n");
mapValue.erase("timesmart");
}
//! make sure balances are recalculated
void MarkDirty()
{
fCreditCached = false;
fAvailableCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fDebitCached = false;
fChangeCached = false;
}
void BindWallet(CWallet *pwalletIn)
{
pwallet = pwalletIn;
MarkDirty();
}
//! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const
{
if (vin.empty())
return 0;
CAmount debit = 0;
if(filter & ISMINE_SPENDABLE)
{
if (fDebitCached)
debit += nDebitCached;
else
{
nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
}
if(filter & ISMINE_WATCH_ONLY)
{
if(fWatchDebitCached)
debit += nWatchDebitCached;
else
{
nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);
fWatchDebitCached = true;
debit += nWatchDebitCached;
}
}
return debit;
}
CAmount GetCredit(const isminefilter& filter) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
int64_t credit = 0;
if (filter & ISMINE_SPENDABLE)
{
// GetBalance can assume transactions in mapWallet won't change
if (fCreditCached)
credit += nCreditCached;
else
{
nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
fCreditCached = true;
credit += nCreditCached;
}
}
if (filter & ISMINE_WATCH_ONLY)
{
if (fWatchCreditCached)
credit += nWatchCreditCached;
else
{
nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
fWatchCreditCached = true;
credit += nWatchCreditCached;
}
}
return credit;
}
CAmount GetImmatureCredit(bool fUseCache=true) const
{
if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
{
if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached;
nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
}
return 0;
}
CAmount GetAvailableCredit(bool fUseCache=true) const
{
if (pwallet == 0)
return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
if (fUseCache && fAvailableCreditCached)
return nAvailableCreditCached;
CAmount nCredit = 0;
uint256 hashTx = GetHash();
for (unsigned int i = 0; i < vout.size(); i++)
{
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
}
}
nAvailableCreditCached = nCredit;
fAvailableCreditCached = true;
return nCredit;
}
CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const
{
if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
{
if (fUseCache && fImmatureWatchCreditCached)
return nImmatureWatchCreditCached;
nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
fImmatureWatchCreditCached = true;
return nImmatureWatchCreditCached;
}
return 0;
}
CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const
{
if (pwallet == 0)
return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
if (fUseCache && fAvailableWatchCreditCached)
return nAvailableWatchCreditCached;
CAmount nCredit = 0;
for (unsigned int i = 0; i < vout.size(); i++)
{
if (!pwallet->IsSpent(GetHash(), i))
{
const CTxOut &txout = vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
}
}
nAvailableWatchCreditCached = nCredit;
fAvailableWatchCreditCached = true;
return nCredit;
}
CAmount GetChange() const
{
if (fChangeCached)
return nChangeCached;
nChangeCached = pwallet->GetChange(*this);
fChangeCached = true;
return nChangeCached;
}
void GetAmounts(std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const;
void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived,
CAmount& nSent, CAmount& nFee, const isminefilter& filter) const;
bool IsFromMe(const isminefilter& filter) const
{
return (GetDebit(filter) > 0);
}
bool IsTrusted() const
{
// Quick answer in most cases
if (!IsFinalTx(*this))
return false;
int nDepth = GetDepthInMainChain();
if (nDepth >= 1)
return true;
if (nDepth < 0)
return false;
if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit
return false;
// Trusted if all inputs are from us and are in the mempool:
BOOST_FOREACH(const CTxIn& txin, vin)
{
// Transactions not sent by us: not trusted
const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
if (parent == NULL)
return false;
const CTxOut& parentOut = parent->vout[txin.prevout.n];
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)
return false;
}
return true;
}
bool WriteToDisk();
int64_t GetTxTime() const;
int GetRequestCount() const;
void RelayWalletTransaction();
std::set<uint256> GetConflicts() const;
};
class COutput
{
public:
const CWalletTx *tx;
int i;
int nDepth;
bool fSpendable;
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn)
{
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn;
}
std::string ToString() const;
};
/** Private key that includes an expiration date in case it never gets used. */
class CWalletKey
{
public:
CPrivKey vchPrivKey;
int64_t nTimeCreated;
int64_t nTimeExpires;
std::string strComment;
//! todo: add something to note what created it (user, getnewaddress, change)
//! maybe should have a map<string, string> property map
CWalletKey(int64_t nExpires=0);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vchPrivKey);
READWRITE(nTimeCreated);
READWRITE(nTimeExpires);
READWRITE(LIMITED_STRING(strComment, 65536));
}
};
/**
* Account information.
* Stored in wallet with key "acc"+string account name.
*/
class CAccount
{
public:
CPubKey vchPubKey;
CAccount()
{
SetNull();
}
void SetNull()
{
vchPubKey = CPubKey();
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(vchPubKey);
}
};
/**
* Internal transfers.
* Database key is acentry<account><counter>.
*/
class CAccountingEntry
{
public:
std::string strAccount;
CAmount nCreditDebit;
int64_t nTime;
std::string strOtherAccount;
std::string strComment;
mapValue_t mapValue;
int64_t nOrderPos; //! position in ordered transaction list
uint64_t nEntryNo;
CAccountingEntry()
{
SetNull();
}
void SetNull()
{
nCreditDebit = 0;
nTime = 0;
strAccount.clear();
strOtherAccount.clear();
strComment.clear();
nOrderPos = -1;
nEntryNo = 0;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
//! Note: strAccount is serialized as part of the key, not here.
READWRITE(nCreditDebit);
READWRITE(nTime);
READWRITE(LIMITED_STRING(strOtherAccount, 65536));
if (!ser_action.ForRead())
{
WriteOrderPos(nOrderPos, mapValue);
if (!(mapValue.empty() && _ssExtra.empty()))
{
CDataStream ss(nType, nVersion);
ss.insert(ss.begin(), '\0');
ss << mapValue;
ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end());
strComment.append(ss.str());
}
}
READWRITE(LIMITED_STRING(strComment, 65536));
size_t nSepPos = strComment.find("\0", 0, 1);
if (ser_action.ForRead())
{
mapValue.clear();
if (std::string::npos != nSepPos)
{
CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion);
ss >> mapValue;
_ssExtra = std::vector<char>(ss.begin(), ss.end());
}
ReadOrderPos(nOrderPos, mapValue);
}
if (std::string::npos != nSepPos)
strComment.erase(nSepPos);
mapValue.erase("n");
}
private:
std::vector<char> _ssExtra;
};
#endif // BITCOIN_WALLET_H
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 4fd7eb512..0ec83e7b3 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -1,987 +1,987 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "walletdb.h"
#include "base58.h"
#include "protocol.h"
#include "serialize.h"
#include "sync.h"
#include "util.h"
#include "utiltime.h"
#include "wallet.h"
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/thread.hpp>
using namespace boost;
using namespace std;
static uint64_t nAccountingEntryNumber = 0;
//
// CWalletDB
//
bool CWalletDB::WriteName(const string& strAddress, const string& strName)
{
nWalletDBUpdated++;
return Write(make_pair(string("name"), strAddress), strName);
}
bool CWalletDB::EraseName(const string& strAddress)
{
// This should only be used for sending addresses, never for receiving addresses,
// receiving addresses must always have an address book entry if they're not change return.
nWalletDBUpdated++;
return Erase(make_pair(string("name"), strAddress));
}
bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose)
{
nWalletDBUpdated++;
return Write(make_pair(string("purpose"), strAddress), strPurpose);
}
bool CWalletDB::ErasePurpose(const string& strPurpose)
{
nWalletDBUpdated++;
return Erase(make_pair(string("purpose"), strPurpose));
}
bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("tx"), hash), wtx);
}
bool CWalletDB::EraseTx(uint256 hash)
{
nWalletDBUpdated++;
return Erase(std::make_pair(std::string("tx"), hash));
}
bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
{
nWalletDBUpdated++;
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta, false))
return false;
// hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> vchKey;
vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
}
bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
const std::vector<unsigned char>& vchCryptedSecret,
const CKeyMetadata &keyMeta)
{
const bool fEraseUnencryptedKey = true;
nWalletDBUpdated++;
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta))
return false;
if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
return false;
if (fEraseUnencryptedKey)
{
Erase(std::make_pair(std::string("key"), vchPubKey));
Erase(std::make_pair(std::string("wkey"), vchPubKey));
}
return true;
}
bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
}
bool CWalletDB::WriteWatchOnly(const CScript &dest)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("watchs"), dest), '1');
}
bool CWalletDB::EraseWatchOnly(const CScript &dest)
{
nWalletDBUpdated++;
return Erase(std::make_pair(std::string("watchs"), dest));
}
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
return Write(std::string("bestblock"), locator);
}
bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
{
return Read(std::string("bestblock"), locator);
}
bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
{
nWalletDBUpdated++;
return Write(std::string("orderposnext"), nOrderPosNext);
}
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
{
nWalletDBUpdated++;
return Write(std::string("defaultkey"), vchPubKey);
}
bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
{
return Read(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::ErasePool(int64_t nPool)
{
nWalletDBUpdated++;
return Erase(std::make_pair(std::string("pool"), nPool));
}
bool CWalletDB::WriteMinVersion(int nVersion)
{
return Write(std::string("minversion"), nVersion);
}
bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
{
account.SetNull();
return Read(make_pair(string("acc"), strAccount), account);
}
bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
{
return Write(make_pair(string("acc"), strAccount), account);
}
bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
{
return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
}
bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
{
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
}
CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount)
{
list<CAccountingEntry> entries;
ListAccountCreditDebit(strAccount, entries);
CAmount nCreditDebit = 0;
BOOST_FOREACH (const CAccountingEntry& entry, entries)
nCreditDebit += entry.nCreditDebit;
return nCreditDebit;
}
void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
{
bool fAllAccounts = (strAccount == "*");
Dbc* pcursor = GetCursor();
if (!pcursor)
throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
unsigned int fFlags = DB_SET_RANGE;
while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
if (fFlags == DB_SET_RANGE)
ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0)));
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
fFlags = DB_NEXT;
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
{
pcursor->close();
throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
}
// Unserialize
string strType;
ssKey >> strType;
if (strType != "acentry")
break;
CAccountingEntry acentry;
ssKey >> acentry.strAccount;
if (!fAllAccounts && acentry.strAccount != strAccount)
break;
ssValue >> acentry;
ssKey >> acentry.nEntryNo;
entries.push_back(acentry);
}
pcursor->close();
}
DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet)
{
LOCK(pwallet->cs_wallet);
// Old wallets didn't have any defined order for transactions
// Probably a bad idea to change the output of this
// First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
typedef multimap<int64_t, TxPair > TxItems;
TxItems txByTime;
for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it)
{
CWalletTx* wtx = &((*it).second);
txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
}
list<CAccountingEntry> acentries;
ListAccountCreditDebit("", acentries);
BOOST_FOREACH(CAccountingEntry& entry, acentries)
{
txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
}
int64_t& nOrderPosNext = pwallet->nOrderPosNext;
nOrderPosNext = 0;
std::vector<int64_t> nOrderPosOffsets;
for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
{
CWalletTx *const pwtx = (*it).second.first;
CAccountingEntry *const pacentry = (*it).second.second;
int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
if (nOrderPos == -1)
{
nOrderPos = nOrderPosNext++;
nOrderPosOffsets.push_back(nOrderPos);
if (pwtx)
{
if (!WriteTx(pwtx->GetHash(), *pwtx))
return DB_LOAD_FAIL;
}
else
if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
return DB_LOAD_FAIL;
}
else
{
int64_t nOrderPosOff = 0;
BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets)
{
if (nOrderPos >= nOffsetStart)
++nOrderPosOff;
}
nOrderPos += nOrderPosOff;
nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
if (!nOrderPosOff)
continue;
// Since we're changing the order, write it back
if (pwtx)
{
if (!WriteTx(pwtx->GetHash(), *pwtx))
return DB_LOAD_FAIL;
}
else
if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
return DB_LOAD_FAIL;
}
}
WriteOrderPosNext(nOrderPosNext);
return DB_LOAD_OK;
}
class CWalletScanState {
public:
unsigned int nKeys;
unsigned int nCKeys;
unsigned int nKeyMeta;
bool fIsEncrypted;
bool fAnyUnordered;
int nFileVersion;
vector<uint256> vWalletUpgrade;
CWalletScanState() {
nKeys = nCKeys = nKeyMeta = 0;
fIsEncrypted = false;
fAnyUnordered = false;
nFileVersion = 0;
}
};
bool
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CWalletScanState &wss, string& strType, string& strErr)
{
try {
// Unserialize
// Taking advantage of the fact that pair serialization
// is just the two items serialized one after the other
ssKey >> strType;
if (strType == "name")
{
string strAddress;
ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name;
}
else if (strType == "purpose")
{
string strAddress;
ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose;
}
else if (strType == "tx")
{
uint256 hash;
ssKey >> hash;
CWalletTx wtx;
ssValue >> wtx;
CValidationState state;
if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()))
return false;
// Undo serialize changes in 31600
if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
{
if (!ssValue.empty())
{
char fTmp;
char fUnused;
ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString());
wtx.fTimeReceivedIsTxTime = fTmp;
}
else
{
strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
wtx.fTimeReceivedIsTxTime = 0;
}
wss.vWalletUpgrade.push_back(hash);
}
if (wtx.nOrderPos == -1)
wss.fAnyUnordered = true;
pwallet->AddToWallet(wtx, true);
}
else if (strType == "acentry")
{
string strAccount;
ssKey >> strAccount;
uint64_t nNumber;
ssKey >> nNumber;
if (nNumber > nAccountingEntryNumber)
nAccountingEntryNumber = nNumber;
if (!wss.fAnyUnordered)
{
CAccountingEntry acentry;
ssValue >> acentry;
if (acentry.nOrderPos == -1)
wss.fAnyUnordered = true;
}
}
else if (strType == "watchs")
{
CScript script;
ssKey >> script;
char fYes;
ssValue >> fYes;
if (fYes == '1')
pwallet->LoadWatchOnly(script);
// Watch-only addresses have no birthday information for now,
// so set the wallet birthday to the beginning of time.
pwallet->nTimeFirstKey = 1;
}
else if (strType == "key" || strType == "wkey")
{
CPubKey vchPubKey;
ssKey >> vchPubKey;
if (!vchPubKey.IsValid())
{
strErr = "Error reading wallet database: CPubKey corrupt";
return false;
}
CKey key;
CPrivKey pkey;
- uint256 hash = 0;
+ uint256 hash;
if (strType == "key")
{
wss.nKeys++;
ssValue >> pkey;
} else {
CWalletKey wkey;
ssValue >> wkey;
pkey = wkey.vchPrivKey;
}
// Old wallets store keys as "key" [pubkey] => [privkey]
// ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
// using EC operations as a checksum.
// Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
// remaining backwards-compatible.
try
{
ssValue >> hash;
}
catch (...) {}
bool fSkipCheck = false;
- if (hash != 0)
+ if (!hash.IsNull())
{
// hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> vchKey;
vchKey.reserve(vchPubKey.size() + pkey.size());
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
if (Hash(vchKey.begin(), vchKey.end()) != hash)
{
strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
return false;
}
fSkipCheck = true;
}
if (!key.Load(pkey, vchPubKey, fSkipCheck))
{
strErr = "Error reading wallet database: CPrivKey corrupt";
return false;
}
if (!pwallet->LoadKey(key, vchPubKey))
{
strErr = "Error reading wallet database: LoadKey failed";
return false;
}
}
else if (strType == "mkey")
{
unsigned int nID;
ssKey >> nID;
CMasterKey kMasterKey;
ssValue >> kMasterKey;
if(pwallet->mapMasterKeys.count(nID) != 0)
{
strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
return false;
}
pwallet->mapMasterKeys[nID] = kMasterKey;
if (pwallet->nMasterKeyMaxID < nID)
pwallet->nMasterKeyMaxID = nID;
}
else if (strType == "ckey")
{
vector<unsigned char> vchPubKey;
ssKey >> vchPubKey;
vector<unsigned char> vchPrivKey;
ssValue >> vchPrivKey;
wss.nCKeys++;
if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
{
strErr = "Error reading wallet database: LoadCryptedKey failed";
return false;
}
wss.fIsEncrypted = true;
}
else if (strType == "keymeta")
{
CPubKey vchPubKey;
ssKey >> vchPubKey;
CKeyMetadata keyMeta;
ssValue >> keyMeta;
wss.nKeyMeta++;
pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
// find earliest key creation time, as wallet birthday
if (!pwallet->nTimeFirstKey ||
(keyMeta.nCreateTime < pwallet->nTimeFirstKey))
pwallet->nTimeFirstKey = keyMeta.nCreateTime;
}
else if (strType == "defaultkey")
{
ssValue >> pwallet->vchDefaultKey;
}
else if (strType == "pool")
{
int64_t nIndex;
ssKey >> nIndex;
CKeyPool keypool;
ssValue >> keypool;
pwallet->setKeyPool.insert(nIndex);
// If no metadata exists yet, create a default with the pool key's
// creation time. Note that this may be overwritten by actually
// stored metadata for that key later, which is fine.
CKeyID keyid = keypool.vchPubKey.GetID();
if (pwallet->mapKeyMetadata.count(keyid) == 0)
pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
}
else if (strType == "version")
{
ssValue >> wss.nFileVersion;
if (wss.nFileVersion == 10300)
wss.nFileVersion = 300;
}
else if (strType == "cscript")
{
uint160 hash;
ssKey >> hash;
CScript script;
ssValue >> script;
if (!pwallet->LoadCScript(script))
{
strErr = "Error reading wallet database: LoadCScript failed";
return false;
}
}
else if (strType == "orderposnext")
{
ssValue >> pwallet->nOrderPosNext;
}
else if (strType == "destdata")
{
std::string strAddress, strKey, strValue;
ssKey >> strAddress;
ssKey >> strKey;
ssValue >> strValue;
if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue))
{
strErr = "Error reading wallet database: LoadDestData failed";
return false;
}
}
} catch (...)
{
return false;
}
return true;
}
static bool IsKeyType(string strType)
{
return (strType== "key" || strType == "wkey" ||
strType == "mkey" || strType == "ckey");
}
DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
{
pwallet->vchDefaultKey = CPubKey();
CWalletScanState wss;
bool fNoncriticalErrors = false;
DBErrors result = DB_LOAD_OK;
try {
LOCK(pwallet->cs_wallet);
int nMinVersion = 0;
if (Read((string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}
// Get cursor
Dbc* pcursor = GetCursor();
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
return DB_CORRUPT;
}
while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
{
LogPrintf("Error reading next record from wallet database\n");
return DB_CORRUPT;
}
// Try to be tolerant of single corrupt records:
string strType, strErr;
if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
{
// losing keys is considered a catastrophic error, anything else
// we assume the user can live with:
if (IsKeyType(strType))
result = DB_CORRUPT;
else
{
// Leave other errors alone, if we try to fix them we might make things worse.
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
if (strType == "tx")
// Rescan if there is a bad transaction record:
SoftSetBoolArg("-rescan", true);
}
}
if (!strErr.empty())
LogPrintf("%s\n", strErr);
}
pcursor->close();
}
catch (const boost::thread_interrupted&) {
throw;
}
catch (...) {
result = DB_CORRUPT;
}
if (fNoncriticalErrors && result == DB_LOAD_OK)
result = DB_NONCRITICAL_ERROR;
// Any wallet corruption at all: skip any rewriting or
// upgrading, we don't want to make it worse.
if (result != DB_LOAD_OK)
return result;
LogPrintf("nFileVersion = %d\n", wss.nFileVersion);
LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
// nTimeFirstKey is only reliable if all keys have metadata
if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
WriteTx(hash, pwallet->mapWallet[hash]);
// Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
return DB_NEED_REWRITE;
if (wss.nFileVersion < CLIENT_VERSION) // Update
WriteVersion(CLIENT_VERSION);
if (wss.fAnyUnordered)
result = ReorderTransactions(pwallet);
return result;
}
DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx)
{
pwallet->vchDefaultKey = CPubKey();
bool fNoncriticalErrors = false;
DBErrors result = DB_LOAD_OK;
try {
LOCK(pwallet->cs_wallet);
int nMinVersion = 0;
if (Read((string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}
// Get cursor
Dbc* pcursor = GetCursor();
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
return DB_CORRUPT;
}
while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
{
LogPrintf("Error reading next record from wallet database\n");
return DB_CORRUPT;
}
string strType;
ssKey >> strType;
if (strType == "tx") {
uint256 hash;
ssKey >> hash;
CWalletTx wtx;
ssValue >> wtx;
vTxHash.push_back(hash);
vWtx.push_back(wtx);
}
}
pcursor->close();
}
catch (const boost::thread_interrupted&) {
throw;
}
catch (...) {
result = DB_CORRUPT;
}
if (fNoncriticalErrors && result == DB_LOAD_OK)
result = DB_NONCRITICAL_ERROR;
return result;
}
DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx)
{
// build list of wallet TXs
vector<uint256> vTxHash;
DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx);
if (err != DB_LOAD_OK)
return err;
// erase each wallet TX
BOOST_FOREACH (uint256& hash, vTxHash) {
if (!EraseTx(hash))
return DB_CORRUPT;
}
return DB_LOAD_OK;
}
void ThreadFlushWalletDB(const string& strFile)
{
// Make this thread recognisable as the wallet flushing thread
RenameThread("bitcoin-wallet");
static bool fOneThread;
if (fOneThread)
return;
fOneThread = true;
if (!GetBoolArg("-flushwallet", true))
return;
unsigned int nLastSeen = nWalletDBUpdated;
unsigned int nLastFlushed = nWalletDBUpdated;
int64_t nLastWalletUpdate = GetTime();
while (true)
{
MilliSleep(500);
if (nLastSeen != nWalletDBUpdated)
{
nLastSeen = nWalletDBUpdated;
nLastWalletUpdate = GetTime();
}
if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
{
TRY_LOCK(bitdb.cs_db,lockDb);
if (lockDb)
{
// Don't do this if any databases are in use
int nRefCount = 0;
map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
while (mi != bitdb.mapFileUseCount.end())
{
nRefCount += (*mi).second;
mi++;
}
if (nRefCount == 0)
{
boost::this_thread::interruption_point();
map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
if (mi != bitdb.mapFileUseCount.end())
{
LogPrint("db", "Flushing wallet.dat\n");
nLastFlushed = nWalletDBUpdated;
int64_t nStart = GetTimeMillis();
// Flush wallet.dat so it's self contained
bitdb.CloseDb(strFile);
bitdb.CheckpointLSN(strFile);
bitdb.mapFileUseCount.erase(mi++);
LogPrint("db", "Flushed wallet.dat %dms\n", GetTimeMillis() - nStart);
}
}
}
}
}
}
bool BackupWallet(const CWallet& wallet, const string& strDest)
{
if (!wallet.fFileBacked)
return false;
while (true)
{
{
LOCK(bitdb.cs_db);
if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
{
// Flush log data to the dat file
bitdb.CloseDb(wallet.strWalletFile);
bitdb.CheckpointLSN(wallet.strWalletFile);
bitdb.mapFileUseCount.erase(wallet.strWalletFile);
// Copy wallet.dat
filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
filesystem::path pathDest(strDest);
if (filesystem::is_directory(pathDest))
pathDest /= wallet.strWalletFile;
try {
#if BOOST_VERSION >= 104000
filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
#else
filesystem::copy_file(pathSrc, pathDest);
#endif
LogPrintf("copied wallet.dat to %s\n", pathDest.string());
return true;
} catch (const filesystem::filesystem_error& e) {
LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string(), e.what());
return false;
}
}
}
MilliSleep(100);
}
return false;
}
//
// Try to (very carefully!) recover wallet.dat if there is a problem.
//
bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
{
// Recovery procedure:
// move wallet.dat to wallet.timestamp.bak
// Call Salvage with fAggressive=true to
// get as much data as possible.
// Rewrite salvaged data to wallet.dat
// Set -rescan so any missing transactions will be
// found.
int64_t now = GetTime();
std::string newFilename = strprintf("wallet.%d.bak", now);
int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
newFilename.c_str(), DB_AUTO_COMMIT);
if (result == 0)
LogPrintf("Renamed %s to %s\n", filename, newFilename);
else
{
LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
return false;
}
std::vector<CDBEnv::KeyValPair> salvagedData;
bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
if (salvagedData.empty())
{
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
return false;
}
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
bool fSuccess = allOK;
boost::scoped_ptr<Db> pdbCopy(new Db(&dbenv.dbenv, 0));
int ret = pdbCopy->open(NULL, // Txn pointer
filename.c_str(), // Filename
"main", // Logical db name
DB_BTREE, // Database type
DB_CREATE, // Flags
0);
if (ret > 0)
{
LogPrintf("Cannot create database file %s\n", filename);
return false;
}
CWallet dummyWallet;
CWalletScanState wss;
DbTxn* ptxn = dbenv.TxnBegin();
BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
{
if (fOnlyKeys)
{
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
string strType, strErr;
bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
wss, strType, strErr);
if (!IsKeyType(strType))
continue;
if (!fReadOK)
{
LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr);
continue;
}
}
Dbt datKey(&row.first[0], row.first.size());
Dbt datValue(&row.second[0], row.second.size());
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
if (ret2 > 0)
fSuccess = false;
}
ptxn->commit(0);
pdbCopy->close(0);
return fSuccess;
}
bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename)
{
return CWalletDB::Recover(dbenv, filename, false);
}
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
}
bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
{
nWalletDBUpdated++;
return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 23:07 (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5866133
Default Alt Text
(1 MB)

Event Timeline